diff --git a/manifests/helm/jupyterlab/1.0.0/BUILD-README.md b/manifests/helm/jupyterlab/1.0.0/BUILD-README.md new file mode 100644 index 0000000..4161a86 --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/BUILD-README.md @@ -0,0 +1,157 @@ +# Chart 개발 및 빌드 가이드 + +이 문서는 JupyterLab Helm chart의 구조와 템플릿 개발, 검증 방법을 설명합니다. + +## 디렉토리 구조 + +``` +jupyterlab/ +├── Chart.yaml # 차트 메타데이터 +├── values.yaml # 기본값 정의 +├── custom-values.yaml # 환경별 override 예시 +├── README.md # 사용자 가이드 +├── CUSTOM-README.md # custom-values 작성 가이드 +├── BUILD-README.md # 이 파일 (개발/빌드 가이드) +└── templates/ + ├── _helpers.tpl # 재사용 헬퍼 함수 정의 + ├── deployment.yaml # Deployment 리소스 + ├── service.yaml # Service 리소스 + ├── ingress.yaml # Ingress 리소스 + ├── pvc.yaml # PersistentVolumeClaim 리소스 + └── configmap.yaml # ConfigMap (startup-script.sh) +``` + +## 템플릿 구조 설명 + +### `_helpers.tpl` — 헬퍼 함수 + +| 함수 | 반환값 예시 | 설명 | +|------|------------|------| +| `jupyterlab.name` | `my-jupyter` | 릴리즈 이름 그대로 사용 | +| `jupyterlab.configMapName` | `my-jupyter-scripts` | ConfigMap 이름 | +| `jupyterlab.homePvcName` | `pvc-my-jupyter-home` | 홈 PVC 이름 | +| `jupyterlab.dataPvcName` | `pvc-my-jupyter-data` | 데이터 PVC 이름 | +| `jupyterlab.homeDir` | `/home/jovyan` | `homeVolume.mountPath` 또는 기본값 | +| `jupyterlab.dataDir` | `/home/jovyan/data` | `/` | +| `jupyterlab.labels` | (라벨 맵) | 공통 라벨 세트 | +| `jupyterlab.image` | `quay.io/jupyter/scipy-notebook:latest` | 전체 이미지 참조 | +| `jupyterlab.command` | (bash 커맨드 배열) | JupyterLab 시작 커맨드 | + +### `deployment.yaml` + +- `replicas`: `values.replicas` +- 보안 컨텍스트: Pod 레벨 `runAsUser: 1000 / fsGroup: 100`, Container 레벨 `runAsUser: 0` +- readinessProbe: `tcpSocket:8888`, 초기 30초 대기, 10초 주기, 27회 실패 허용 (총 ~5분) +- 볼륨: `localtime`, `startup-script` (항상), `home-volume`, `data-volume`, `groupVolumes` (조건부) + +### `configmap.yaml` + +startup-script.sh 내용: + +```bash +#!/bin/bash +apt-get update -y +python -m pip install --upgrade pip +``` + +이 스크립트는 `/usr/local/bin/start-notebook.d/` 에 마운트되어 JupyterLab 시작 전 자동 실행됩니다. +추가 패키지 설치가 필요하면 이 파일을 수정하세요. + +### `pvc.yaml` + +- `homeVolume.enabled: true` 일 때만 `pvc--home` 생성 +- `dataVolume.enabled: true` 일 때만 `pvc--data` 생성 +- `storageClassName`이 비어 있으면 클러스터 기본 StorageClass 사용 +- PVC 보존 필요 시 주석 해제: `helm.sh/resource-policy: keep` + +### `ingress.yaml` + +- `ingress.enabled: false` 이면 리소스 생성 안 함 +- annotations는 `range`로 동적 주입 (Kong, cert-manager 설정 포함) +- pathType은 `Prefix`로 고정 + +## 개발 시 자주 쓰는 명령어 + +### 렌더링 검증 + +```bash +# 전체 렌더링 출력 +helm template my-jupyter . -f custom-values.yaml + +# 특정 리소스만 확인 +helm template my-jupyter . -f custom-values.yaml | \ + kubectl neat | grep -A50 "kind: Deployment" + +# values 병합 결과 확인 +helm template my-jupyter . -f custom-values.yaml --debug 2>&1 | head -50 +``` + +### Lint + +```bash +helm lint . -f custom-values.yaml +``` + +### values 스키마 검증 (schema 파일 추가 시) + +```bash +helm template my-jupyter . -f custom-values.yaml --validate +``` + +### 실 클러스터 dry-run + +```bash +helm install my-jupyter . \ + --namespace jupyter \ + --create-namespace \ + -f custom-values.yaml \ + --dry-run +``` + +## 새 기능 추가 시 체크리스트 + +- [ ] `values.yaml`에 새 키와 기본값 추가 +- [ ] `_helpers.tpl`에 필요한 헬퍼 함수 추가 +- [ ] 해당 템플릿 파일에 조건부 블록으로 구현 (`{{- if .Values.xxx }}`) +- [ ] `custom-values.yaml`에 예시값 반영 +- [ ] `helm lint` 통과 확인 +- [ ] `helm template` 렌더링 결과 확인 +- [ ] `README.md` Values 테이블 업데이트 + +## 자주 발생하는 오류 + +### Error: YAML parse error + +`values.yaml`이나 `custom-values.yaml`의 들여쓰기 오류. +`helm lint` 로 위치 확인. + +### Error: template: jupyterlab/templates/xxx.yaml: ... nil pointer evaluating + +`values.yaml`에 존재하지 않는 키를 템플릿에서 참조할 때 발생. +`_helpers.tpl`에서 `default` 함수 사용: + +``` +{{ .Values.some.key | default "fallback" }} +``` + +### PVC Pending 상태 + +`storageClassName`이 클러스터에 없는 경우. +`kubectl get storageclass` 로 사용 가능한 클래스 확인 후 설정. + +### Pod CrashLoopBackOff + +startup-script.sh 오류 가능성 높음. +`kubectl logs -n ` 로 스크립트 실행 로그 확인. + +## Chart 버전 관리 + +`Chart.yaml`의 `version` 필드는 **SemVer** 규칙을 따릅니다. + +| 변경 유형 | 버전 업 | 예시 | +|----------|---------|------| +| 하위 호환 버그 픽스 | patch | `1.0.0` → `1.0.1` | +| 새 기능 추가 (하위 호환) | minor | `1.0.0` → `1.1.0` | +| 하위 비호환 변경 | major | `1.0.0` → `2.0.0` | + +`appVersion`은 JupyterLab 애플리케이션 버전을 나타내며, chart version과 별도로 관리합니다. diff --git a/manifests/helm/jupyterlab/1.0.0/CUSTOM-README.md b/manifests/helm/jupyterlab/1.0.0/CUSTOM-README.md new file mode 100644 index 0000000..9fff8c4 --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/CUSTOM-README.md @@ -0,0 +1,215 @@ +# custom-values.yaml 사용 가이드 + +이 문서는 `custom-values.yaml`을 작성하고 배포에 적용하는 방법을 설명합니다. + +## Helm Values Merge 동작 방식 + +Helm은 `--values` 플래그로 전달된 파일을 기본 `values.yaml`에 **deep merge** 방식으로 적용합니다. + +- `custom-values.yaml`에 명시된 키는 `values.yaml` 기본값을 **덮어씁니다** +- 명시하지 않은 키는 `values.yaml` 기본값이 **그대로 유지됩니다** +- 중첩 객체(`resources`, `ingress` 등)도 키 단위로 병합됩니다 + +``` +values.yaml (기본값) + + +custom-values.yaml (override) + = +최종 적용값 +``` + +## custom-values.yaml 필드 설명 + +### image + +```yaml +image: + registry: "quay.io" # 레지스트리 주소. 비워두면 Docker Hub 사용 + repository: "jupyter/scipy-notebook" + tag: "latest" # 특정 버전 고정 권장 (예: "2024-01-15") +``` + +> `image.pullPolicy`는 custom-values에 없으므로 `values.yaml` 기본값 `IfNotPresent` 적용 + +### resources + +```yaml +resources: + limits: + cpu: "2000m" # 최대 2 vCPU + memory: "4096Mi" # 최대 4 GiB + requests: + cpu: "500m" # 예약 0.5 vCPU + memory: "1024Mi" # 예약 1 GiB + gpu: "" # GPU 사용 시: "1" (nvidia.com/gpu limits/requests에 동일하게 설정됨) +``` + +### jupyterConfig + +```yaml +jupyterConfig: + token: "" # 비어 있으면 토큰 인증 비활성화 + # 값 설정 시 SHA-256으로 해시되어 ServerApp.password에 적용 +``` + +### ingress + +```yaml +ingress: + enabled: true + ingressClassName: "kong" + annotations: + cert-manager.io/cluster-issuer: "root-ca-issuer" + cert-manager.io/duration: "8760h" + cert-manager.io/renew-before: "720h" + konghq.com/https-redirect-status-code: "301" + konghq.com/protocols: "https" + hosts: + - host: "jupyter.example.com" # 실제 도메인으로 변경 필수 + paths: + - / + tls: + - hosts: + - "jupyter.example.com" + secretName: "jupyter-tls-secret" +``` + +### homeVolume / dataVolume + +```yaml +homeVolume: + enabled: false # true로 설정 시 /home/jovyan에 PVC 마운트 + size: "5Gi" + storageClassName: "" # 클러스터 기본 StorageClass 사용 + accessMode: "ReadWriteOnce" + # mountPath: "" # custom-values에 없음 → 기본값 /home/jovyan 적용 + +dataVolume: + enabled: false # true로 설정 시 /home/jovyan/data에 PVC 마운트 + size: "5Gi" + storageClassName: "" + accessMode: "ReadWriteOnce" + # mountPath: "" # custom-values에 없음 → 기본값 "data" 적용 +``` + +### groupVolumes + +```yaml +groupVolumes: [] +# 공유 PVC 마운트 예시: +# groupVolumes: +# - claimName: "pvc-shared-data" +# mountPath: "/group_data" +# - claimName: "pvc-team-models" +# mountPath: "/models" +``` + +## custom-values.yaml 미포함 필드 (values.yaml 기본값 적용) + +| 필드 | 기본값 | 비고 | +|------|--------|------| +| `image.pullPolicy` | `IfNotPresent` | | +| `replicas` | `1` | 0으로 설정하면 Pod 중지 | +| `homeVolume.mountPath` | `""` → `/home/jovyan` | | +| `dataVolume.mountPath` | `""` → `data` | | +| `imagePullSecrets` | `[]` | Private 레지스트리 사용 시 추가 필요 | + +## 배포 명령어 + +```bash +# 렌더링 미리보기 (실제 배포 없음) +helm template my-jupyter ./manifests/helm/jupyterlab \ + -f ./manifests/helm/jupyterlab/custom-values.yaml + +# 신규 설치 +helm install my-jupyter ./manifests/helm/jupyterlab \ + --namespace jupyter \ + --create-namespace \ + -f ./manifests/helm/jupyterlab/custom-values.yaml + +# 업그레이드 +helm upgrade my-jupyter ./manifests/helm/jupyterlab \ + -n jupyter \ + -f ./manifests/helm/jupyterlab/custom-values.yaml + +# 변경 사항 확인 (helm-diff 플러그인 필요) +helm diff upgrade my-jupyter ./manifests/helm/jupyterlab \ + -n jupyter \ + -f ./manifests/helm/jupyterlab/custom-values.yaml +``` + +## 환경별 설정 예시 + +### 개발 환경 (최소 리소스, 인증 없음) + +```yaml +image: + tag: "latest" + +resources: + limits: + cpu: "500m" + memory: "1024Mi" + requests: + cpu: "100m" + memory: "256Mi" + gpu: "" + +jupyterConfig: + token: "" + +ingress: + enabled: true + ingressClassName: "kong" + hosts: + - host: "jupyter-dev.example.com" + paths: [/] + tls: + - hosts: ["jupyter-dev.example.com"] + secretName: "jupyter-dev-tls" +``` + +### 운영 환경 (토큰 인증, PVC, GPU) + +```yaml +image: + tag: "2024-01-15" # 버전 고정 + +resources: + limits: + cpu: "4000m" + memory: "8192Mi" + requests: + cpu: "1000m" + memory: "2048Mi" + gpu: "1" # NVIDIA GPU 1개 + +jupyterConfig: + token: "my-secure-token" + +ingress: + enabled: true + ingressClassName: "kong" + hosts: + - host: "jupyter.example.com" + paths: [/] + tls: + - hosts: ["jupyter.example.com"] + secretName: "jupyter-prod-tls" + +homeVolume: + enabled: true + size: "20Gi" + storageClassName: "fast-ssd" + accessMode: "ReadWriteOnce" + +dataVolume: + enabled: true + size: "100Gi" + storageClassName: "fast-ssd" + accessMode: "ReadWriteOnce" + +groupVolumes: + - claimName: "pvc-shared-datasets" + mountPath: "/datasets" +``` diff --git a/manifests/helm/jupyterlab/1.0.0/Chart.yaml b/manifests/helm/jupyterlab/1.0.0/Chart.yaml new file mode 100644 index 0000000..c475387 --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v2 +name: jupyterlab +description: Helm chart for deploying JupyterLab instances using pure Kubernetes resources (no Operator required) +type: application +version: 1.0.0 +appVersion: "1.0" +keywords: + - jupyterlab + - jupyter + - notebook + - data-science +maintainers: + - name: paasup diff --git a/manifests/helm/jupyterlab/1.0.0/README.md b/manifests/helm/jupyterlab/1.0.0/README.md new file mode 100644 index 0000000..763d76f --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/README.md @@ -0,0 +1,118 @@ +# JupyterLab Helm Chart + +Helm chart for deploying JupyterLab instances using pure Kubernetes resources (no Operator required). + +## Overview + +| Item | Value | +|------|-------| +| Chart Version | 1.0.0 | +| App Version | 1.0 | +| Default Image | `quay.io/jupyter/scipy-notebook:latest` | +| Port | 8888 | +| Maintainer | paasup | + +## Architecture + +``` +Deployment + └── Container: jupyterlab (port 8888) + ├── ConfigMap: startup-script.sh (pip upgrade 등 초기화) + ├── Volume: /etc/localtime (Asia/Seoul) + ├── Volume: homeVolume → /home/jovyan (optional) + ├── Volume: dataVolume → /home/jovyan/data (optional) + └── groupVolumes → 임의 경로 (optional) +Service (ClusterIP, port 8888) +Ingress (Kong, HTTPS) +PVC: pvc--home (optional) +PVC: pvc--data (optional) +``` + +## Prerequisites + +- Kubernetes 1.21+ +- Helm 3.x +- Kong Ingress Controller +- cert-manager (TLS 사용 시) + +## Installation + +```bash +# 기본 설치 +helm install my-jupyter ./manifests/helm/jupyterlab \ + --namespace jupyter \ + --create-namespace + +# custom-values 적용 +helm install my-jupyter ./manifests/helm/jupyterlab \ + --namespace jupyter \ + --create-namespace \ + -f ./manifests/helm/jupyterlab/custom-values.yaml + +# 설치 확인 +helm status my-jupyter -n jupyter +kubectl get pods -n jupyter +``` + +## Upgrade / Uninstall + +```bash +helm upgrade my-jupyter ./manifests/helm/jupyterlab \ + -n jupyter \ + -f ./manifests/helm/jupyterlab/custom-values.yaml + +helm uninstall my-jupyter -n jupyter +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `image.registry` | string | `quay.io` | 이미지 레지스트리 | +| `image.repository` | string | `jupyter/scipy-notebook` | 이미지 리포지토리 | +| `image.tag` | string | `latest` | 이미지 태그 | +| `image.pullPolicy` | string | `IfNotPresent` | 이미지 풀 정책 | +| `replicas` | int | `1` | Pod 복제본 수 (1=실행, 0=중지) | +| `resources.limits.cpu` | string | `2000m` | CPU 제한 | +| `resources.limits.memory` | string | `4096Mi` | 메모리 제한 | +| `resources.requests.cpu` | string | `500m` | CPU 요청 | +| `resources.requests.memory` | string | `1024Mi` | 메모리 요청 | +| `resources.gpu` | string | `""` | NVIDIA GPU 수 (비어 있으면 비활성화) | +| `jupyterConfig.token` | string | `""` | 접근 토큰 (비어 있으면 인증 비활성화) | +| `ingress.enabled` | bool | `true` | Ingress 활성화 여부 | +| `ingress.ingressClassName` | string | `kong` | Ingress 클래스 | +| `ingress.hosts` | list | `[{host: jupyter.example.com, paths: ["/"]}]` | 호스트 설정 | +| `ingress.tls` | list | `[{hosts: [...], secretName: ...}]` | TLS 설정 | +| `homeVolume.enabled` | bool | `false` | 홈 디렉토리 PVC 활성화 | +| `homeVolume.mountPath` | string | `""` | 마운트 경로 (기본: `/home/jovyan`) | +| `homeVolume.size` | string | `5Gi` | PVC 크기 | +| `homeVolume.storageClassName` | string | `""` | StorageClass 이름 | +| `homeVolume.accessMode` | string | `ReadWriteOnce` | 접근 모드 | +| `dataVolume.enabled` | bool | `false` | 데이터 디렉토리 PVC 활성화 | +| `dataVolume.mountPath` | string | `""` | 서브 디렉토리 이름 (기본: `data`) | +| `dataVolume.size` | string | `5Gi` | PVC 크기 | +| `dataVolume.storageClassName` | string | `""` | StorageClass 이름 | +| `dataVolume.accessMode` | string | `ReadWriteOnce` | 접근 모드 | +| `groupVolumes` | list | `[]` | 공유 그룹 볼륨 목록 | +| `imagePullSecrets` | list | `[]` | Private 레지스트리 시크릿 | + +## Generated Resource Names + +릴리즈 이름이 `my-jupyter`일 경우: + +| 리소스 | 이름 | +|--------|------| +| Deployment | `my-jupyter` | +| Service | `my-jupyter` | +| Ingress | `my-jupyter` | +| ConfigMap | `my-jupyter-scripts` | +| Home PVC | `pvc-my-jupyter-home` | +| Data PVC | `pvc-my-jupyter-data` | + +## Notes + +- Pod는 `runAsUser: 1000`, `fsGroup: 100`으로 실행됩니다. +- Container는 `runAsUser: 0` (root)으로 실행됩니다 (pip install 등을 위해). +- 토큰 미설정 시 인증 없이 JupyterLab에 접근 가능합니다. 운영 환경에서는 `jupyterConfig.token`을 설정하거나 별도 인증 레이어를 구성하세요. +- PVC는 `helm uninstall` 시 삭제됩니다. 데이터 보존이 필요하면 `pvc.yaml`의 `helm.sh/resource-policy: keep` 주석을 해제하세요. +- `/etc/localtime`은 호스트의 `Asia/Seoul` zoneinfo를 마운트합니다. diff --git a/manifests/helm/jupyterlab/1.0.0/custom-values.yaml b/manifests/helm/jupyterlab/1.0.0/custom-values.yaml new file mode 100644 index 0000000..ce321ed --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/custom-values.yaml @@ -0,0 +1,49 @@ +image: + registry: "quay.io" + repository: "jupyter/scipy-notebook" + tag: "latest" + +resources: + limits: + cpu: "2000m" + memory: "4096Mi" + requests: + cpu: "500m" + memory: "1024Mi" + gpu: "" + +jupyterConfig: + token: "" + +ingress: + enabled: true + ingressClassName: "kong" + annotations: + cert-manager.io/cluster-issuer: "root-ca-issuer" + cert-manager.io/duration: "8760h" + cert-manager.io/renew-before: "720h" + konghq.com/https-redirect-status-code: "301" + konghq.com/protocols: "https" + hosts: + - host: "jupyter.example.com" + paths: + - / + tls: + - hosts: + - "jupyter.example.com" + secretName: "jupyter-tls-secret" + +homeVolume: + enabled: false + size: "5Gi" + storageClassName: "" + accessMode: "ReadWriteOnce" + +dataVolume: + enabled: false + size: "5Gi" + storageClassName: "" + accessMode: "ReadWriteOnce" + +groupVolumes: [] + diff --git a/manifests/helm/jupyterlab/1.0.0/templates/_helpers.tpl b/manifests/helm/jupyterlab/1.0.0/templates/_helpers.tpl new file mode 100644 index 0000000..e488c55 --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/templates/_helpers.tpl @@ -0,0 +1,93 @@ +{{/* +Common resource name: uses Helm release name +*/}} +{{- define "jupyterlab.name" -}} +{{- .Release.Name }} +{{- end }} + +{{/* +ConfigMap name: -scripts +*/}} +{{- define "jupyterlab.configMapName" -}} +{{- printf "%s-scripts" .Release.Name }} +{{- end }} + +{{/* +Home volume PVC name: pvc--home +*/}} +{{- define "jupyterlab.homePvcName" -}} +{{- printf "pvc-%s-home" .Release.Name }} +{{- end }} + +{{/* +Data volume PVC name: pvc--data +*/}} +{{- define "jupyterlab.dataPvcName" -}} +{{- printf "pvc-%s-data" .Release.Name }} +{{- end }} + +{{/* +Resolved home directory path (mountPath or default /home/jovyan) +*/}} +{{- define "jupyterlab.homeDir" -}} +{{- .Values.homeVolume.mountPath | default "/home/jovyan" }} +{{- end }} + +{{/* +Resolved data volume mount path: / +*/}} +{{- define "jupyterlab.dataDir" -}} +{{- $home := include "jupyterlab.homeDir" . }} +{{- $sub := .Values.dataVolume.mountPath | default "data" }} +{{- printf "%s/%s" $home $sub }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "jupyterlab.labels" -}} +app: {{ include "jupyterlab.name" . }} +app.kubernetes.io/name: jupyterlab +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} +{{- end }} + +{{/* +Full image reference: [registry/]repository:tag +*/}} +{{- define "jupyterlab.image" -}} +{{- if .Values.image.registry }} +{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository .Values.image.tag }} +{{- else }} +{{- printf "%s:%s" .Values.image.repository .Values.image.tag }} +{{- end }} +{{- end }} + +{{/* +JupyterLab startup command +*/}} +{{- define "jupyterlab.command" -}} +{{- $token := .Values.jupyterConfig.token }} +{{- $homeDir := include "jupyterlab.homeDir" . }} +- /bin/bash +- -c +- | + start.sh jupyter lab \ + --core-mode \ + {{- if $token }} + --ServerApp.password='sha256::{{ $token | sha256sum | trimSuffix " -" }}' \ + --ServerApp.token='' \ + {{- else }} + --ServerApp.token='' \ + --ServerApp.password='' \ + {{- end }} + --ServerApp.ip='0.0.0.0' \ + --ServerApp.allow_origin='*' \ + --IdentityProvider.cookie_options="max-age=28800" \ + --ServerApp.disable_check_xsrf=True \ + --ServerApp.quit_button=False \ + --ServerApp.allow_root=False \ + --ServerApp.terminals_enabled=True \ + --ServerApp.root_dir={{ $homeDir }} +{{- end }} diff --git a/manifests/helm/jupyterlab/1.0.0/templates/configmap.yaml b/manifests/helm/jupyterlab/1.0.0/templates/configmap.yaml new file mode 100644 index 0000000..5ac1a18 --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/templates/configmap.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "jupyterlab.configMapName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "jupyterlab.labels" . | nindent 4 }} +data: + startup-script.sh: | + #!/bin/bash + apt-get update -y + python -m pip install --upgrade pip diff --git a/manifests/helm/jupyterlab/1.0.0/templates/deployment.yaml b/manifests/helm/jupyterlab/1.0.0/templates/deployment.yaml new file mode 100644 index 0000000..8271e9e --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/templates/deployment.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "jupyterlab.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "jupyterlab.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ include "jupyterlab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + {{- include "jupyterlab.labels" . | nindent 8 }} + spec: + securityContext: + runAsUser: 1000 + fsGroup: 100 + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: jupyterlab + image: {{ include "jupyterlab.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + {{- include "jupyterlab.command" . | nindent 12 }} + ports: + - containerPort: 8888 + protocol: TCP + env: + - name: NVIDIA_DRIVER_CAPABILITIES + value: "compute,utility" + resources: + limits: + cpu: {{ .Values.resources.limits.cpu | quote }} + memory: {{ .Values.resources.limits.memory | quote }} + {{- if .Values.resources.gpu }} + nvidia.com/gpu: {{ .Values.resources.gpu | quote }} + {{- end }} + requests: + cpu: {{ .Values.resources.requests.cpu | quote }} + memory: {{ .Values.resources.requests.memory | quote }} + {{- if .Values.resources.gpu }} + nvidia.com/gpu: {{ .Values.resources.gpu | quote }} + {{- end }} + securityContext: + runAsUser: 0 + readinessProbe: + tcpSocket: + port: 8888 + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 27 + volumeMounts: + - name: localtime + mountPath: /etc/localtime + readOnly: true + - name: startup-script + mountPath: /usr/local/bin/start-notebook.d/startup-script.sh + subPath: startup-script.sh + {{- if .Values.homeVolume.enabled }} + - name: home-volume + mountPath: {{ include "jupyterlab.homeDir" . }} + {{- end }} + {{- if .Values.dataVolume.enabled }} + - name: data-volume + mountPath: {{ include "jupyterlab.dataDir" . }} + {{- end }} + {{- range .Values.groupVolumes }} + - name: gv-{{ .claimName }} + mountPath: {{ .mountPath }} + {{- end }} + volumes: + - name: localtime + hostPath: + path: /usr/share/zoneinfo/Asia/Seoul + - name: startup-script + configMap: + name: {{ include "jupyterlab.configMapName" . }} + defaultMode: 0755 + {{- if .Values.homeVolume.enabled }} + - name: home-volume + persistentVolumeClaim: + claimName: {{ include "jupyterlab.homePvcName" . }} + {{- end }} + {{- if .Values.dataVolume.enabled }} + - name: data-volume + persistentVolumeClaim: + claimName: {{ include "jupyterlab.dataPvcName" . }} + {{- end }} + {{- range .Values.groupVolumes }} + - name: gv-{{ .claimName }} + persistentVolumeClaim: + claimName: {{ .claimName }} + {{- end }} diff --git a/manifests/helm/jupyterlab/1.0.0/templates/ingress.yaml b/manifests/helm/jupyterlab/1.0.0/templates/ingress.yaml new file mode 100644 index 0000000..606baf6 --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/templates/ingress.yaml @@ -0,0 +1,42 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "jupyterlab.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "jupyterlab.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- range $k, $v := . }} + {{ $k }}: {{ $v | quote }} + {{- end }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ . }} + pathType: Prefix + backend: + service: + name: {{ include "jupyterlab.name" $ }} + port: + number: 8888 + {{- end }} + {{- end }} +{{- end }} diff --git a/manifests/helm/jupyterlab/1.0.0/templates/pvc.yaml b/manifests/helm/jupyterlab/1.0.0/templates/pvc.yaml new file mode 100644 index 0000000..72e79ac --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/templates/pvc.yaml @@ -0,0 +1,43 @@ +{{- if .Values.homeVolume.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "jupyterlab.homePvcName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "jupyterlab.labels" . | nindent 4 }} + annotations: + # Uncomment below to retain the PVC even after helm uninstall. + # helm.sh/resource-policy: keep +spec: + accessModes: + - {{ .Values.homeVolume.accessMode }} + {{- if .Values.homeVolume.storageClassName }} + storageClassName: {{ .Values.homeVolume.storageClassName }} + {{- end }} + resources: + requests: + storage: {{ .Values.homeVolume.size }} +--- +{{- end }} +{{- if .Values.dataVolume.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "jupyterlab.dataPvcName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "jupyterlab.labels" . | nindent 4 }} + annotations: + # Uncomment below to retain the PVC even after helm uninstall. + # helm.sh/resource-policy: keep +spec: + accessModes: + - {{ .Values.dataVolume.accessMode }} + {{- if .Values.dataVolume.storageClassName }} + storageClassName: {{ .Values.dataVolume.storageClassName }} + {{- end }} + resources: + requests: + storage: {{ .Values.dataVolume.size }} +{{- end }} diff --git a/manifests/helm/jupyterlab/1.0.0/templates/service.yaml b/manifests/helm/jupyterlab/1.0.0/templates/service.yaml new file mode 100644 index 0000000..86ac94e --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/templates/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "jupyterlab.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "jupyterlab.labels" . | nindent 4 }} +spec: + type: ClusterIP + selector: + app: {{ include "jupyterlab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + ports: + - name: http + port: 8888 + targetPort: 8888 + protocol: TCP diff --git a/manifests/helm/jupyterlab/1.0.0/values.yaml b/manifests/helm/jupyterlab/1.0.0/values.yaml new file mode 100644 index 0000000..2ea8b4a --- /dev/null +++ b/manifests/helm/jupyterlab/1.0.0/values.yaml @@ -0,0 +1,77 @@ +# JupyterLab Simple Helm Chart - Default values +# Deploys JupyterLab using pure Kubernetes resources without an Operator. + +# JupyterLab Docker image +# Full image ref: /: +# Leave registry empty to use Docker Hub (default). +image: + registry: "quay.io" + repository: "jupyter/scipy-notebook" + tag: "latest" + pullPolicy: IfNotPresent + +# Running state: 1=running, 0=stopped +replicas: 1 + +# Compute resources +resources: + limits: + cpu: "2000m" + memory: "4096Mi" + requests: + cpu: "500m" + memory: "1024Mi" + # Number of NVIDIA GPUs to allocate (e.g. "1"). Leave empty to disable. + # nvidia.com/gpu is set to the same value in both limits and requests (Kubernetes requirement). + gpu: "" + +# JupyterLab process configuration +jupyterConfig: + # Access token (leave empty to disable token authentication) + token: "" + +# External access Ingress configuration +ingress: + enabled: true + ingressClassName: "kong" + annotations: + cert-manager.io/cluster-issuer: "root-ca-issuer" + cert-manager.io/duration: "8760h" + cert-manager.io/renew-before: "720h" + konghq.com/https-redirect-status-code: "301" + konghq.com/protocols: "https" + hosts: + - host: "jupyter.example.com" + paths: + - / + tls: + - hosts: + - "jupyter.example.com" + secretName: "jupyter-tls-secret" + +# Home directory volume: PVC mounted at the user's home directory. +# mountPath: mount path inside the container (defaults to /home/jovyan if left empty) +homeVolume: + enabled: false + mountPath: "" + size: "5Gi" + storageClassName: "" + accessMode: "ReadWriteOnce" + +# Data volume: PVC mounted at /. +# mountPath: subdirectory name under the home directory (defaults to "data" if left empty) +dataVolume: + enabled: false + mountPath: "" + size: "5Gi" + storageClassName: "" + accessMode: "ReadWriteOnce" + +# Shared group volume mounts (optional) +# groupVolumes: +# - claimName: "pvc-shared-data" +# mountPath: "/group_data" +groupVolumes: [] + +# imagePullSecrets for private registries +imagePullSecrets: []