Amazon EKSにJupyterHubを zero-to-jupyterhub-k8s(Z2JH)でインストールする方法をまとめます。
以下の手順に従って作業を実施します。
- Kubernetesのセットアップ
- Helmのセットアップ
- JupyterHub のセットアップ
- ACMの証明書を使用したSSLの設定
- Jupyter Notebookイメージの変更
- Adminユーザの設定
- Authenticatorの変更
- Hubイメージの変更
- 任意の設定を適用(jupyterhub_config.pyの設定)
- おまけ:EKS on Fargateは未サポート
- さいごに
Kubernetesのセットアップ
EC2をワーカーノードとしたEKSクラスタを作成します。Fargateへの導入も試しましたが、執筆時点(2020/11/27)では後述の通り未対応のようです。
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: <cluster-name> region: ap-northeast-1 version: "1.18" managedNodeGroups: - name: <node-groupname> ...
$ eksctl create cluster -f cluster.yaml
Helmのセットアップ
Helmをインストールします。
$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 11213 100 11213 0 0 38010 0 --:--:-- --:--:-- --:--:-- 38010 Downloading https://get.helm.sh/helm-v3.4.1-darwin-amd64.tar.gz Verifying checksum... Done. Preparing to install helm into /usr/local/bin Password: helm installed into /usr/local/bin/helm
以下のコマンドを実行し、空のリストが表示されればインストール成功です。
$ helm list NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
JupyterHub のセットアップ
設定ファイルの準備
セキュリティトークン proxy.secretToken
を設定した設定ファイル config.yaml
を作成します。
config.yaml
はHelmのValuesファイルです。
$ cat << EOF > config.yaml proxy: secretToken: "$(openssl rand -hex 32)" EOF
JupyterHubのインストール
JupyterHubのHelmチャートリポジトリを追加します。
$ helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/ "jupyterhub" has been added to your repositories $ helm repo update Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "jupyterhub" chart repository Update Complete. ⎈Happy Helming!⎈
config.yaml
で設定したチャートをインストールします。
$RELEASE
にはHelmのReleaseを、
$NAMESPACE
にはKubernetesのNamespaceを設定しています。
$ RELEASE=jhub $ NAMESPACE=jhub $ helm upgrade --cleanup-on-fail \ --install $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --create-namespace \ --version=0.9.0 \ --values config.yaml Release "jhub" does not exist. Installing it now.
Podの起動を確認します。
$ kubectl get pods -n jhub NAME READY STATUS RESTARTS AGE continuous-image-puller-7hwqt 1/1 Running 0 2m6s continuous-image-puller-7p8ln 1/1 Running 0 2m6s continuous-image-puller-86c7j 1/1 Running 0 2m6s hub-85f5d4b66b-nsk6q 1/1 Running 0 2m6s proxy-694ff48877-l5dph 1/1 Running 0 2m6s user-scheduler-56557c648d-sfh7f 1/1 Running 0 2m6s user-scheduler-56557c648d-t5hb8 1/1 Running 0 2m6s
Service proxy-public
がLoadBalancerとして作成されています。
$ kubectl get svc proxy-public -n jhub NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE proxy-public LoadBalancer ... ... 443:31337/TCP,80:30630/TCP 3h25m
公式の手順通りであればここで proxy-public
の EXTERNAL-IP
に接続できますが、
バージョン 0.9.0
だと以下のIssueによりロードバランサが落ちるため接続できません。
デフォルトでHTTPSが有効( proxy.https.enabled=true
)となったことで、HTTPSが未設定の場合にヘルスチェックで落ちるようです。
HTTPS対応は後述するACMの証明書を使用したSSLの設定で対応するとして、一旦 https.enabled
に false
を設定することで問題を回避します。
proxy: secretToken: "..." https: enabled: false
設定を反映します。
$ helm upgrade --cleanup-on-fail \ $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --version=0.9.0 \ --values config.yaml
これで EXTERNAL-IP
に接続できるようになります。
初期設定のAuthenticatorはDummy Authenticatorなので好きなユーザでログインできます。
ログインするとJupyter Notebookイメージを使用したシングルユーザJupyterノートブックサーバがPod jupyter-<username>
(以降、ユーザPod)で起動し、ストレージ(PersistentVolume/PersistentVolumeClaim)が割り当てられます。
以下はユーザ rick
でログインした場合に作成されるリソースの例です。
$ kubectl get pods -n jhub NAME READY STATUS RESTARTS AGE ... jupyter-rick 1/1 Running 0 3m9s $ kubectl get pv -n jhub NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE ... pvc-c60b01b1-7f42-44a6-8662-f54631bc5a52 10Gi RWO Delete Bound jhub/claim-rick gp2 21h $ kubectl get pvc -n jhub NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE ... claim-rick Bound pvc-c60b01b1-7f42-44a6-8662-f54631bc5a52 10Gi RWO gp2 21h
ユーザPodは画面右上の Quit
や Control Panel->Stop My Server
で削除されますが、
JupyterHubは一定時間ユーザによるアクションがない非アクティブなユーザPodを自動的に削除し、リソースを開放してくれます。
この挙動は config.yaml
の cull
で変更可能です。
PVとPVCは明確に削除しない限りはユーザがサーバをシャットダウンしても残り続けます。
標準ではEBS(General Purpose SSD)にデータが保存されますが config.yaml
の singleuser.storage
で変更可能です。
Customizing User Storage — Zero to JupyterHub with Kubernetes 0.0.1-set.by.chartpress documentation
ACMの証明書を使用したSSLの設定
JupyterHubのインストールではHTTPで接続しましたが、より安全に通信するためにACMで管理する証明書を使用してSSLの設定を行います。
config.yaml
に proxy.https
と proxy.service
の設定を追記します。
proxy.https.hosts
には接続に使用するホスト名を、 proxy.service.annotations
の service.beta.kubernetes.io/aws-load-balancer-ssl-cert
には証明書のARNを指定してください。
Security — Zero to JupyterHub with Kubernetes 0.0.1-set.by.chartpress documentation
proxy: secretToken: "..." https: enabled: true type: offload hosts: <hostname> service: annotations: service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "<arn>" service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600'
設定を反映します。
$ helm upgrade --cleanup-on-fail \ $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --version=0.9.0 \ --values config.yaml
続けてRoute53でエイリアスレコードを作成し、 proxy.https.hosts
に設定したホスト名で proxy-public
(CLB)にトラフィックを流します。
これでSSL通信に対応することができました。
Jupyter Notebookイメージの変更
ユーザが使用するJupyter NotebookのDockerイメージは singleuser.image
で指定することができます。
Jupyter Docker Stacksから目的にあったイメージを選択します。
以下はJulia、Python、Rのデータ分析ライブラリが詰まったdatascience-notebookを使用する例です。
singleuser: image: name: jupyter/datascience-notebook tag: 177037d09156
設定を反映します。
$ helm upgrade --cleanup-on-fail \ $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --version=0.9.0 \ --values config.yaml
Nodeにアクセスし、期待通りのイメージがPullされていることを確認できました。
$ ssh -i /path/to/key ec2-user@<node-external-ip> [ec2-user@ip-... ~]$ docker images jupyter/datascience-notebook REPOSITORY TAG IMAGE ID CREATED SIZE jupyter/datascience-notebook 177037d09156 fb0e8e0b07c7 2 years ago 6.11GB
これでJulia、Python、Rのノートブックが作成できるようになりました。
Adminユーザの設定
ユーザのサーバを管理するAdminユーザは auth.admin.users
で設定できます。
auth: admin: users: - adminuser1 - adminuser2
Adminユーザは Control Panel->Admin
から各ユーザのサーバを起動・停止したり、ユーザのノートブックにアクセスすることができます。
Authenticatorの変更
標準のDummy Authenticatorを適切なAuthenticatorに変更します。
以下のリンク先を参考にセットアップすることで、GitHubやGoogle等のユーザでログインすることができます。
Authentication — Zero to JupyterHub with Kubernetes 0.0.1-set.by.chartpress documentation
標準でサポートしていないAuthenticatorを使用する場合は、後述するHubイメージの変更で使用したいAuthenticatorをJupyterHubにインストールし、任意の設定を適用(jupyterhub_config.pyの設定)で c.JupyterHub.authenticator_classを設定します。
Hubイメージの変更
Hubイメージをカスタマイズしたい場合には以下の手順を実行します。
jupyterhub/k8s-hub:0.9.0
をベースとしたDockerfileを作成(※タグはHelmチャートのバージョンによって異なる)- 作成したイメージをDockerレジストリ(ECR)にPush
config.yaml
のhub.image
でECRのレポジトリとタグを指定
ここでは例として、REMOTE_USERでユーザを認証するREMOTE_USER authenticatorをHubイメージに追加してみます。
FROM jupyterhub/k8s-hub:0.9.0 RUN pip install jhub_remote_user_authenticator
ビルドしたコンテナイメージをECRにPushします。名前やタグは任意ですがベースイメージと合わせました。
config.yaml
を編集し、ECRに登録したHubイメージを指定します。
hub: image: name: ...ecr.ap-northeast-1.amazonaws.com/k8s-hub tag: 0.9.0
設定を反映します。
$ helm upgrade --cleanup-on-fail \ $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --version=0.9.0 \ --values config.yaml
起動したHubのPodに接続し、インストールの成功を確認します。
$ kubectl exec -it <hub-pod> -n jhub -- bash jovyan@hub-...:/srv/jupyterhub$ pip freeze | grep authenticator jhub-remote-user-authenticator==0.1.0 ...
任意の設定を適用(jupyterhub_config.pyの設定)
Helmチャートで用意された設定では対応できない場合、
hub.extraConfig
でJupyterHubの設定ファイル( jupyterhub_config.py
)に任意のコードを追加することができます。
Advanced Topics — Zero to JupyterHub with Kubernetes 0.0.1-set.by.chartpress documentation
前述のHubイメージの変更で導入したREMOTE_USER authenticatorを使用してみます。
hub: extraConfig: authConfig: | c.JupyterHub.authenticator_class = 'jhub_remote_user_authenticator.remote_user_auth.RemoteUserAuthenticator'
Pod起動時のログを確認し、 Using Authenticator: ...RemoteUserAuthenticator
と表示されていることが確認できました。
$ stern hub -n jhub + hub-c88c447c4-9c6t2 › hub hub-c88c447c4-9c6t2 hub Loading /etc/jupyterhub/config/values.yaml hub-c88c447c4-9c6t2 hub Loading /etc/jupyterhub/secret/values.yaml hub-c88c447c4-9c6t2 hub Loading extra config: authConfig hub-c88c447c4-9c6t2 hub [I 2020-11-25 11:32:20.104 JupyterHub app:2240] Running JupyterHub version 1.1.0 hub-c88c447c4-9c6t2 hub [I 2020-11-25 11:32:20.105 JupyterHub app:2271] Using Authenticator: jhub_remote_user_authenticator.remote_user_auth.RemoteUserAuthenticator ...
実際にHubにアクセスしてみると、REMOTE_USERがHTTPヘッダーにないため 401 : Unauthorized
エラーとなります。
Chrome拡張 ModHeaderでREMOTE_USERを設定するとログインに成功します。
おまけ:EKS on Fargateは未サポート
おまけです。今回ご紹介したHelmチャートはFargateに対応していませんが、インストールに挑戦してみた結果を残しておきます。
確認には以下のエントリで構築したEKSクラスタを使用しました。
JupyterHubのインストールと同様に、以下のコマンドでチャートをインストールします(Namespaceはdefaultを指定)。
$ RELEASE=jhub $ NAMESPACE=default $ helm upgrade --cleanup-on-fail \ --install $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --create-namespace \ --version=0.9.0 \ --values config.yaml
チャートのインストールがいつまで経っても終わらず、タイムアウトしてしまいました。
Error: failed pre-install: timed out waiting for the condition
$ helm list NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION jhub default 1 2020-11-23 20:19:41.511629 +0900 JST failed jupyterhub-0.9.0 1.1.0
Podを確認すると hook-image-awaiter-xxxxx
がエラー終了しています。
$ kubectl get pods NAME READY STATUS RESTARTS AGE hook-image-awaiter-52k6w 0/1 Pending 0 3s hook-image-awaiter-ksxb5 0/1 Error 0 3m49s hook-image-awaiter-p6tx4 0/1 Error 0 7m31s
エラーログは以下の通りです。
$ kubectl logs hook-image-awaiter-p6tx4 2020/11/23 11:23:28 Get https://10.100.0.1:443/apis/apps/v1/namespaces/default/daemonsets/hook-image-puller: dial tcp 10.100.0.1:443: getsockopt: connection timed out
hook-image-puller
にFargateがサポートしていないDaemonSetを使用しているためだと思われます。
hook-image-puller
は事前にDockerイメージを各NodeにPullしてくれるリソースです。
With the hook-image-puller enabled (the default), the user images being introduced will be pulled to the nodes before the hub pod is updated to utilize the new image.
関連するIssueも見つかりました。特に問題は解決しないままクローズされています。
ユーザPod起動時にイメージをPullする時間を許容できると仮定し、
DaemonSetを使わないようにhook-image-puller
を無効化してみます。
同様にノード追加時にイメージをプルする continuous-image-puller
も無効化します。
Optimizations — Zero to JupyterHub with Kubernetes 0.0.1-set.by.chartpress documentation
proxy: secretToken: "..." prePuller: hook: enabled: false continuous: enabled: false
作成したリソースを削除し、再度 helm upgrade
を実行します。
コマンドの実行が完了し、一見うまくいったように見えます。
$ helm upgrade --cleanup-on-fail \ --install $RELEASE jupyterhub/jupyterhub \ --namespace $NAMESPACE \ --create-namespace \ --version=0.9.0 \ --values config.yaml Release "jhub" does not exist. Installing it now. NAME: jhub LAST DEPLOYED: Mon Nov 23 21:47:05 2020 NAMESPACE: jhub STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: Thank you for installing JupyterHub! Your release is named jhub and installed into the namespace jhub. You can find if the hub and proxy is ready by doing: kubectl --namespace=jhub get pod and watching for both those pods to be in status 'Running'. You can find the public IP of the JupyterHub by doing: kubectl --namespace=jhub get svc proxy-public It might take a few minutes for it to appear! Note that this is still an alpha release! If you have questions, feel free to 1. Read the guide at https://z2jh.jupyter.org 2. Chat with us at https://gitter.im/jupyterhub/jupyterhub 3. File issues at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues
しかし、HubのPod( hub-xxxx
)がPendingのままで起動することはありませんでした。
$ kubectl get pods NAME READY STATUS RESTARTS AGE hub-5787d5cbc7-lnhsg 0/1 Pending 0 4m2s proxy-694ff48877-8l7zl 1/1 Running 0 4m2s user-scheduler-5f6c6d896d-8cnqh 1/1 Running 0 4m2s user-scheduler-5f6c6d896d-l28mf 1/1 Running 0 4m2s
ここで大人しくEC2を使用する方針に切り替えたのでこれ以上の調査は行っていません。
正式にサポートされた際には再度確認したいと思います。
さいごに
本エントリではZ2JHを使用してJupyterHubをEKSに導入する手順をまとめました。
J2JHでは今回触れたこと以外にもユーザリソースの設定やアクセス制限、JupyterLabを標準で使用するなど、様々な設定が可能です。
詳細が気になる方はCustomization Guideや
Administrator Guideをご確認ください。