MicroK8sでKubeflow 1.0：「導入」と「Jupyterカスタマイズ」

# モチベ

『クラウドの力を、自分のマシンでも感じたい。』　完。

先日正式リリースとなった Kubeflow 1.0 が、MicroK8sのアドオンになりました。[MicroK8s Release Note](https://microk8s.io/docs/release-notes)

k8sもKubeflowも初学者ですが、以下について残します。

- インストール
- Kubeflowから起動できるJupyterサーバーに、カスタムコンテナイメージを使う  
今回は、Jupyter Labで立ち上げます。

# 環境・バージョン

- Ubuntu 18.04.4 LTS (Bionic Beaver)
  - Kernel 5.3.0-45-generic
- MicroK8s v1.18.0

以下は、使ってみて感じた、あくまで主観です。

- Memory
16GB以上は欲しい。

- Storage
/var をごりごり使う。128GB以上の空きは欲しい。

# インストール

## MicroK8s

[公式ドキュメント](https://microk8s.io/docs/)を参考に、MicroK8sをインストール。

```bash
sudo snap install microk8s --classic --channel=1.18/stable
sudo usermod -a -G microk8s <username>
```

## (Optional) Insecure Registry の設定

**カスタムコンテナイメージの格納先に DockerHub など、証明書を気にしなくてよいレジストリを選択する場合、ここは不要。**

後半では、オンプレで使える[Docker Registry](https://docs.docker.com/registry)を使用する。証明書ガン無視なので、`insecure-registry`の設定を入れます。

- `/var/snap/microk8s/current/args/containerd-template.toml`のpluginセッションに、以下を参考にレジストリのIPアドレス:ポート情報を追加。

```text:/var/snap/microk8s/current/args/containerd-template.toml（抜粋）
[plugins]
  (略)
  [plugins.cri]
    (略)
    [plugins.cri.registry]
      [plugins.cri.registry.mirrors]
        #
        # このセッション内に以下２行追加。この場合、192.168.0.10がオンプレのレジストリ。
        #
        [plugins.cri.registry.mirrors."192.168.0.10:5000"]
          endpoint = ["http://192.168.0.10:5000"]
(略)
```

- MicroK8sの再起動

```bash
microk8s stop
microk8s start
```


## Kubeflow

MicroK8s v1.18.0のKubeflowアドオンでは、MetalLBが利用されています。
この恩恵で、AmbassadorのExternal IPにMetalLBのIPアドレスがセットされます。

ただし、MetalLBのIPアドレスのレンジが`10.64.140.43-10.64.140.49`で [enable.kubeflow.sh](https://github.com/ubuntu/microk8s/blob/v1.18/microk8s-resources/actions/enable.kubeflow.sh#L124) にハードコーディングされています。（v1.18.0では？）

ここでは、MetalLBアドオンを先にインストールし、自分の環境に合わせたレンジを先に設定します。

```bash
# GPUを利用する場合、必要。
microk8s enable gpu

# MetalLBのインストール。バインドするIPアドレスのレンジを指定。
# 　→MicroK8s構築マシンのIPアドレスでバインド。
microk8s enable metallb:192.168.0.15-192.168.0.15

# （任意）Kubeflowアクセス時のFQDN指定。
# 　　　　指定しなくても、<External IP>.xip.io が自動設定される。一番ラク。
# 　　　　指定ならば、下記注意参照。
export KUBEFLOW_HOSTNAME=miya-kubeflow.localdev

# （任意）ダッシュボードログイン時のパスワードの指定。
export KUBEFLOW_AUTH_PASSWORD=p@ssw0rd

# kubeflowのインストールは、かなり時間がかかるので気長に待つ。ひたすら待つ。
microk8s enable kubeflow
```

#### 注意

`KUBEFLOW_HOSTNAME`に、例のように内部で使うドメインを指定する場合、CoreDNSのForwarderに内部DNSを指定する必要がある。（自分はこれをしないと動かなかった。）

以下やりかた。

- ConfigMapを編集。

```bash
microk8s kubectl -n kube-system edit configmap coredns
```

`forward`に、Google Public DNS（8.8.8.8，8.8.4.4）が二箇所確認できるはずなので、内部DNSを追加ないし、置き換える。

- Podの再構成。

待っていれば自動で反映されるはず。が、さっとやりたいので。

```bash
# CoreDNSのPod名検索
microk8s kubectl get pods -o=name -n kube-system | grep coredns

# Podを消して、Deployを強制。
microk8s kubectl delete -n kube-system <Pod名>
```


### 確認

- 正常にインストールが終了すると、こんな感じの文章が出力される。

```text
Congratulations, Kubeflow is now available.

The dashboard is available at http://<KUBEFLOW_HOSTNAMEで設定したやつ>/
    Username: admin
    Password: <KUBEFLOW_AUTH_PASSWORD>で設定したやつ

To see these values again, run:
    microk8s juju config dex-auth static-username
    microk8s juju config dex-auth static-password

To tear down Kubeflow and associated infrastructure, run:
   microk8s disable kubeflow
```

- コンテナが正常に上がっているか確認。

```bash
microk8s kubectl -n kubeflow get all

# kubectlが異常に遅いとき　→下の「kubectlがやたらと重い」参照

# Ambassadorに注目すると、External IPがセットされているはず。
# NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)             AGE
# service/ambassador      LoadBalancer   10.152.183.147   192.168.0.15   80:30617/TCP        14h
```

- ブラウザに設定したホスト名でアクセスし、ダッシュボードを表示＆ログイン。
<img width="1218" alt="login.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450728/25160c02-c660-0f78-afd9-0f2017d1e908.png">
<img width="1218" alt="dashboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450728/3f887a9b-44d6-4099-d6f0-d03d59b905f1.png">
インストール完了。

#### kubectlがやたらと重い

`${HOME}/.kube` の所有者がrootになっている場合、sudoなしでkubectlを利用すると遅くなることがあるみたい。所有者をユーザにする。


# 自作イメージでJupyter NoteBook立ち上げ

## tl; dr

[公式ドキュメント - Create a Custom Jupyter Image](https://www.kubeflow.org/docs/notebooks/custom-notebook) の起動コマンド使えば、自作コンテナイメージ作れる。

## 以降でやること

- カスタムしたコンテナイメージの作成。
- レジストリへ登録。
- 登録したイメージでの、Notebook Sever立ち上げ。

### DockerFile作成

以下のDockerFileを利用。

- Point
  - 永続Volumeは、/home/jovyan にマウントされる
  - CMDの引数部は、Create a Custom Jupyter Imageのドキュメントをそのまま使えばいい。

```dockerfile:JupyterLabのカスタムイメージ
FROM tensorflow/tensorflow:2.1.0-gpu-py3-jupyter

MAINTAINER miya_sh

WORKDIR /home/jovyan
RUN pip3 install --no-cache-dir jupyterlab && mv /tf /home/jovyan/

RUN useradd -m -s /bin/bash jovyan
USER jovyan

ENV NB_PREFIX /
CMD ["bash", "-c", "jupyter lab --notebook-dir=/home/jovyan --ip=0.0.0.0 --no-browser --allow-root --port=8888 --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.allow_origin='*' --NotebookApp.base_url=${NB_PREFIX}"]
```

### レジストリへPush

DockerHubなど好きなレジストリにイメージを登録。

ここでは、オンプレで使える[Docker Registry](https://docs.docker.com/registry)を使う。

```bash
# 別マシン（ここでは 192.168.0.10）にレジストリを作る
docker run --rm -d -p 5000:5000 -v ${PWD}/images:/var/lib/registry registry

# 先ほどのDockerFileをビルド＆プッシュ
docker build -t 192.168.0.10:5000/kubeflow-jupyter/tensorflow:2.1.0-gpu .
docker push 192.168.0.10:5000/kubeflow-jupyter/tensorflow:2.1.0-gpu
```

### Notebook Sever立ち上げ

[公式ドキュメント](https://www.kubeflow.org/docs/notebooks/setup)にあるように、ナビゲーションからサーバ作成を行う。

- 作ったイメージを指定する。
<img width="1218" alt="custom-image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450728/89abc725-6661-ec4b-c644-35bb56fab365.png">

- GPUを使う場合、ベンダー（`nvidia.com/gpu`，`amd.com/gpu`）および個数をExtra Resourcesに指定する。
例）`{"nvidia.com/gpu": "1"}`
<img width="1218" alt="gpu.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450728/2a1436ab-2c6c-51e8-4793-18e75bda2b03.png">

- LAUNCHで作成。初回はイメージPullのため時間がかかる。
<img width="1218" alt="dash.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450728/3d53bd8b-2aea-b2f7-c244-737112aee77b.png">

- CONNECTで無事Jupyter Labとつながれば、ほっと一息。
念の為、TensorFlowのバージョンとか、GPU見えているか確認。
<img width="1218" alt="jupyterlab.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450728/7dc79dd4-1ea4-8b7f-9139-6fcdede53922.png">
学習まで試す用に→[TensorFlow 2.0 公式チュートリアル](https://www.tensorflow.org/tutorials/quickstart/beginner?hl=ja)

---

MicroK8sのマルチノードで、散らばってるGPUが活用できないか、シームレスな学習環境を作れるか試してみたい・・・