Jupyterhub on Kubernetes
Jupyterhub를 Kubernetes Cluster에서 실행 해 보자. 2023-03-25

안녕하세요? 오늘은 Kubernetes 환경에 JupyterHub를 설치 하는 방법에 대해서 알아 보도록 하겠습니다.

Kubernetes

Kubernetes는 컨테이너를 쉽고 빠르게 배포/확장하고 관리를 자동화해주는 Container Orchestration Tool 입니다. Kubernetes 시스템을 통해, 다음을 제공 받을 수 있습니다.

  • 서비스 디스커버리와 로드 밸런싱: DNS 이름, 혹은 자체 IP 주소를 이용하여 컨테이너를 노출 해 주고, 트래픽이 많아지면 로드 밸런싱을 제공 하여 줍니다.
  • 스토리지 오케스트레이션: 로컬 저장소, 클라우드 공급자 등과 같은 저장소 시스템을 자동으로 탑재 하게 할 수 있습니다.
  • 자동화된 롤아웃과 롤백: 컨테이너의 원하는 상태를 지정 해 놓으면, 이에 맞춰 롤아웃, 롤백을 자동으로, 원하는 속도로 수행 해 줍니다.
  • 자동화된 빈 패킹 (bin packing): 컨테이너화된 작업을 실행하는 데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공 하는데, 컨테이너가 필요로 하는 CPU와 메모리를 적정 하게 제공 해 줍니다.
  • 자동화된 복구 (self-healing): 실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하는 기술을 가지고 있습니다.
  • 시크릿과 구성 관리: 암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 관리 할 수 있습니다.

Kubernetes Logo

JupyterHub

JupyterHub여러 사용자가 공유하는 Jupyter Notebook 서버를 구축하는 데 사용되는 오픈 소스 프레임워크입니다. Jupyter Notebook을 사용 하는 의도는 각기 다양 합니다. 어떤 사람들은 머신러닝을 위해, 어떤 사람은 Spark Job을 통한 ETL을 위해, 어떤 사람은 다른 버전의 API를 사용 하기 위해 다양하게 사용 합니다. 이에 따라, 각자 사용 하는 노트북 파일과 커널을 분리 해 줄 필요가 있었고, 이에 따라 다양한 사용자 인증 제어, 커널 관리 등을 제공 할 수 있는 JupyterHub가 등장 하게 되었습니다.

JupyterHub Logo

Why JupyterHub on K8S?

JupyterHub on K8S 일까요? JupyterHubSpawner라는 인터페이스를 통해, 사용자 마다 별개의 Jupyter Server를 제공 할 수 있게 합니다. K8S 환경에서는 KubeSpawner를 이용하여, K8SSecret, Node, Storage 등, 다양한 리소스들을 사용 할 수 있다는 것이 장점입니다.

또한, 원하는 이미지로 Pod을 생성 하는 것을 선택하게 할 수 있고, 또한, Secret에 대한 Namespace 수준의 권한 제어 등, Jupyter Server 운영의 입장에서 얻을 수 있는 이득이 많습니다.

그리고, 공식 Helm 차트를 제공하여, 편리 하고 빠른 배포 또한 제공 합니다.

사진으로 내부 아키텍처를 표현하면 다음과 같습니다.

JupyterHub Architecture

사전 준비

사전에 준비 해야 할 프로그램 목록 입니다. 해당 프로그램 들에 대한 사전적인 지식이 있어야 합니다.

  • Docker
  • minikube
  • Kubectl
  • Helm

설치 방법

일단, JupyterHub Helm Chart Repository를 등록 합니다.

helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/
helm repo update

그 다음 빈 폴더를 생성 한 후, 해당 폴더에 config.yaml을 생성 합니다. config.yaml에는 다음 내용을 작성 합니다. 사용 할 Docker Image를 명시하기 위함 입니다. 도커 이미지 내, Jupyter Kernel 세팅이 되어 있다면 사용 가능합니다.

