jupyterlab
This commit is contained in:
@@ -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` | `<homeDir>/<dataVolume.mountPath>` |
|
||||
| `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-<release>-home` 생성
|
||||
- `dataVolume.enabled: true` 일 때만 `pvc-<release>-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 <pod> -n <namespace>` 로 스크립트 실행 로그 확인.
|
||||
|
||||
## 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과 별도로 관리합니다.
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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
|
||||
@@ -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-<release>-home (optional)
|
||||
PVC: pvc-<release>-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를 마운트합니다.
|
||||
@@ -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: []
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
{{/*
|
||||
Common resource name: uses Helm release name
|
||||
*/}}
|
||||
{{- define "jupyterlab.name" -}}
|
||||
{{- .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
ConfigMap name: <release-name>-scripts
|
||||
*/}}
|
||||
{{- define "jupyterlab.configMapName" -}}
|
||||
{{- printf "%s-scripts" .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Home volume PVC name: pvc-<release-name>-home
|
||||
*/}}
|
||||
{{- define "jupyterlab.homePvcName" -}}
|
||||
{{- printf "pvc-%s-home" .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Data volume PVC name: pvc-<release-name>-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: <homeDir>/<mountPath or "data">
|
||||
*/}}
|
||||
{{- 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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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: <registry>/<repository>:<tag>
|
||||
# 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 <homeDir>/<mountPath>.
|
||||
# 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: []
|
||||
Reference in New Issue
Block a user