JupyterHub with K8s 步驟一到六
在 GCP Kubernetes Engines 建置 JupyterHub 的筆記

A multi-user version of the notebook designed for companies, classrooms and research labs.
TL; DR
依循 Zero to JupyterHub with Kubernetes 官方文件,建置一個讓學員以 GitHub 帳號登入使用有持續性(Persistent)的 Jupyter Notebook 伺服器。
緣起
雖然很清楚多數學員的使用習慣其實還是「本機端」開發環境,但是伴隨著作業系統、Python 版本、開發介面還有環境模組管理套件這四個面向的排列組合,常在一開始就容易讓人(不管是學員或老師)覺得有些疲憊:
- 作業系統:Windows、macOS、Linux
- Python 版本:Python.org、Anaconda
- 開發介面:IDLE、PyCharm、Spyder、Jupyter Notebook、Visual Studio Code
- 環境模組管理套件:venv、pip、conda
當然我並不覺得可以完全不用理會環境建置的議題,只是以 Python 多樣化的使用管理排列組合,更會希望學員是在已經對 Python 還有命令列熟悉的情況下,再比較完整地認識該怎麼在一台電腦中設定 Python、PATH、虛擬環境還有專案管理。
為什麼使用 JupyterHub with K8s
假如希望可以跳過本機端開發環境建置切入教學的主題,簡單來說就是讓學員只需要透過「瀏覽器」跟「網路」就可以進行程式撰寫跟輸出檢視;以資料科學開發介面的 Jupyter Notebook 來說,符合這個特性的選項有三個:
- Google Colab:Google 文件版的 Jupyter Notebook,可以即時互動、協作,缺點是沒有終端機介面、沒有文字編輯器介面、不能將資料與筆記本一起共享;
- BinderHub:奠基於 repo2docker 技術的 JupyterHub,可以開啟終端機、使用文字編輯器以及共享資料集;缺點是無持續性(Non-persistent)以及閒置斷線時間過短(10 分鐘),不過這算是無可厚非的,畢竟 BinderHub 並不需要使用者註冊登入且沒有收費;
- JupyterHub:多人版本的 Jupyter 伺服器,可以開啟終端機、使用文字編輯器、共享資料集、有持續性(Persistent)以及閒置斷線時間可以由管理員設定。
而 JupyterHub 其實也分兩個版本:
由於我的 JupyterHub 教學場景是一個學期的三學分課程,在這一個學期中,學員的使用頻率應該有很大的變異性,可能在課堂進行、作業繳交期限以及期中期末考試這些特定的時段才會有比較大的用量,我選擇了 JupyterHub with K8s。
步驟一到六
步驟一到六基本上就是依循 Zero to JupyterHub with Kubernetes 官方文件的指引,並在過程中加入一些容易忽略的備註。
在開始之前,請注意一個預設的 JupyterHub with K8s 每天的基本花費約 5 塊美金,如果讀者只想試著操作一遍,事後請務必記得將專案關閉刪除。
- 建置 Kubernetes Engine
- 安裝 Helm
- 安裝 JupyterHub
- 設定網域名稱
- 設定 HTTPS
- 設定 GitHub OAuth
步驟一:建置 Kubernetes Engine
登入自己的 Google 雲端平台 https://console.cloud.google.com/ 建立一個新專案:JupyterHub with K8s。

來到新專案的儀表板之後,點選 Go to APIs overview。

點選 ENABLE APIS AND SERVICES。

在搜尋文字方框中輸入 Kubernetes Engine API。

點選 Kubernetes Engine API。

啟動 Kubernetes Engine API。

Google 雲端平台會花費一些時間啟動 Kubernetes Engine API ,請務必記得操作這個步驟,否則接下來在 Cloud Shell 的指令將發生錯誤;接著在右上角點選 Activate Cloud Shell。

