kubernetes v1.23.6/Nvidia GPUと戯れる その6

[前回](https://qiita.com/yoshiono/items/4da34a6cf8e52dca0895)からの続きになります。

# 作業手順
以下となります。
1. 作業環境の準備
2. Nvidia CUDA Toolkit、docker、Nvidia Container Toolkit
3. kubeadmでクラスター構築
4. helmとGPU Operator
5. IstioとMetalLB
6. Jupyterhub ←ココ

# 5. Jupyterhub
- [こちら](https://qiita.com/jjjkkkjjj/items/b62a7424065c5537348e)によると「JupyterHubとは，マルチユーザーに対応した認証機能つきのJupyterサーバーです。」とのことです。MLOpsで流行の仕組みなので選択しました。
- [こちら](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/installation.html)が、Jypterhub for kubernetesのインストール手順です。マニュアルに記載されているシンプルなインストール方法にはPersistent Volumeの作業が含まれていないと思っていたら、やっぱり、ユーザーデータ(ipynbファイル)を永続的に保存できません(k8sを再起動するとユーザーデータは消えてしまいます)。
    - 試行錯誤の結果、以下の手順でユーザーデータを保存できるようにしました。
        - ①NFS用のPvovisionerの導入
        - ②PVCを作成する 
        - ③Helmのリポジトリを登録、config.yamlを作成
        - ④helm upgradeでJupyterhubをインストール
        - オプション⑤Jupyterhubで自分のDockerイメージを使えるようにする


# ①NFS用のPvovisionerの導入
- まずは、ユーザーデータ(ipynbファイル)を保存できるようにする必要があります。Jupyterhubのマニュアルの[Customizing User Storage](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/customizing/user-storage.html)にユーザーデータの保存について説明されています。k8s側の作業としてPVCを作成し、それをconfig.yamlに指定してjupyterhubをインストールします。
    - JupyterhubはUser storageを管理するためにk8sを使います。PodにStorageを割り当てることに関する2つの主要なk8s objectがあります。
        - PVC(Persistent Vlume Clame)は、要求されたStorageの種類を定義しています。その構成はconfig.yamlに記述します。
        - PV(Persistent Volume)はユーザーデータが保存される実際のボリュームで、PVCの定義を使ってk8sが作成します。
- k8sにおけるPVの[マニュアル](https://kubernetes.io/ja/docs/concepts/storage/persistent-volumes/)の要約は以下です。
    - PVは静的か動的どちらかでプロビジョニングされます。
        - 静的：クラスター管理者は多数のPVを作成します。それらはクラスターのユーザーが使うことのできる実際のストレージの詳細を保持します。それらはKubernetes APIに存在し、利用できます。
        - 動的：クラスターはSC(StorageClass)に基づいてPVC用にボリュームを動的にプロビジョニングします。
- 結論として、jupyterhubのPV割当は動的プロビジョニングで実現されており、JupyterhubでユーザーデータをPVに保存するためには、PVCをconfig.yamlに記述する必要があり、PVCを定義するためにはSCを作成する必要がある、となります。
    - 動的プロビジョニングの説明は[こちら](https://kubernetes.io/ja/docs/concepts/storage/dynamic-provisioning/)です。
    - SCの説明は[こちら](https://kubernetes.io/docs/concepts/storage/storage-classes/)です。また、Jupyterhubの[マニュアル](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/customizing/user-storage.html#type-of-storage-provisioned)によると「StorageClassオブジェクトは、どのような種類のPersistentVolumesがユーザーのためにプロビジョニングされるかを決定するために使用されます。」となっています。
   - Storage ClassはProvisionerを指定して構成します。本環境ではユーザーデータはNFSに保存することを想定しているので、ProvisionerはNFSを選択します。また、特に根拠はなく、NFS Provisionerはnfs-subdir-external-provisionerというパッケージを選択しました。インストール手順は[こちら](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner)です。今回のインストールコマンドは以下です。
        - storageClass.defaultClass:　jupyterhubのconfig.yamlでSCを明示するので不要な気はするのですが、一応、default SCになるように指定します。
        - nfs.server、nfs.pathで、それぞれ、NFSのServer名とマウントポイントを指定します(NFS Clientとして設定済なのになんで？、と思いますが)。
        - storageClass.nameで、任意のSC名(ここでは、nfs-storage)を指定します。
```OS:nfs-external-provisionerのインストール
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=linux02 \
    --set nfs.path=/root/share \
    --set storageClass.defaultClass=true \
    --set storageClass.name=nfs-storage
```

# ②PVCを作成する 
- PVCのマニュアルは[こちら](https://kubernetes.io/ja/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims)です。少なくとも、accessModes、StorageClassNameは指定する必要があります。
- 以下、本環境のPVCの定義です。容量は5Giで、StorageClass名としてnfs-storageを指定しています。
```OS: PVCの定義
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc01
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: nfs-storage
```

# ③Helmのリポジトリを登録、config.yamlを作成
- helmのリポジトリ登録はマニュアル通りです。
```OS:JupyterhubのHelmリポジトリの登録
helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/
helm repo update
```
- 結論として、config.yamlは以下になります。
    - image: name:　設定方法は[こちら](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/customizing/user-environment.html#choose-and-use-an-existing-docker-image)です。自分の作成したDockerイメージ(onoyoshio11/gpu-jupyter)を使用しています。理由は以下です。
        - tensorflowなどのGPUライブラリが含まれるDockerイメージをデプロイする必要がある。
        - jupyterlabでGPUを使う処理を実行すると"tensorflow Successful NUMA node read from SysFS had negative value (-1)"というメッセージが出て鬱陶しいので、自分でBuildしたイメージだと大丈夫では？と考えた。詳細、後述します。
        - GPUを使用するDockerイメージの詳細は[こちら](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html)の[GPU-Jupyter](https://github.com/iot-salzburg/gpu-jupyter)です。
        - GPUを使用せず、jupyter-notebookとして最小限のモノを使う場合は、singleuser.image.name=jupyter/minimal-notebook, tag=latestにすればよいです。
    - ProfileList:　GPUを割り当てるように設定しています。手順は[マニュアル](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/customizing/user-resources.html#set-user-gpu-guarantees-limits)に従っています。
    - storage:　SCとしてnfs-storageを使用して、2Giまでユーザーデータを保存できるように設定しています。方法は[こちら](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/customizing/user-storage.html)です。
    - extraEnv:で、jupyterlabの中でsudoコマンドを実行できるように設定しています。こちらが、[ヒント](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2177)になります。
    - prePuller:　Istioのサービスメッシュを使用している状態だとインストールが正常終了しない、という問題の対処を記述しています。詳細は[こちら](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2357)です。
```OS:config.yaml
singleuser:
  image:
    name: onoyoshio11/gpu-jupyter
    tag: v1.4_cuda-11.2_ubuntu-20.04
  profileList:
    - display_name: "GPU Server"
      description: "Spawns a notebook server with access to a GPU"
      kubespawner_override:
        extra_resource_limits:
          nvidia.com/gpu: "1"
  storage:
    capacity: 2Gi
    dynamic:
      storageClass: nfs-storage
  extraEnv:
    GTANT_SUDO: "yes"
    NOTEBOOK_ARGS: "--allow-root"
  uid: 0
  cmd: start-singleuser.sh
prePuller:
  annotations:
    sidecar.istio.io/inject: "false"
```


# ④helm upgradeでJupyterhubをインストール
- コマンドは以下です。
    - インストール手順のマニュアルは[こちら](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/installation.html)です。コマンド引数のversionはjupyterhubをインストールするためのhelm chartのバージョンです。jupyterhubのバージョンじゃないです。[helm repository](https://jupyterhub.github.io/helm-chart/)を確認して、現在の最新バージョンである1.2.0を指定しています。
```OS: JupyterhubのHelmインストール
helm upgrade --install jhub jupyterhub/jupyterhub   --namespace default    --version=1.2.0   --values config.yaml
```

# 実行結果
- "//"でコメントを記入しています。
- 通常のjupyterlabイメージをデプロイするのですが、imageサイズが大きすぎてfailし、再度、導入しています。手順と意味についてコメントを追記しました。

```OS: Jupyterhubのデプロイ
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.13.0-40-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

1のアップデートはすぐに適用されます。
これらの追加アップデートを確認するには次を実行してください: apt list --upgradabl                                                                                                                                                             e


20 updates could not be installed automatically. For more details,
see /var/log/unattended-upgrades/unattended-upgrades.log
Your Hardware Enablement Stack (HWE) is supported until April 2025.


Last login: Wed May  4 13:05:39 2022 from 172.31.35.113
root@k8s01:~#　// nfs ProvisionerのHelmリポジトリを登録
root@k8s01:~# helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
"nfs-subdir-external-provisioner" has been added to your repositories　
root@k8s01:~#　// Helmでnfs Provisionerをインストール
root@k8s01:~# helm install nfs-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
>     --set nfs.server=linux02 \
>     --set nfs.path=/root/share \
>     --set storageClass.defaultClass=true \
>     --set storageClass.name=nfs-storage
NAME: nfs-external-provisioner
LAST DEPLOYED: Wed May  4 13:41:58 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
root@k8s01:~# // nfs Provisionerのインストール状態を確認(deployedになっているのでOK)
root@k8s01:~# helm list
NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                   APP VERSION
nfs-external-provisioner        default         1               2022-05-04 13:41:58.276408203 +0900 JST deployed        nfs-subdir-external-provisioner-4.0.16  4.0.2
root@k8s01:~# //nfs provisonerのインストール時に指定したStorace Class名(nfs-storage)が作成されていることを確認
root@k8s01:~# kubectl get sc
NAME                    PROVISIONER                                                              RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-storage (default)   cluster.local/nfs-external-provisioner-nfs-subdir-external-provisioner   Delete          Immediate           true                   47s
root@k8s01:~# // PVC用のyamlを編集する
root@k8s01:~/jupyterhub# vi pvc01.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc01
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: nfs-storage

root@k8s01:~# // 作成したyamlによりPVCを作成する。
root@k8s01:~/jupyterhub# kubectl apply -f pvc01.yaml
persistentvolumeclaim/pvc01 created
root@k8s01:~# // PVCが作成されていることを確認
root@k8s01:~/jupyterhub# kubectl get pvc
NAME    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc01   Bound    pvc-0ec19865-b424-48aa-8438-9b441e68fccf   5Gi        RWO            nfs-storage    83s

root@k8s01:~# // PVCを作成すると自動的にPVが一つ作成されていることがわかる。
root@k8s01:~/jupyterhub# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE
pvc-0ec19865-b424-48aa-8438-9b441e68fccf   5Gi        RWO            Delete           Bound    default/pvc01   nfs-storage             80s
root@k8s01:~# // 実際のNFSのディレクトリに実体としてPVに使用されているディレクトリが作成されている。
root@k8s01:~/jupyterhub# ls /root/share
a.a  default-pvc01-pvc-0ec19865-b424-48aa-8438-9b441e68fccf
root@k8s01:~/jupyterhub# // jupyterhub導入前にk8sの全てのPodの状態を確認(ErrorやCrashLoopBackOffがないのでOK)
root@k8s01:~/jupyterhub# kubectl get pod -A
NAMESPACE          NAME                                                              READY   STATUS      RESTARTS        AGE
calico-apiserver   calico-apiserver-66489864db-dgr49                                 1/1     Running     3 (16m ago)     15h
calico-apiserver   calico-apiserver-66489864db-nt569                                 1/1     Running     3 (16m ago)     15h
calico-system      calico-kube-controllers-557cb7fd8b-bw2sr                          1/1     Running     3 (16m ago)     15h
calico-system      calico-node-lrx66                                                 1/1     Running     3 (16m ago)     15h
calico-system      calico-node-vrzm5                                                 1/1     Running     3 (16m ago)     15h
calico-system      calico-typha-5d4d7646fb-wv4vx                                     1/1     Running     5 (16m ago)     15h
default            details-v1-5498c86cf5-x7zfx                                       2/2     Running     2 (16m ago)     39m
default            nfs-external-provisioner-nfs-subdir-external-provisioner-b8fgph   2/2     Running     1 (5m31s ago)   5m43s
default            productpage-v1-65b75f6885-q2swr                                   2/2     Running     2 (16m ago)     39m
default            ratings-v1-b477cf6cf-6zz98                                        2/2     Running     2 (16m ago)     39m
default            reviews-v1-79d546878f-8qbxr                                       2/2     Running     2 (16m ago)     39m
default            reviews-v2-548c57f459-jdddv                                       2/2     Running     2 (16m ago)     39m
default            reviews-v3-6dd79655b9-pzfgh                                       2/2     Running     2 (16m ago)     39m
gpu-operator       gpu-feature-discovery-j2qkl                                       1/1     Running     2 (16m ago)     15h
gpu-operator       gpu-feature-discovery-x6n4z                                       1/1     Running     2 (16m ago)     15h
gpu-operator       gpu-operator-1651585557-node-feature-discovery-master-7dc5cnks5   1/1     Running     2 (16m ago)     15h
gpu-operator       gpu-operator-1651585557-node-feature-discovery-worker-bg44g       1/1     Running     2 (16m ago)     15h
gpu-operator       gpu-operator-1651585557-node-feature-discovery-worker-lsj2d       1/1     Running     2 (16m ago)     15h
gpu-operator       gpu-operator-7bfc5f55-rq98b                                       1/1     Running     2 (16m ago)     15h
gpu-operator       nvidia-cuda-validator-jtg26                                       0/1     Completed   0               14m
gpu-operator       nvidia-cuda-validator-rnt5l                                       0/1     Completed   0               15m
gpu-operator       nvidia-dcgm-exporter-rsxjw                                        1/1     Running     2 (16m ago)     15h
gpu-operator       nvidia-dcgm-exporter-sd6j7                                        1/1     Running     2 (16m ago)     15h
gpu-operator       nvidia-device-plugin-daemonset-6bvpg                              1/1     Running     2 (16m ago)     15h
gpu-operator       nvidia-device-plugin-daemonset-zlmhw                              1/1     Running     2 (16m ago)     15h
gpu-operator       nvidia-device-plugin-validator-kwlbp                              0/1     Completed   0               14m
gpu-operator       nvidia-device-plugin-validator-m5ff4                              0/1     Completed   0               14m
gpu-operator       nvidia-operator-validator-bcq7s                                   1/1     Running     2 (16m ago)     15h
gpu-operator       nvidia-operator-validator-q257m                                   1/1     Running     2 (16m ago)     15h
istio-system       istio-egressgateway-7569bf4864-2pl66                              1/1     Running     1 (16m ago)     40m
istio-system       istio-ingressgateway-5d6f5f9d78-w7zlm                             1/1     Running     1 (16m ago)     40m
istio-system       istiod-d56576b74-zcptd                                            1/1     Running     1 (16m ago)     40m
kube-system        coredns-64897985d-n49tt                                           1/1     Running     3 (16m ago)     15h
kube-system        coredns-64897985d-schzf                                           1/1     Running     3 (16m ago)     15h
kube-system        etcd-k8s01.dcws.dell.com                                          1/1     Running     3 (16m ago)     15h
kube-system        kube-apiserver-k8s01.dcws.dell.com                                1/1     Running     4 (16m ago)     15h
kube-system        kube-controller-manager-k8s01.dcws.dell.com                       1/1     Running     3 (16m ago)     15h
kube-system        kube-proxy-4r9k4                                                  1/1     Running     3 (16m ago)     15h
kube-system        kube-proxy-xqwbd                                                  1/1     Running     3 (16m ago)     15h
kube-system        kube-scheduler-k8s01.dcws.dell.com                                1/1     Running     3 (16m ago)     15h
metallb-system     controller-57fd9c5bb-jnd2n                                        1/1     Running     1 (16m ago)     32m
metallb-system     speaker-hwlvl                                                     1/1     Running     1 (16m ago)     32m
metallb-system     speaker-lsljc                                                     1/1     Running     1 (16m ago)     32m
tigera-operator    tigera-operator-75b96586c9-szbh5                                  1/1     Running     4 (16m ago)     15h
root@k8s01:~#  // jupyterhub用のconfigファイルを編集する。
root@k8s01:~/jupyterhub# vi config.yaml

singleuser:
  image:
    name: onoyoshio11/gpu-jupyter
    tag: latest
  profileList:
    - display_name: "GPU Server"
      description: "Spawns a notebook server with access to a GPU"
      kubespawner_override:
        extra_resource_limits:
          nvidia.com/gpu: "1"
  storage:
    capacity: 2Gi
    dynamic:
      storageClass: nfs-storage
  extraEnv:
    GTANT_SUDO: "yes"
    NOTEBOOK_ARGS: "--allow-root"
  uid: 0
  cmd: start-singleuser.sh
prePuller:
  annotations:
    sidecar.istio.io/inject: "false"
~
~
"config.yaml" [New File] 22 lines, 516 characters written

root@k8s01:~/jupyterhub# // jupyterhubのHelmリポジトリを登録する
root@k8s01:~/jupyterhub# helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/
helm repo update"jupyterhub" has been added to your repositories
root@k8s01:~/jupyterhub# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nfs-subdir-external-provisioner" chart repository
...Successfully got an update from the "jupyterhub" chart repository
...Successfully got an update from the "nvidia" chart repository
Update Complete. ?Happy Helming!?
root@k8s01:~/jupyterhub# // jupyterhubをhelmでインストールする。
root@k8s01:~/jupyterhub# helm upgrade --install jhub jupyterhub/jupyterhub   --namespace default    --version=1.2.0   --values config.yaml
Release "jhub" does not exist. Installing it now.
Error: failed pre-install: timed out waiting for the condition
root@k8s01:~/jupyterhub# // jupyterlab用のimageが大きすぎてpull処理がTimeoutして、インストールに失敗している。
root@k8s01:~/jupyterhub# // Podの状態を確認するとhook-image-pullerがInit状態になっていることがわかる。
root@k8s01:~/jupyterhub# kubectl get pod -A
NAMESPACE          NAME                                                              READY   STATUS      RESTARTS      AGE
calico-apiserver   calico-apiserver-66489864db-dgr49                                 1/1     Running     3 (24m ago)   15h
----------------------中略----------------------calico-system      calico-typha-5d4d7646fb-wv4vx                                     1/1     Running     5 (24m ago)   15h
default            details-v1-5498c86cf5-x7zfx                                       2/2     Running     2 (24m ago)   47m
default            hook-image-awaiter-lb7nb                                          1/1     Running     0             5m33s
default            hook-image-puller-mdv6q                                           0/1     Init:1/2    0             5m34s
default            hook-image-puller-ndr6m                                           0/1     Init:1/2    0             5m34s
default            nfs-external-provisioner-nfs-subdir-external-provisioner-b8fgph   2/2     Running     1 (12m ago)   13m
default            productpage-v1-65b75f6885-q2swr                                   2/2     Running     2 (24m ago)   47m
----------------------中略----------------------
tigera-operator    tigera-operator-75b96586c9-szbh5                                  1/1     Running     4 (24m ago)   15h
root@k8s01:~/jupyterhub#  // 数分後、Podの状態を確認するとhook-image-pullerがRunningになっている。Imageがダウンロードされたと考えられる。
root@k8s01:~/jupyterhub# kubectl get pod -A
NAMESPACE          NAME                                                              READY   STATUS      RESTARTS       AGE
calico-apiserver   calico-apiserver-66489864db-dgr49                                 1/1     Running     3 (178m ago)   17h
----------------------中略----------------------calico-system      calico-typha-5d4d7646fb-wv4vx                                     1/1     Running     5 (178m ago)   17h
default            details-v1-5498c86cf5-x7zfx                                       2/2     Running     2 (178m ago)   3h21m
default            hook-image-awaiter-lb7nb                                          0/1     Completed   0              160m
default            hook-image-puller-mdv6q                                           1/1     Running     0              160m
default            hook-image-puller-ndr6m                                           1/1     Running     0              160m
default            nfs-external-provisioner-nfs-subdir-external-provisioner-b8fgph   2/2     Running     1 (167m ago)   167m
----------------------中略----------------------
tigera-operator    tigera-operator-75b96586c9-szbh5                                  1/1     Running     4 (178m ago)   17h
root@k8s01:~/jupyterhub# // インストールに失敗しているので、jhubのSTATUSはfailedになっている。
root@k8s01:~/jupyterhub# helm list
NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                   APP VERSION
jhub                            default         1               2022-05-04 13:49:33.262631363 +0900 JST failed          jupyterhub-1.2.0                        1.5.0
nfs-external-provisioner        default         1               2022-05-04 13:41:58.276408203 +0900 JST deployed        nfs-subdir-external-provisioner-4.0.16  4.0.2
root@k8s01:~/jupyterhub# // hook-image関連が正常なので、docker imageがLocalに存在する状態になってる。再度、インストールする。
root@k8s01:~/jupyterhub# helm upgrade --install jhub jupyterhub/jupyterhub   --namespace default    --version=1.2.0   --values config.yaml
Release "jhub" has been upgraded. Happy Helming!
NAME: jhub
LAST DEPLOYED: Wed May  4 16:36:06 2022
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
Thank you for installing JupyterHub!

Your release is named "jhub" and installed into the namespace "default".

You can check whether the hub and proxy are ready by running:

 kubectl --namespace=default get pod

and watching for both those pods to be in status 'Running'.

You can find the public (load-balancer) IP of JupyterHub by running:

  kubectl -n default get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}'

It might take a few minutes for it to appear!

To get full information about the JupyterHub proxy service run:

  kubectl --namespace=default get svc proxy-public

If you have questions, please:

  1. Read the guide at https://z2jh.jupyter.org
  2. Ask for help or chat to us on https://discourse.jupyter.org/
  3. If you find a bug please report it at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues
root@k8s01:~/jupyterhub# // インストールは成功したように見える。Podの状態を確認すると、hub-fcbbb8696-gbcm2は初期化中、とわかる。
root@k8s01:~/jupyterhub# kubectl --namespace=default get pod
NAME                                                              READY   STATUS            RESTARTS       AGE
continuous-image-puller-fwjsr                                     1/1     Running           0              33s
continuous-image-puller-kfrb7                                     1/1     Running           0              33s
details-v1-5498c86cf5-x7zfx                                       2/2     Running           2 (3h5m ago)   3h29m
hub-fcbbb8696-gbcm2                                               0/2     PodInitializing   0              32s
nfs-external-provisioner-nfs-subdir-external-provisioner-b8fgph   2/2     Running           1 (174m ago)   174m
productpage-v1-65b75f6885-q2swr                                   2/2     Running           2 (3h5m ago)   3h29m
proxy-fbc749cd9-p955w                                             2/2     Running           0              32s
ratings-v1-b477cf6cf-6zz98                                        2/2     Running           2 (3h5m ago)   3h29m
reviews-v1-79d546878f-8qbxr                                       2/2     Running           2 (3h5m ago)   3h29m
reviews-v2-548c57f459-jdddv                                       2/2     Running           2 (3h5m ago)   3h29m
reviews-v3-6dd79655b9-pzfgh                                       2/2     Running           2 (3h5m ago)   3h29m
user-scheduler-fd4f649df-s6gr8                                    2/2     Running           0              32s
user-scheduler-fd4f649df-v2frw                                    2/2     Running           0              32s
root@k8s01:~/jupyterhub# // 数分経過後、再度、状態を確認すると、正常起動しているように見える。
root@k8s01:~/jupyterhub# kubectl --namespace=default get pod
NAME                                                              READY   STATUS    RESTARTS       AGE
continuous-image-puller-fwjsr                                     1/1     Running   0              3m57s
continuous-image-puller-kfrb7                                     1/1     Running   0              3m57s
details-v1-5498c86cf5-x7zfx                                       2/2     Running   2 (3h9m ago)   3h32m
hub-fcbbb8696-gbcm2                                               2/2     Running   0              3m56s
nfs-external-provisioner-nfs-subdir-external-provisioner-b8fgph   2/2     Running   1 (178m ago)   178m
productpage-v1-65b75f6885-q2swr                                   2/2     Running   2 (3h9m ago)   3h32m
proxy-fbc749cd9-p955w                                             2/2     Running   0              3m56s
ratings-v1-b477cf6cf-6zz98                                        2/2     Running   2 (3h9m ago)   3h32m
reviews-v1-79d546878f-8qbxr                                       2/2     Running   2 (3h9m ago)   3h32m
reviews-v2-548c57f459-jdddv                                       2/2     Running   2 (3h9m ago)   3h32m
reviews-v3-6dd79655b9-pzfgh                                       2/2     Running   2 (3h9m ago)   3h32m
user-scheduler-fd4f649df-s6gr8                                    2/2     Running   0              3m56s
user-scheduler-fd4f649df-v2frw                                    2/2     Running   0              3m56s
root@k8s01:~/jupyterhub# // proxy-publicというサービスが172.31.35.141で外部公開されている。
root@k8s01:~/jupyterhub# kubectl get svc
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
details        ClusterIP      10.108.221.79    <none>          9080/TCP       3h32m
hub            ClusterIP      10.99.171.75     <none>          8081/TCP       4m17s
kubernetes     ClusterIP      10.96.0.1        <none>          443/TCP        18h
productpage    ClusterIP      10.111.38.119    <none>          9080/TCP       3h32m
proxy-api      ClusterIP      10.100.108.213   <none>          8001/TCP       4m17s
proxy-public   LoadBalancer   10.111.179.33    172.31.35.141   80:30663/TCP   4m17s
ratings        ClusterIP      10.111.171.162   <none>          9080/TCP       3h32m
reviews        ClusterIP      10.101.247.61    <none>          9080/TCP       3h32m
root@k8s01:~/jupyterhub# //　proxy-publicはjupyterhubに使用されていることがわかる。外部clientのブラウザからjupyterhubにアクセスできる状態になっている。
root@k8s01:~/jupyterhub# kubectl describe svc proxy-public
Name:                     proxy-public
Namespace:                default
Labels:                   app=jupyterhub
                          app.kubernetes.io/managed-by=Helm
                          chart=jupyterhub-1.2.0
                          component=proxy-public
                          heritage=Helm
                          release=jhub
Annotations:              meta.helm.sh/release-name: jhub
                          meta.helm.sh/release-namespace: default
Selector:                 component=proxy,release=jhub
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.105.230.120
IPs:                      10.105.230.120
LoadBalancer Ingress:     172.31.35.141
Port:                     http  80/TCP
TargetPort:               http/TCP
NodePort:                 http  31592/TCP
Endpoints:                192.168.157.222:8000
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
root@k8s01:~/jupyterhub# // Webサイト(を外部に公開しているIPアドレス)を開くことができる。
root@k8s01:~/jupyterhub# curl http://172.31.35.141/hub/login

<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">

    <title>JupyterHub</title>
    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
----------------------省略----------------------
```
# オプション⑤Jupyterhubで自分のDockerイメージを使えるようにする
- jupyterhubで使用するdocker imageに関する説明は[こちら](https://zero-to-jupyterhub.readthedocs.io/en/latest/jupyterhub/customizing/user-environment.html?highlight=docker%20stacks#choose-and-use-an-existing-docker-image)です。Jupyter docker stack以外のdocker imageは、PVの構成やネットワーク設定がjupyterhubに対応しておらず、適切に動作しない可能性があります(例 Nvidia GPU Operaterの動作確認用のtf-notebook)。
- 自分の環境でimageをビルドすれば、前述の"tensorflow Successful NUMA node read from SysFS had negative value (-1)"のメッセージは消えるのではないか？、などと考えたのが、理由です。
- 結局、自分の環境でdocker imageをbuildしても、件のエラーは修正されなかったので、実際の作業では、[こちら](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html)で紹介されている作成済みのgpu-jupyterというdocker imageを使用しました。
- 以下、[こちら](https://qiita.com/blueskyarea/items/7ddd5441d9212c5a6570)を参考に、dockerhubからlocalにpullしたgpu-jupyterのImageを、自分のdockerhubのリポジトリにpushし、上記のjupyterhub用のconfig.yamlで指定できるようにした作業です。
    - gpu-jypterの[github](https://github.com/iot-salzburg/gpu-jupyter)にdockerhubの[リンク](https://hub.docker.com/r/cschranz/gpu-jupyter)があり、pullする際の正確なimage名とtagを確認できます。

```OS: docker操作
root@k8s01:~/jupyterhub# // localのdockerでgpu-jupyterを起動します。
root@k8s01:~/jupyterhub# docker run --gpus all -d -it -p 8848:8888 -v $(pwd)/data:/home/jovyan/work -e GRANT_SUDO=yes -e JUPYTER_ENABLE_LAB=yes --user root cschranz/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04
Unable to find image 'cschranz/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04' locally
v1.4_cuda-11.2_ubuntu-20.04: Pulling from cschranz/gpu-jupyter
7b1a6ab2e44d: Pull complete
----------------------中略----------------------
b1ac57ce5b2f: Pull complete
Digest: sha256:6b7b174ddb8e93d3f71deb72d5482e897e5855c55dfc5c0c95c44eafd0a3888f
Status: Downloaded newer image for cschranz/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04
73290bccd77382c4d7ff0935b95168f0cb7fcc503a052049dea637a8ec11a164
root@k8s01:~/jupyterhub# // 自分のdockerhubのリポジトリにpushするためにdockerにログインします
root@k8s01:~/jupyterhub# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: onoyoshio11
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
root@k8s01:~/jupyterhub# // gpu-jupyterのコンテナの状態を確認します。Upしていることがわかります。
root@k8s01:~/jupyterhub# docker ps
CONTAINER ID   IMAGE                                              COMMAND                  CREATED        STATUS        PORTS                                       NAMES
73290bccd773   cschranz/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04   "tini -g -- start-no…"   15 hours ago   Up 15 hours   0.0.0.0:8848->8888/tcp, :::8848->8888/tcp   relaxed_khorana
----------------------中略----------------------
df4734840710   k8s.gcr.io/pause:3.6                               "/pause"                 15 hours ago   Up 15 hours                                               k8s_POD_etcd-k8s01.dcws.dell.com_kube-system_33185968f1e701e134cbb0b42e8a4887_4
a0011e54f4b3   k8s.gcr.io/pause:3.6                               "/pause"                 15 hours ago   Up 15 hours                                               k8s_POD_kube-scheduler-k8s01.dcws.dell.com_kube-system_85cd7c1fd95b05df19a21cb1ef7b6520_4
root@k8s01:~/jupyterhub# // 自分のリポジトリにpushできるように適当なコンテナ名(abc)を変更します。
root@k8s01:~/jupyterhub# docker run -d --name abc cschranz/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04
e7cd90cb4df6c3c7caf6cd3480c81b2536798a6bf74455bd1a396cd631929ebe
root@k8s01:~/jupyterhub# // 自分のリポジトリ名と同じになるように設定します。
root@k8s01:~/jupyterhub# docker commit abc onoyoshio11/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04
sha256:fc24ed698d6f6c6e25111118e5d83c14d1759730567a5da60bc6ddd47be81775
root@k8s01:~/jupyterhub# // docker pushにより自分のdockerhubのリポジトリにdocker imageをuploadします。
root@k8s01:~/jupyterhub# docker push onoyoshio11/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04
The push refers to repository [docker.io/onoyoshio11/gpu-jupyter]
ae0f49433d3a: Pushed
353bf107a535: Mounted from cschranz/gpu-jupyter
----------------------中略----------------------
9f54eef41275: Mounted from cschranz/gpu-jupyter
v1.4_cuda-11.2_ubuntu-20.04: digest: sha256:3c1c1879bfe7a45b28acccdef8d9141f5a4c543de21ed5b95c62ffdef2143839 size: 10636
```

# その他
- JupyterhubでGPUを使う処理を実行すると下記のようなメッセージが出力されます。
    - この環境でgit cloneからのDocker buildを行いましたが変化なし
    - 以下のURLでも放置状態。特に、Tensorflow.comですら放置状態。こういうものかな…というか、どうしようもないと考えて放置することに決定。
        - tensorflowの公式サイトのガイド
https://www.tensorflow.org/hub/tutorials/semantic_approximate_nearest_neighbors?hl=ja
        - kaggleの公式サイトの、このメッセージに関するQ&A
https://www.kaggle.com/questions-and-answers/272476

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104507/e781b0ff-c0b2-855e-e3a5-ef12caa96c31.png)

- NFS provisionerのnfs-subdir-external-provisionerの説明
https://hawksnowlog.blogspot.com/2021/03/how-to-use-nfs-as-storageclass-server-on-kubernetes.html

以上、