天天看點

Zero to JupyterHub with Kubernetes @aliyun

序言

JupyterHub是一個可以支援多個客戶同時線上的Jypter管理平台

JupyterHub的目标

• A cloud provider such as Google Cloud, Microsoft Azure, Amazon EC2, IBM Cloud,Alibaba Cloud…

• Kubernetes to manage resources on the cloud

• Helm to configure and control the packaged JupyterHub installation

• JupyterHub to give users access to a Jupyter computing environment

• A terminal interface on some operating system

引子

JupyterHub并沒有提供alibaba的指導,是以本文補充在JupyterHub在aliyun 容器服務上從零開始的步驟。

此外在GPU的使用上通過GPU共享方案--CGPU來提高GPU的使用率。

操作步驟

建立kubernetes叢集

  1. 建立容器服務叢集
  2. 添加GPU節點
  3. 設定GPU節點為共享模式,參考《嘗鮮阿裡雲容器服務Kubernetes 1.16,擁抱GPU新姿勢》

安裝JupyterHub

安裝Helm

阿裡雲容器服務目前預設支援的Helm版本為v3。

https://github.com/helm/helm/releases/

頁面找到最新的版本,下載下傳helm檔案,并添加到對應的path路徑中。

  1. Generate a random hex string representing 32 bytes to use as a security token. Run this command in a terminal and copy the output:
openssl rand -hex 32           
  1. 建立PVC和StorageClass資訊

    建立Nas存儲,并建立挂載點。參考連結

    https://help.aliyun.com/document_detail/144398.html?spm=a2c4g.11186623.6.757.71aa2b8djPh6fE

