Oracle Container Engine for Kubernetes(OKE)のカーネルリソースをJupyter Enterprise Gatewayでローカル環境から使ってみる

# はじめに([前回記事](https://qiita.com/takuya_0301/items/c6e40166c37b8436698c)のサマリ)
前回、OKE上にKubeflowを利用して機械学習プラットフォームを構築するという記事を書きました。
これだけだと、単にマネージドKubernetes上に機械学習プラットフォームを構築しただけという形なので、今回の記事ではOKE上のカーネルリソースをローカルのJupyter Notebookから利用するという記事を書いてみたいと思います。

# Jupyter Enterprise Gatewayとは
Jupyter Notebookの機能の一つにJupyter Enterprise Gatewayという非常に便利な機能があります。
Jupyter Enterprise Gatewayは、マルチクラスター環境で複数ユーザが利用できるようにするための機能を提供するプラグインです。

後ほど説明しようと思いますが、このJupyter Enterprise Gatewayによって、通常はリモート上のJupyter Notebookで起動するカーネルをWebサーバ上で起動できます。
この際にKubernetesやHadoop YARNなどの分散コンピューティング基盤を利用すると、クラスター全体にカーネルを分散し、同時に接続可能なカーネル数を劇的に向上させることができます。

今回は、OKE上のカーネルに対して、ローカル環境にあるJupyter Notebookから接続してみるということをやってみます。

# 既存環境の確認
まずは、今の環境を確認していきます。
環境は、前回の記事で構築した状態とします。
念の為、Podを確認しておくと以下のような状態です。(kube-system Namespaceは除外しています)

```sh
[opc@kubeflow-client mykubeflow]$ kubectl get pod -A
NAMESPACE         NAME                                                         READY   STATUS      RESTARTS   AGE
cert-manager      cert-manager-7c75b559c4-s7tjm                                1/1     Running     0          6m23s
cert-manager      cert-manager-cainjector-7f964fd7b5-lvdvc                     1/1     Running     0          6m23s
cert-manager      cert-manager-webhook-566dd99d6-5wrmg                         1/1     Running     1          6m23s
istio-system      cluster-local-gateway-5898bc5c74-djc9t                       1/1     Running     0          6m37s
istio-system      istio-citadel-6dffd79d7-n4cvl                                1/1     Running     0          6m47s
istio-system      istio-galley-77cb9b44dc-548gl                                1/1     Running     0          6m48s
istio-system      istio-ingressgateway-7bb77f89b8-k2pnl                        1/1     Running     0          6m48s
istio-system      istio-nodeagent-59lvb                                        1/1     Running     0          6m48s
istio-system      istio-nodeagent-b5f6m                                        1/1     Running     0          6m49s
istio-system      istio-nodeagent-p2k8f                                        1/1     Running     0          6m48s
istio-system      istio-nodeagent-xtw66                                        1/1     Running     0          6m48s
istio-system      istio-pilot-67d94fc954-vjj8k                                 2/2     Running     0          6m48s
istio-system      istio-policy-546596d4b4-784pl                                2/2     Running     1          6m47s
istio-system      istio-security-post-install-release-1.3-latest-daily-6bm4w   0/1     Completed   0          6m47s
istio-system      istio-sidecar-injector-796b6454d9-fq5gj                      1/1     Running     0          6m47s
istio-system      istio-telemetry-58f9cd4bf5-qv6tn                             2/2     Running     1          6m47s
istio-system      prometheus-7c6d764c48-7k5ss                                  1/1     Running     0          6m48s
knative-serving   activator-6c87fcbbb6-jlk6x                                   1/1     Running     0          4m28s
knative-serving   autoscaler-847b9f89dc-7mspk                                  1/1     Running     0          4m29s
knative-serving   controller-55f67c9ddb-q7xk5                                  1/1     Running     0          4m28s
knative-serving   istio-webhook-db664df87-czd2r                                1/1     Running     0          4m30s
knative-serving   networking-istio-76f8cc7796-rw458                            1/1     Running     0          4m29s
knative-serving   webhook-6bff77594b-b5rz5                                     1/1     Running     0          4m30s
kubeflow          admission-webhook-bootstrap-stateful-set-0                   1/1     Running     2          5m22s
kubeflow          admission-webhook-deployment-5cd7dc96f5-x9xls                1/1     Running     1          4m47s
kubeflow          application-controller-stateful-set-0                        1/1     Running     0          7m4s
kubeflow          argo-ui-65df8c7c84-vnz74                                     1/1     Running     0          4m55s
kubeflow          cache-deployer-deployment-5f4979f45-2d5s6                    2/2     Running     2          4m55s
kubeflow          cache-server-7859fd67f5-w56hs                                2/2     Running     0          4m48s
kubeflow          centraldashboard-67767584dc-r9wvq                            1/1     Running     0          4m54s
kubeflow          jupyter-web-app-deployment-8486d5ffff-j76h4                  1/1     Running     0          4m54s
kubeflow          katib-controller-7fcc95676b-b5qfc                            1/1     Running     1          4m54s
kubeflow          katib-db-manager-85db457c64-lzprn                            1/1     Running     1          4m54s
kubeflow          katib-mysql-6c7f7fb869-cd4s2                                 1/1     Running     0          4m51s
kubeflow          katib-ui-65dc4cf6f5-8vw4s                                    1/1     Running     0          4m53s
kubeflow          kfserving-controller-manager-0                               2/2     Running     0          4m18s
kubeflow          kubeflow-pipelines-profile-controller-797fb44db9-b6dn6       1/1     Running     0          4m53s
kubeflow          metacontroller-0                                             1/1     Running     0          5m22s
kubeflow          metadata-db-6dd978c5b-ppdzw                                  1/1     Running     0          4m53s
kubeflow          metadata-envoy-deployment-67bd5954c-6kqqv                    1/1     Running     0          4m53s
kubeflow          metadata-grpc-deployment-577c67c96f-fmwd2                    1/1     Running     1          4m52s
kubeflow          metadata-writer-756dbdd478-mbg8z                             2/2     Running     0          4m52s
kubeflow          minio-54d995c97b-qcv5m                                       1/1     Running     0          4m52s
kubeflow          ml-pipeline-7c56db5db9-s2scf                                 2/2     Running     1          4m52s
kubeflow          ml-pipeline-persistenceagent-d984c9585-4gq6b                 2/2     Running     0          4m52s
kubeflow          ml-pipeline-scheduledworkflow-5ccf4c9fcc-rzr7c               2/2     Running     0          4m51s
kubeflow          ml-pipeline-ui-7ddcd74489-bqp66                              2/2     Running     0          4m48s
kubeflow          ml-pipeline-viewer-crd-56c68f6c85-z7829                      2/2     Running     3          4m51s
kubeflow          ml-pipeline-visualizationserver-5b9bd8f6bf-wjww6             2/2     Running     0          4m51s
kubeflow          mpi-operator-d5bfb8489-69h7h                                 1/1     Running     0          4m50s
kubeflow          mxnet-operator-7576d697d6-2gvfm                              1/1     Running     0          4m50s
kubeflow          mysql-74f8f99bc8-hjc7t                                       2/2     Running     0          4m50s
kubeflow          notebook-controller-deployment-5bb6bdbd6d-4ldpf              1/1     Running     0          4m50s
kubeflow          profiles-deployment-56bc5d7dcb-2rs8x                         2/2     Running     0          4m49s
kubeflow          pytorch-operator-847c8d55d8-v5dwn                            1/1     Running     0          4m49s
kubeflow          seldon-controller-manager-6bf8b45656-46g4m                   1/1     Running     0          4m49s
kubeflow          spark-operatorsparkoperator-fdfbfd99-qvr2f                   1/1     Running     0          5m22s
kubeflow          spartakus-volunteer-558f8bfd47-zj66n                         1/1     Running     0          4m16s
kubeflow          tf-job-operator-58477797f8-27fv8                             1/1     Running     0          4m48s
kubeflow          workflow-controller-64fd7cffc5-hlx6n                         1/1     Running     0          4m48s
```