Cloud Shell 啟動之後,輸入指令建置叢集(cluster)
gcloud container clusters create \
--machine-type n1-standard-2 \
--num-nodes 2 \
--zone asia-east1-c \
--cluster-version latest \
jhub需要自行調整輸入的參數有:
- zone:調整輸入為 asia-east1-c 指向台灣彰化縣的機房,可供選擇的清單可以參考 https://cloud.google.com/compute/docs/regions-zones/#available
- CLUSTERNAME:我們調整輸入為 jhub
執行完畢後可以使用 kubectl get node 指令檢查叢集是否已經啟動;接下來的指令是將帳號調整為叢集管理員權限。
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=<GOOGLE-EMAIL-ACCOUNT>需要自行調整的參數是替換
gcloud beta container node-pools create user-pool \
--machine-type n1-standard-2 \
--num-nodes 0 \
--enable-autoscaling \
--min-nodes 0 \
--max-nodes 3 \
--node-labels hub.jupyter.org/node-purpose=user \
--node-taints hub.jupyter.org_dedicated=user:NoSchedule \
--zone asia-east1-c \
--cluster jhub步驟二:安裝 Helm
Helm 是 K8s 的套件管理工具,可以協助我們安裝、升級和管理套件,Helm 之於 K8s 就像是 pip 之於 Python、npm 之於 nodejs 的關係;我們將使用 Helm chart 管理 K8s 設定檔,如此未來透過編輯 config.yaml 檔案即可完成 JupyterHub 的安裝與部署。
使用 curl 指令下載安裝指令並執行。
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash步驟三:安裝 JupyterHub
在終端機以 openssl rand -hex 32 指令生成一組金鑰,並複製起來。在家目錄新增 config.yaml 檔,並貼入以下的內容,替換 <RANDOM_HEX> 為前一個指令所生成的金鑰;官方文件使用的文字編輯器是 nano,我習慣使用 vim 所以直接以 vim config.yaml 指令新增設定檔,並新增金鑰至文件中後儲存離開。
proxy:
secretToken: "<RANDOM_HEX>"新增 JupyterHub Helm chart 為安裝做準備。
helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/
helm repo update在家目錄執行安裝指令,請 Helm chart 透過 config.yaml 安裝 JupyterHub。
RELEASE=jhub
NAMESPACE=jhubhelm upgrade --cleanup-on-fail \
--install $RELEASE jupyterhub/jupyterhub \
--namespace $NAMESPACE \
--create-namespace \
--version=0.9.0 \
--values config.yaml步驟四:設定網域名稱
JupyterHub 安裝完畢之後,可使用指令 kubectl get service — namespace jhu 查詢可以透過哪一組 IP 位址登入 JupyterHub,在 (proxy-public, EXTERNAL-IP) 就能檢視該組 IP。
將 proxy-public 所對應的 EXTERNAL-IP 複製貼上至瀏覽器網址列即可來到 JupyterHub 的登入頁面;此時可以注意到登入頁面有一個安全連線的警告訊息,值得注意的是,必須在設定完網域名稱後才能進行 HTTPS 設定。

前往網域名稱供應商做好設定,以我的例子來說是從既有的網域新增一個子網域,那麼就要新增一筆 A 記錄對應 JupyterHub 的 EXTERNAL-IP。
A jhub <YOUR_JHUB_EXTERNAL_IP>稍微等待一下,將 jhub.YOUR-DOMAIN-NAME 輸入至瀏覽器網址列,就可以來到 JupyterHub 的登入頁面,接著才能設定 HTTPS。
步驟五:設定 HTTPS
JupyterHub 使用 Let’s Encrypt 服務自動產生 HTTPS 憑證,我們只需要透過編輯 config.yaml 就可以完成設定,在 config.yaml 中的 proxy 項目下加入 HTTPS 設定後儲存文件離開。
proxy:
secretToken: "<RANDOM_HEX>"
https:
enabled: true
hosts:
- <YOUR-DOMAIN-NAME>
letsencrypt:
contactEmail: <YOUR-EMAIL-ADDRESS>每一次更新 config.yaml 設定檔後都需要執行 helm upgrade 指令讓更新生效。
RELEASE=jhub
NAMESPACE=jhubhelm upgrade --cleanup-on-fail \
--install $RELEASE jupyterhub/jupyterhub \
--namespace $NAMESPACE \
--create-namespace \
--version=0.9.0 \
--values config.yaml如果有發現找不到 Helm 指令的情況,可以重新安裝 Helm 之後在執行 helm upgrade 指令。
步驟六:設定 GitHub OAuth
截至目前為止,我們所建置好的 JupyterHub with K8s 可以透過 jhub.YOUR-DOMAIN-NAME 讓任何人註冊登入使用,還需要再設定 GitHub OAuth,讓管理員能夠驗證上課學員。
透過帳戶圖片、設定(Settings)來到開發者設定(Developer settings),點選 OAuth Apps 然後新增 OAuth Apps。

為新增的 OAuth App 註冊獲得一組 Client ID 與 Client Secret。

在 config.yaml 設定檔中的 auth 項目新增 OAuth App 的 Client ID、Client Secret 以及 Callback URL。
auth:
type: github
github:
clientId: "<y0urg1thubc1ient1d>"
clientSecret: "<an0ther1ongs3cretstr1ng>"
callbackUrl: "https://jhub.YOUR-DOMAIN-NAME/hub/oauth_callback"設定完成 OAuth App 之後,學員只要註冊有 GitHub 帳號就可以登入使用 JupyterHub,最後是將課堂學員的 GitHub 帳戶新增到白名單(whitelist)中,文件儲存離開後,也別忘了再執行一次 helm update 的指令。
auth:
type: github
github:
clientId: "<y0urg1thubc1ient1d>"
clientSecret: "<an0ther1ongs3cretstr1ng>"
callbackUrl: "https://jhub.YOUR-DOMAIN-NAME/hub/oauth_callback"
whitelist:
users:
- student1
- student2
# ...大功告成!我們順利完成了步驟一到六,建置了一個有持續性的 JupyterHub with K8s 可以讓學員以 GitHub 帳號登入使用!