如下檔案存儲為storage.yaml 檔案,并通過 `kubectl apply -f storage.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: alicloud-nas-subpath
mountOptions:
- nolock,tcp,noresvport
- vers=3
parameters:
  volumeAs: subpath
  server: "0994fd65-66f5.cn-zhangjiakou.extreme.nas.aliyuncs.com:/share"   #需要放自己的nas的挂載點,操作參考 https://help.aliyun.com/document_detail/144398.html?spm=a2c4g.11186623.6.757.71aa2b8djPh6fE
provisioner: nasplugin.csi.alibabacloud.com
reclaimPolicy: Retain
#---
#kind: PersistentVolumeClaim
#apiVersion: v1
#metadata:
#  name: hub-db-dir
#  namespace: jhub
#spec:
#  accessModes:
#    - ReadWriteMany
#  storageClassName: alicloud-nas-subpath
#  resources:
#    requests:
#      storage: 1Gi           
  1. 修改原始config檔案
  2. 修改config.yaml檔案中各個鏡像的路徑,避免拉取google倉庫的鏡像而造成的失敗
  3. 因為目前使用的版本0.8.2不能和k8s1.16正确比對,在install腳本中添加了對應的path 腳本參考 https://github.com/jupyterhub/kubespawner/issues/354
  4. 修改pod申請gpu資源的配置,設定CGPU的模式 aliyun.com/gpu-mem: 4 https://zero-to-jupyterhub.readthedocs.io/en/latest/customizing/user-resources.html
    以上修改已經在下面的config.yaml檔案中完成了修改。 注意proxy.secretToken必須換為第一步生成的資訊
               
proxy:
  secretToken: "d8d198d787f22869e67df3ad3ac5f4d99a843c20243f9ed785f77822fb4ce517" ## 該token選用自己在步驟1中生成的即可
prePuller:
  continuous:
    enabled: false
  extraImages: {}
  hook:
    enabled: true
    image:
      name: jupyterhub/k8s-image-awaiter
      tag: 0.8.2
  pause:
    image:
      # 替換預設的google鏡像
      name: registry.cn-zhangjiakou.aliyuncs.com/kubernetesmirror/pause 
      tag: "3.1"
hub:
  #image:
  #  name: jupyterhub/k8s-hub
  #  tag: 0.9.0-beta.3
  #  0.8.2  # 嘗試提高該版本來與kubernetes 1.16比對,待驗證https://github.com/jupyterhub/kubespawner/issues/354
  db:
    password: null
    pvc:
      accessModes:
      - ReadWriteOnce
      annotations: {}
      selector: {}
      storage: 1Gi
      storageClassName: alicloud-nas-subpath
singleuser:
  storage:
    capacity: 2Gi
    dynamic:
      pvcNameTemplate: claim-{username}{servername}
      storageAccessModes:
      - ReadWriteOnce
      storageClass: alicloud-nas-subpath
      volumeNameTemplate: volume-{username}{servername}
  profileList:
    - display_name: "CGPU Server" ## 共享GPU使用模式
      description: "Spawns a notebook server with access to a CGPU"
      kubespawner_override:
        extra_resource_limits:
          aliyun.com/gpu-mem: 2
    - display_name: "GPU Server"  ## 普通GPU使用模式
      description: "Spawns a notebook server with access to a GPU"
      kubespawner_override:
        extra_resource_limits:
          nvidia.com/gpu: 1
  image:
    #name: jupyterhub/k8s-singleuser-sample
    #name: tensorflow/tensorflow
    pullPolicy: IfNotPresent
    # 替換為自己需要的鏡像即可,本例中使用的是支援TensorFlow的鏡像,可以從官方網站上去找合适的鏡像
    name: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tensorflow-notebook
    tag: 1.15.2           
  1. 執行安裝腳本

    将如下記憶體存儲為install.sh 檔案,并将步驟3存儲的config.yaml 檔案放在同一個目錄下,執行安裝指令

bash sh ./install.sh

# Suggested values: advanced users of Kubernetes and Helm should feel
# free to use different values.
RELEASE=jhub
NAMESPACE=jhub
helm upgrade --install $RELEASE jupyterhub/jupyterhub \
  --namespace $NAMESPACE  \
  --version=0.8.2 \
  --values config.yaml -v 6
sleep 30
export NAMESPACE=jhub
kubectl patch deploy -n $NAMESPACE hub --type json --patch '[{"op": "replace", "path": "/spec/template/spec/containers/0/command", "value": ["bash", "-c", "\nmkdir -p ~/hotfix\ncp -r /usr/local/lib/python3.6/dist-packages/kubespawner ~/hotfix\nls -R ~/hotfix\npatch ~/hotfix/kubespawner/spawner.py << EOT\n72c72\n<             key=lambda x: x.last_timestamp,\n---\n>             key=lambda x: x.last_timestamp and x.last_timestamp.timestamp() or 0.,\nEOT\n\nPYTHONPATH=$HOME/hotfix jupyterhub --config /srv/jupyterhub_config.py --upgrade-db\n"]}]'           

注意:“kubectl patch deploy...” 該指令執行是在安裝helm後30秒後執行,注意耐心等待,不要提前終止

驗證與使用

  1. 驗證pod和svc的狀态均為正常
jumper(⎈ |zjk-gpu:jhub)➜  ~ k get pod -n jhub
NAME                     READY   STATUS    RESTARTS   AGE
hub-86d7754c55-jnsd8     1/1     Running   0          10h
proxy-657b654c85-htl62   1/1     Running   0          10h
jumper(⎈ |zjk-gpu:jhub)➜  ~ k get svc -n jhub
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
hub            ClusterIP      172.21.10.208   <none>        8081/TCP                     10h
proxy-api      ClusterIP      172.21.13.171   <none>        8001/TCP                     10h
proxy-public   LoadBalancer   172.21.15.40    47.92.24.78   80:30889/TCP,443:31823/TCP   10h           
  1. 根據通過svc proxy-public 對應的公網IP通路網站,由于沒有設定使用者密碼,可以随意設定
    Zero to JupyterHub with Kubernetes @aliyun
  2. 如下圖是在config.yaml檔案中配置的多個profile,分别是申請普通的GPU資源,以及共享型的GPU資源
    Zero to JupyterHub with Kubernetes @aliyun
  3. 如下圖,正在建立正在使用的pod
    Zero to JupyterHub with Kubernetes @aliyun
  4. 檢視命名空間 jhub下的pod的情況,有一個jupyter-${使用者名}的pod生成
jumper(⎈ |zjk-gpu:jhub)➜  54_cgpu_demo git:(master) ✗ k get pod
NAME                     READY   STATUS    RESTARTS   AGE
hub-5ff8cff85f-nmhfl     1/1     Running   0          46h
jupyter-lilong           1/1     Running   0          17s
proxy-657b654c85-8mn6t   1/1     Running   0          46h           
  1. 至此,環境建立成功。

問題記錄

搭建過程中的問題與記錄

問題1 NoneType

錯誤資訊

[E 2020-05-30 00:24:14.373 JupyterHub base:1011] Preventing implicit spawn for a because last spawn failed: '<' not supported between instances of 'NoneType' and 'NoneType'

說明: 已知問題,參考

上面在安裝helm chart之後的“kubectl patch deploy...”就是修複該問題

問題2 存儲

不管是hub的建立,還是每個客戶運作自己環境建立的pod均需要使用到存儲,如果pod啟動pending,且kubectl describe pod * 裡面顯示存儲資源不足,可以參考nas相關的存儲的設定。

參考

  1. NAS動态存儲卷 https://help.aliyun.com/document_detail/144398.html
  2. JupyterHub在k8s上的安裝 https://zero-to-jupyterhub.readthedocs.io/en/latest/
  3. 自定義環境 https://zero-to-jupyterhub.readthedocs.io/en/latest/customizing/user-environment.html