この環境にJupyter Enterprise Gatewayをデプロイしていきます。

# Jupyter Enterprise Gatewayのデプロイ

Jupyter Enterprise Gatewayのデプロイも非常に簡単です。
以下の1行を叩くだけです。

```sh
kubectl apply -f https://raw.githubusercontent.com/jupyter/enterprise_gateway/master/etc/kubernetes/enterprise-gateway.yaml
```

これでJupyter Enterprise GatewayのデプロイするためのManifestが適用され、以下のようなPodがデプロイされます。

```sh
[opc@kubeflow-client mykubeflow]$ kubectl get pod -n enterprise-gateway
NAME                                  READY   STATUS             RESTARTS   AGE
enterprise-gateway-647648f598-qzxxg   1/1     Running            0          8m
kernel-image-puller-8c4zm             1/1     Running            0          7m59s
kernel-image-puller-h9dts             1/1     Running            0          7m59s
kernel-image-puller-qldsr             1/1     Running            0          7m59s
kernel-image-puller-r5nq5             1/1     Running            0          8m
```

これでJupyter Enterprise Gatewayのデプロイは完了です！

# 動作確認

それでは動作確認をしていきます。


## Jupyter Notebook(Jupyter Lab)のインストール
まず、事前準備を行っていきます。

ローカル端末にJupyter Notebook(Jupyter Lab)がインストールされていない場合は以下のコマンドでインストールしましょう。
(Pythonがインストールされていない場合は、[こちら](https://www.python.org/)からPython3系をインストールしてください)

```sh
pip install jupyterlab
```

## Jupyter Notebook(Jupyter Lab)の起動

インストールが完了したら、以下のコマンドでJupyter Labを起動します。

```sh
JUPYTER_GATEWAY_URL=http://<Node IP>:<NodePort> jupyter lab --no-browser
```

`JUPYTER_GATEWAY_URL`に定義するURLはOKE上にデプロイされたJupyter Enterprise GatewayのPodが稼働しているNodeのIPとNodePortです。
例えば、以下のような場合、

```sh
[opc@kubeflow-client ~]$ kubectl get pod,svc -n enterprise-gateway -o wide |grep enterprise
NAME                                      READY   STATUS             RESTARTS   AGE     IP             NODE          NOMINATED NODE   READINESS GATES
pod/enterprise-gateway-647648f598-qzxxg   1/1     Running            0          5h13m   10.244.0.18    10.0.10.195   <none>           <none>
service/enterprise-gateway   NodePort   10.96.8.222   <none>        8888:30567/TCP,8877:31965/TCP   5h13m   gateway-selector=enterprise-gateway
```

このようになります。

```sh
JUPYTER_GATEWAY_URL=http:// 10.0.10.195:30567 jupyter lab --no-browser
```

OKEのクライアント上では、Jupyter Notebookがポート8888で起動しているので、OKEクライアントのポート8888をローカル環境の任意のポートにポートフォワードします。

ローカルのブラウザから`http://localhost:<ローカル環境の任意のポート>`にアクセスすると以下の画面が起動します。

![スクリーンショット 2021-08-11 12.35.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/8e95004f-7d40-81e8-05c2-206c043ad8ba.png)

これで事前準備完了です！

## Jupyter NotebookでOKE上のカーネルリソースを使う

いよいよ動作確認本番です。

Notebookには様々な種類がありますが、今回は`Python on Kubernetes`を利用してみます。

`Python on Kubernetes`をクリックすると、従来のJupyter Notebookが起動します。

このとき、右下の赤枠の部分が`Idle`状態になっていれば、無事OKE上のカーネルを利用できています！

![スクリーンショット 2021-08-11 12.42.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/c069587f-2cef-47e8-b016-f4c448e3c1df.png)

ちなみにJupyter Notebook(Jupyter Lab)を起動したOKEクライアント側の標準出力メッセージを見てみると、以下のような内容が出力されています。

```sh
[I 2021-08-11 03:41:50.850 ServerApp] Creating new notebook in 
[I 2021-08-11 03:41:51.846 ServerApp] Request start kernel: kernel_id=None, path=''
[I 2021-08-11 03:41:55.219 ServerApp] GatewayKernelManager started kernel: 4350a9d2-3def-462a-aed6-bab41664a6cb, args: {'kernel_name': 'python_kubernetes', 'cwd': '/home/opc'}
[I 2021-08-11 03:41:55.684 ServerApp] Connecting to ws://10.0.10.195:30567/api/kernels/4350a9d2-3def-462a-aed6-bab41664a6cb/channels
[I 2021-08-11 03:43:51.895 ServerApp] Saving file at /Untitled.ipynb
```

先ほど起動時に設定したJUPYTER_GATEWAY_URLを経由してWebSocketで接続していることが確認できます。

これで、ローカル環境からOKEのカーネルリソースを使ってJupyter Notebookを起動することができました！
ローカル環境のスペックを気にすることなく、ガンガン機械学習プロセスを回すことができますね！！
また、これを利用するとローカル環境にGPUを積んでいなくても、OKEのGPUプール(OKEではWorker NodeにGPUインスタンスを利用することが可能)を利用して機械学習を行うこともできるので、夢が広がります！

## 【おまけ】VSCodeからも動作確認してみる

ここからはおまけです。
先ほどはブラウザでJupyter Notebookを起動しましたが、ここではVSCodeからやってみようと思います。
前提として、VSCodeに[Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)および[Jupyter](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter)の拡張機能のインストールが必要です。

VSCodeを開いて、コマンドパレットを開き、`Jupyter: Specify local or remote Jupyter server for Connections`を入力します。
`Specify the URI of an existing server`というメニューが出てくるので、こちらをクリックしてOKEクライアントのJupyter Notebook(Jupyter Lab)を起動時のメッセージ内にある以下の部分をコピーし、貼り付けます。

```sh
 http://localhost:8888/lab?token=<トークン>
```

入力すると、VSCodeの再起動が要求されるので、再起動します。

再起動後、Jupyter Notebookが起動するはずです。
ここで、右上にある`カーネルの選択`部分ををクリックすると、先ほどのブラウザで起動した時と同じようなカーネルの種類が選択できるようになっています。
![2021-08-11 14_52_16-Window.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/ba93eace-720a-cf4e-c89e-236ce942c5a8.png)


これでVSCode上からJupyter Enterprise Gateway経由でリモートカーネルを利用できます。

# まとめ
Kubernetes上にJupyter Enterprise Gatewayをデプロイすると、上記のようにKubernetes上のリソースプールを効率的に利用してローカルで学習プロセスを回すことができます。
特にGPUを利用するような大規模が学習が必要な場合には強力なプラットフォームになります。

従来はアプリケーション基盤として利用しがちなKubernetesですが、このように分散コンピューティングの仕組みとして利用することもできるので、みなさんも試してみてください！

# 参考資料

* Jupyter Enterprise Gatewayドキュメント
    * https://jupyter-enterprise-gateway.readthedocs.io/en/latest/