singleuser:
  image:
    name: jupyter/minimal-notebook
    tag: latest

만약 의존성 설치 및 다른 파일 등의 복사가 필요 하다면 다음과 같이 Dockerfile을 만든 후 build 할 수 있습니다.

FROM jupyter/minimal-notebook:latest
RUN pip install --no-cache-dir astropy

그 이후에는 터미널에 다음을 입력 합니다.

helm upgrade --cleanup-on-fail \
  <helm-release-name> jupyterhub/jupyterhub \
  --namespace <k8s-namespace> \
  --create-namespace \
  --version=<chart-version> \
  --values config.yaml
  • <helm-release-name>: Helm Release Name을 의미 하며, 여러 개의 차트 설치들을 구분 하기 위해 사용 합니다.
  • <k8s-namespace>: Kubernetes Cluster내의 어느 Namespace에 설치 할 지 선택합니다. --create-namespace 옵션을 통해, 없는 경우 Namespace를 같이 설치하는 역할 또한 수행 합니다.
  • <chart-version>: chart-version을 선택 합니다. 현재 최신 버전은 2.0.0 입니다.

예시 입니다. 먼저, minikube 시작을 완료 해 주신 후에, Helm 명령어를 수행 합니다.

minikube start

helm upgrade --cleanup-on-fail \
  jphub-k8s jupyterhub/jupyterhub \
  --namespace jphub-k8s \
  --create-namespace \
  --version=2.0.0 \
  --values config.yaml

kubectl --namespace=jphub-k8s port-forward service/proxy-public 8080:http

만약 minikubeJupyter Kernal Image load를 하지 않으셨다면 다음과 같이, Image load를 수행 하셔야 합니다.

minikube image load jupyter/minimal-notebook:latest   

그 다음 http://localhost:8080을 접속 하면 다음과 같은 화면들을 확인 할 수 있습니다. (기본 아이디, 비밀번호는 admin/admin 입니다.)

JupyterHub Main page

Customize

config.yaml을 수정 함으로 다양한 Customize를 수행 할 수 있습니다.

OAuth

OAuth 인증을 수행 할 수 있습니다. 아래 예제는 Github 예제 입니다.

singleuser:
  image:
    name: jupyter/minimal-notebook
    tag: latest
hub:
  config:
    GitHubOAuthenticator:
      client_id: <id token>
      client_secret: <secret token>
      oauth_callback_url: http://localhost:8080/hub/oauth_callback
      allowed_organizations:
        - org:team
      scope:
        - read:org
    JupyterHub:
      authenticator_class: github

Let users select their environment

유저로 하여금, 다양한 Docker Image로 구성된 커널 중 하나를 선택 할 수 있게 할 수 있습니다. Kubespawner를 이용 하여, image, env, cpu limit, secret 등, 많은 Kubernetes 내의 자원을 이용 할 수 있습니다. 공식 Kubespawner Document도 존재 합니다.

singleuser:
  # Defines the default image
  image:
    name: jupyter/minimal-notebook
    tag: 2343e33dec46
  profileList:
    - display_name: "Minimal environment"
      description: "To avoid too much bells and whistles: Python."
      default: true
    - display_name: "Datascience environment"
      description: "If you want the additional bells and whistles: Python, R, and Julia."
      kubespawner_override:
        image: jupyter/datascience-notebook:2343e33dec46
    - display_name: "Spark environment"
      description: "The Jupyter Stacks spark image!"
      kubespawner_override:
        image: jupyter/all-spark-notebook:2343e33dec46
    - display_name: "Learning Data Science"
      description: "Datascience Environment with Sample Notebooks"
      kubespawner_override:
        image: jupyter/datascience-notebook:2343e33dec46
        lifecycle_hooks:
          postStart:
            exec:
              command:
                - "sh"
                - "-c"
                - >
                  gitpuller https://github.com/data-8/materials-fa17 master materials-fa;