JupyterHub on Kubernetes
📅 October 23, 2021
•⏱️4 min read
일반적으로 JupyterHub를 Kubernetes 환경에 배포할 때 Helm Chart를 많이 사용합니다.
이 글에서는 zero-to-jupyterhub-k8s Helm Chart에 포함된 다양한 기능들에 대해 소개해보려 합니다.
목차
- kubespawner
- zero-to-jupyterhub-k8s chart
- proxy
- singleuser, profile
- idle-culler
- user-scheduler
- image-pre-puller
- monitoring
KubeSpawner
먼저 JupyterHub의 기본 아키텍쳐에 대해 간단히 짚고 넘어가보겠습니다. JupyterHub에는 노트북 서버를 다양한 방법을 통해 프로비저닝하기 위해 Spawner라는 인터페이스가 존재합니다. K8S 환경이라면 KubeSpawner를 사용하게 됩니다. 이를 통해 프로비저닝 단계에서 kube-scheduler 기반으로 다양한 K8S 리소스를 노트북 서버와 함께 사용할 수 있습니다. 또한 KubeSpawner는 사용자에게 격리된 환경과 컴퓨팅 리소스를 제공할 수 있습니다.
노트북 Pod 생성 이벤트
노트북이 생성되는 과정은 다음과 같습니다.
- 할당 가능한 노드 탐색 (NodeSelector, Affinity)
- 없으면 CA에 의해 노드 추가, 노드가 Ready 상태가 될 때까지 대기
- Pod을 추가된 노드에 할당
- 노트북 이미지 pull
- 노트북 컨테이너 실행
zero-to-jupyterhub-k8s Chart
zero-to-jupyterhub-k8s Helm Chart 의 아키텍쳐는 위의 그림과 같습니다. 기존 JupyterHub와 달리 hook-image-awaiter, jupyterhub-idle-culler 등의 컴포넌트가 추가된 모습을 확인하실 수 있습니다. 이제 대략적으로 어떤 기능을 제공하는지 알아보겠습니다.
Proxy
proxy:
service:
type: ClusterIP
chp:
networkPolicy:
enabled: false
먼저 CHP(configurable-http-proxy) 설정 부분입니다. JupyterHub에서 Proxy는 인증, 사용자 노트북 라우팅, 헬스 체크 등 다양한 역할을 수행합니다. 차트에서는 유연한 Proxy 설정을 위해 CHP, Traefik 등 다양한 옵션을 지원합니다. 아키텍쳐는 aws-load-balancer-controller를 사용한다는 가정하에 구성한 예시입니다. 위 그림과 같이 사용자는 중간의 Proxy 컴포넌트를 거쳐 JupyterHub에 접속하게 됩니다.
SingleUser, Profile
singleUser는 사용자의 노트북 환경을 의미하며 사용자는 미리 정의된 프로필(이미지)을 선택하여 원하는 노트북 환경을 생성할 수 있습니다. 위 아키텍쳐에서는 PV, PVC를 통해 사용자에게 개인, 공용 볼륨을 할당해주었습니다.
profileList:
- display_name: "Python Notebook"
description: "Spec: CPU 2, Memory 4G / Spark 3.1"
kubespawner_override:
image: jupyter/python-notebook:hub-1.4.2
cpu_limit: 2
mem_limit: "4G"
cpu_guarantee: 1
mem_guarantee: "2G"
environment:
TZ: Asia/Seoul
lifecycle_hooks:
postStart:
exec:
command:
프로필에는 리소스 뿐만 아니라 lifecycle_hook, environment 등 K8S의 다양한 리소스를 함께 정의하여 유연하게 구성할 수 있습니다. 노트북 기본 이미지는 jupyter/docker-stacks 저장소로부터 생성한다면 편하게 패키지 의존성을 관리할 수 있습니다.
resource guarantee
resource guarantee는 모든 사용자가 최소한 _guarantee
만큼의 리소스를 사용할 수 있으며 최대 _limit
만큼의 리소스를 제공받을 수 있음을 의미합니다. 예를 들어 사용자에게 2G의 RAM이 보장되는 경우, 사용자는 2G 이상의 RAM을 사용할 수 있습니다. 문서에서는 guarantee 값을 limit의 반으로 설정하는 것을 권장하고 있습니다.
Idle Culler
cull:
enabled: true
timeout: 86400
every: 600
concurrency: 10
idle-culler는 일정 주기 동안 미사용된 노트북 리소스를 정리합니다. 이를 통해 노트북 리소스를 최적화하여 운영할 수 있습니다. idle-culler를 활성화하면 JupyterHub Service에 등록되며 이후 JupyterHub API를 통해 사용자 활동을 주기적으로 확인합니다.
User Scheduler
user scheduler는 노트북 리소스를 적절한 노드에 할당하기 위해 추가되었습니다. 기본 K8S 스케줄러는 여러 노드에 분산하여 리소스를 할당하지만, user scheduler는 가장 리소스를 많이 점유하고 있는 노드에 리소스를 할당합니다. 이를 통해 Cluster AutoScaler, idle-culler와 연계하여 노트북 리소스를 최적화하여 운영할 수 있습니다.
예를 들어 일반적인 설정이라면, pod가 다양한 노드에 분산되어 클러스터 scale-in 조건까지 도달하기가 어렵습니다. 하지만 user-scheduler를 사용한다면, 위 그림과 같이 노드에 할당된 pod의 수가 점진적으로 줄어들게 됩니다.
Image Pre Puller
prePuller:
resources:
requests:
cpu: 10m
memory: 8Mi
hook:
enabled: true
pullOnlyOnChanges: true
Image prePuller는 사용자가 노트북을 실행하기 전에 노드에 미리 이미지를 준비하여 노트북 환경 생성 시간을 단축시켜 줍니다. 예를 들어 CA에 의해 노드가 새로 추가된다거나 새로운 이미지가 프로필에 등록된 경우, 미리 노드에 프로필 이미지를 pull 하게 됩니다.
Monitoring
JupyterHub는 /metrics
endpoint를 통해 prometheus 메트릭을 지원합니다. 주요 지표로는 활성 사용자 수, 노트북 서버 생성까지 소요되는 시간 등이 있습니다. 사용 가능한 전체 메트릭은 JupyterHub 문서에서 확인하실 수 있습니다.
또한 jupyterhub/grafana-dashboards 저장소를 통해 미리 정의된 운영 대시보드를 제공합니다. 이를 통해 쉽게 모니터링을 구성할 수 있습니다.