PodmanでJupyterHub／JupyterLabを試す

# はじめに

今年の海の日三連休は、、、諸事情により遠出はできず、夜更かしする時間ができたので、小ネタをば。

仕事の都合でJupyter on Podmanを試したくなったので、その過程を紹介してゆきます。

が、結論を言うと、本稿で紹介する方式はJupyterHub本家さんからサポートされていません！
DockerSpawnerでPodmanを使えなくはないよ。。。というようなニュアンスでしょう。

では、さっそく参ります。

## 本稿で紹介すること

- CentOS Stream 8のインストール
- Podmanのインストール
- JupyterHubの起動


## 参考記事

以下の記事、「Using Podman[^1]」の節を手元PCで試します。

https://jupyterhub-dockerspawner.readthedocs.io/en/latest/spawner-types.html

原文ままを記載すると、、、再掲ですが、JupyterHub本家さんからサポートされていません！

> Podman is an alternative to Docker for running containers, and supports running containers without root access. <font color="#ff0000">It is not officially supported by JupyterHub,</font> but it can be used with DockerSpawner by running a podman service:
> 
> ```
> podman system service --time=0 &
> export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
> # Run jupyterhub as normal
> jupyterhub --config=jupyterhub_config.py
> ```
> There are several other ways of running the Podman service.
> 
> Not all Docker features are supported by Podman.


# CentOS Stream 8のインストール

過去の投稿記事で準備した手元PCのLinuxを使います。

https://qiita.com/Blaster36/items/a0a119f44ca727ee0ddf

もう少し詳しく言うと、

- ホストOS：CentOS 7
- ゲストOS：CentOS Stream 8

という構成で、KVMで仮想化したCentOS Streamを使ってJupyterHub on Podmanを試します。

ホストOS上で、CentOS公式[^2][^3]からISOファイルをDownloadします。
本稿執筆時点だと、<b><font color="#ff0000">ファイル名はCentOS-Stream-8-20230710.0-x86_64-dvd1.iso、サイズは約11GB</font></b>でした。
![Screenshot 2023-07-19 at 01-26-02 Index of _pub_Linux_CentOS_8-stream_isos_x86_64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/78037856-623a-e82d-254e-7bee4200d3f2.png)

あとは、インストールウィザードに沿ってインストールするだけです。
![KVM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/46692edf-1a6c-9ab6-a0e7-dbd2967c4ade.png)

:::note info
以下、筆者のKVM（仮想マシンマネージャー）における設定値です。
- CPU：4Core
- MEM：16GB
- DISK：100GB
- NW：NAT（192.168.130.0/24）

以下、筆者のCentOSのインストールウィザードにおける設定値です。
- 言語：日本語
- タイムゾーン：アジア／東京
- ソフトウェア選択：最小限のインストール
- ホスト名：centos8
- NW：NAT（IPAddr 192.168.130.100, Mask 255.255.255.0, Gateway 192.168.130.1, DNS 192.168.130.1）
:::

OSインストールを終えると、以下の状態です。

```shell
[root@centos8 ~]# cat /etc/redhat-release
CentOS Stream release 8
[root@censot8 ~]# dnf info centos-release-stream
利用可能なパッケージ
名前         : centos-release-stream
バージョン   : 8.1
リリース     : 1.1911.0.7.el8
Arch         : x86_64
サイズ       : 11 k
ソース       : centos-release-stream-8.1-1.1911.0.7.el8.src.rpm
リポジトリー : extras
概要         : CentOS-Stream release file
ライセンス   : GPLv2
説明         : CentOS-Stream release files
```


# Podmanのインストール

ここからは、ゲストOS上で進めます。
以下のコマンドを実行するだけです。（他には、、、Firewallを無効化したり、VIMやZIP／UNZIPをインストールしています。）

```shell
[root@centos8 ~]# dnf -y install podman
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                                        アーキテクチャー                            バージョン                                                               リポジトリー                                   サイズ
=============================================================================================================================================================================================================================================
インストール:
 podman                                                            x86_64                                      3:4.5.1-5.module_el8+461+0c6b54ea                                        appstream                                       15 M
依存関係のインストール:
 checkpolicy                                                       x86_64                                      2.9-1.el8                                                                baseos                                         348 k
 conmon                                                            x86_64                                      3:2.1.7-1.module_el8+454+d7ef4b8d                                        appstream                                       57 k
 container-selinux                                                 noarch                                      2:2.218.0-1.module_el8+454+d7ef4b8d                                      appstream                                       69 k
 containernetworking-plugins                                       x86_64                                      1:1.3.0-2.module_el8+461+0c6b54ea                                        appstream                                       21 M
 containers-common                                                 x86_64                                      2:1-51.module_el8+454+d7ef4b8d                                           appstream                                      136 k
 criu                                                              x86_64                                      3.18-4.module_el8+454+d7ef4b8d                                           appstream                                      564 k
 fuse-common                                                       x86_64                                      3.3.0-17.el8                                                             baseos                                          22 k
 fuse-overlayfs                                                    x86_64                                      1.12-1.module_el8+454+d7ef4b8d                                           appstream                                       70 k
 fuse3                                                             x86_64                                      3.3.0-17.el8                                                             baseos                                          54 k
 fuse3-libs                                                        x86_64                                      3.3.0-17.el8                                                             baseos                                          95 k
 libnet                                                            x86_64                                      1.1.6-15.el8                                                             appstream                                       67 k
 libslirp                                                          x86_64                                      4.4.0-1.module_el8+454+d7ef4b8d                                          appstream                                       70 k
 podman-catatonit                                                  x86_64                                      3:4.5.1-5.module_el8+461+0c6b54ea                                        appstream                                      360 k
 policycoreutils-python-utils                                      noarch                                      2.9-24.el8                                                               baseos                                         260 k
 protobuf-c                                                        x86_64                                      1.3.0-8.el8                                                              appstream                                       37 k
 python3-audit                                                     x86_64                                      3.0.7-5.el8                                                              baseos                                          87 k
 python3-libsemanage                                               x86_64                                      2.9-9.el8                                                                baseos                                         128 k
 python3-policycoreutils                                           noarch                                      2.9-24.el8                                                               baseos                                         2.3 M
 python3-setools                                                   x86_64                                      4.3.0-3.el8                                                              baseos                                         624 k
 runc                                                              x86_64                                      1:1.1.7-2.module_el8+463+b24391b1                                        appstream                                      3.1 M
 shadow-utils-subid                                                x86_64                                      2:4.6-18.el8                                                             baseos                                         113 k
 slirp4netns                                                       x86_64                                      1.2.0-3.module_el8+454+d7ef4b8d                                          appstream                                       54 k
弱い依存関係のインストール:
 tar                                                               x86_64                                      2:1.30-9.el8                                                             baseos                                         847 k
モジュールストリームの有効化中:
 container-tools                                                                                               rhel8

トランザクションの概要
=============================================================================================================================================================================================================================================
インストール  24 パッケージ

ダウンロードサイズの合計: 46 M
インストール後のサイズ: 139 M
：
```

:::note warn
企業ネットワークの場合は、Proxy設定なり証明書導入なり、適宜実施しましょう。
:::

Podmanインストールを終えると、以下の状態です。

```shell
[root@centos8 ~]# which podman
/usr/bin/podman
[root@centos8 ~]# podman --version
podman version 4.5.1
```


# JupyterHubの起動

まずは、JupyterHubをゲストOSにインストールします。
JupyterHub（ユーザ単位でJupyterLabコンテナを起動する側）は単なるPythonライブラリです。その他の必要なPythonライブラリ群とまとめてインストールします。

```shell
dnf -y install python38 python38-pip
python3 -m pip install pip -U
python3 -m pip install jupyterhub==3.1.1 IPython jupyter_client notebook jupyterlab docker dockerspawner escapism
```

次に、プロキシ（configurable-http-proxy）をゲストOSにインストールします。
WebブラウザとJupyterHubの間にプロキシが入って、WebブラウザからのHTTPリクエストを受け渡しています。

```shell
dnf -y install nodejs npm
npm install -g configurable-http-proxy
```

最後に、JupyterLab（JupyterHubから起動される側）のコンテナイメージをいくつかDownloadします。

```shell
podman pull docker.io/jupyter/minimal-notebook:python-3.8.8
podman pull quay.io/jupyteronopenshift/s2i-minimal-notebook-py36:2.5.1
podman pull quay.io/thoth-station/s2i-minimal-py38-notebook:v1.0.0
```

よく使うPythonライブラリをいろいろと詰め込んでもよいのですが、取り急ぎはJupyterHubとのつなぎ役になるPythonライブラリだけ入れ直します。
冒頭に示した参考記事に加えて、JupyterHubのReleaseNote[^4]およびDockerSpawnerのReleaseNote[^5]から、JupyterHubを１つ前のMajorVer.で最新の3.1.1を採用ました。
そのため、JupyterLabにも連動して<b><font color="#ff0000">「jupyterhub==3.1.1」</font></b>を導入します。

```shell:Dockerfile.lab
FROM jupyter/minimal-notebook:python-3.8.8
RUN python3 -m pip install jupyterhub==3.1.1
```

Dockerfileの準備ができたら、以下のコマンドでコンテナイメージをBuildします。

```shell
podman image build -t jupyter/minimal-notebook:python-3.8.8-podman -f Dockerfile.lab . --no-cache

```

あとは、お馴染みの「jupyterhub_config.py」を定義して、JupyterHubの細かな挙動を設定します。
振返りを兼ねて、いくつかポイントを記載します。
- 認証はダミー、どんなID・PWを入力してもログイン可能
- 管理ユーザとして「admin」を指定（ログイン後に管理メニューを利用可能）
- コンテナイメージの選択肢は４つ、UbuntuベースとRHEL（CentOS、RHCOS）ベースのイメージを起動可能
- 永続ボリュームの割当て、各コンテナイメージ（JupyterLab）のHomeディレクトリ配下にマウント

```python:jupyterhub_config.py
from jupyter_client.localinterfaces import public_ips
ip = public_ips()[0]

c.JupyterHub.authenticator_class = 'jupyterhub.auth.DummyAuthenticator'
c.JupyterHub.hub_ip = ip
c.JupyterHub.ip = ip
c.Authenticator.admin_users = {'admin'}

c.ServerApp.iopub_data_rate_limit = 100000000.0
c.ServerApp.rate_limit_window = 1.0

from dockerspawner import DockerSpawner

class DemoFormSpawner(DockerSpawner):
  def _options_form_default(self):
    default_stack = 'thoth-station/s2i-minimal-notebook(Python 3.8)'
    return """
    <label for="stack">Select your desired stack</label>
    <select name="stack" size="1">
      <option value="jupyter/minimal-notebook:python-3.8.8">jupyter/minimal-notebook(Python 3.8)</option>
      <option value="jupyter/minimal-notebook:python-3.8.8-podman">jupyter/minimal-notebook(Python 3.8 w/jupyterhub==3.1.1)</option>
      <option value="quay.io/jupyteronopenshift/s2i-minimal-notebook-py36:2.5.1">jupyteronopenshift/s2i-minimal-notebook(Python 3.6)</option>
      <option value="quay.io/thoth-station/s2i-minimal-py38-notebook:v1.0.0">thoth-station/s2i-minimal-notebook(Python 3.8)</option>
    </select>
    """.format(stack=default_stack)

  def options_from_form(self, formdata):
    options = {}
    options['stack'] = formdata['stack']
    container_image = ''.join(formdata['stack'])
    print('SPAWN: ' + container_image + 'IMAGE')
    self.image = container_image
    self.log.info( "<<options_from_form>> %s", self.image)
    return options

  def start(self):
    self.log.info( "<<start>> %s", self.image)
    notebook_dir = '/home/jovyan'
    if self.image == 'quay.io/jupyteronopenshift/s2i-minimal-notebook-py36:2.5.1':
       notebook_dir = '/opt/app-root/src'
    elif self.image == 'quay.io/thoth-station/s2i-minimal-py38-notebook:v1.0.0':
       notebook_dir = '/opt/app-root/src'
    else:
       notebook_dir = '/home/jovyan'
    self.notebook_dir = notebook_dir
    self.volumes = {'jupyterhub-user-{username}':notebook_dir}
    self.volumes['jupyterhub-user-{username}'] = notebook_dir + '/work'
    self.log.info( "<<start self.volumes>> %s", self.volumes)
    return super().start()

c.JupyterHub.spawner_class = DemoFormSpawner
c.DemoFormSpawner.remove = True
c.DemoFormSpawner.default_url = '/lab'
c.DemoFormSpawner.start_timeout = 300
c.DemoFormSpawner.http_timeout = 120
c.DemoFormSpawner.network_name = 'jupyterhub'
c.DemoFormSpawner.debug = True
```

「jupyterhub_config.py」の準備ができたら、以下のコマンドでJupyterHubをOSプロセスとして起動します。
- プロキシはJupyterHubを起動すると、その裏側で起動されます。
- JupyterLabはユーザがログイン＆コンテナイメージを選択すると、JupyterHubから起動されます。

```shell
jupyterhub --config=jupyterhub_config.py
```


# 動作確認

Webブラウザで接続してみると、、、
![Screenshot 2023-07-18 at 23-42-05 JupyterHub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/58cc71b9-9b82-15bd-7ab7-1264e5a06ecf.png)

ログインはOK。コンテナイメージを選択して「Start」ボタンを押下してみると、、、
![Screenshot 2023-07-18 at 23-36-12 JupyterHub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/72d3193f-21a3-50c5-a92c-310047dc21b1.png)

JupyterLabコンテナ起動はNG。
![Screenshot 2023-07-18 at 23-36-41 JupyterHub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/53b13957-4a2b-14ac-2d86-5615b3ed5c24.png)


# Podmanのインストール　その②

「Using Podman[^1]」の節で案内する設定を施してゆきます。

> ```
> podman system service --time=0 &
> export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
> # Run jupyterhub as normal
> jupyterhub --config=jupyterhub_config.py
> ```

Podmanをサービングして、、、
（設定方法はいくつかあるため[^6]、好きな方法でOKです。）

```shell
[root@centos8 ~]# systemctl status podman.socket
● podman.socket - Podman API Socket
   Loaded: loaded (/usr/lib/systemd/system/podman.socket; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:podman-system-service(1)
   Listen: /run/podman/podman.sock (Stream)
[root@centos8 ~]# systemctl --now enable podman.socket
Created symlink /etc/systemd/system/sockets.target.wants/podman.socket → /usr/lib/systemd/system/podman.socket.
[root@centos8 ~]# systemctl status podman.socket
● podman.socket - Podman API Socket
   Loaded: loaded (/usr/lib/systemd/system/podman.socket; enabled; vendor preset: disabled)
   Active: active (listening) since Tue 2023-07-16 14:34:36 JST; 1s ago
     Docs: man:podman-system-service(1)
   Listen: /run/podman/podman.sock (Stream)
    Tasks: 0 (limit: 100620)
   Memory: 0B
   CGroup: /system.slice/podman.socket

 7月 16 14:34:36 centos8 systemd[1]: Listening on Podman API Socket.
```

環境変数を定義して、、、
（直前のコマンド実行結果、Listenの項目値です。）

```shell
export DOCKER_HOST=unix://run/podman/podman.sock
```

改めて、以下のコマンドでJupyterHubをOSプロセスとして起動します。

```shell:Dockerfile.hub
jupyterhub --config=jupyterhub_config.py
```


# 動作確認　その②

コンテナイメージを選択して「Start」ボタンを押下してみると、、、
![Screenshot 2023-07-19 at 01-03-55 JupyterHub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/c3e59ac8-c826-74d0-d245-4a2711a75af4.png)

Errorで同じ画面に留まることなく、JupyterLabコンテナを起動して、、、
![Screenshot 2023-07-19 at 00-54-33 JupyterHub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/0f8b768f-126a-3b95-8965-0098ee7e5828.png)

JupyterLabコンテナ起動が無事に完了！おめでとう！！
![Screenshot 2023-07-19 at 00-54-44 JupyterLab.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/51096ad0-af85-2ba8-cd80-7f3484c31924.png)

# Notebook（Pythonコード）

GitHubで公開しています。

https://github.com/Blaster36/Script/tree/main/jupyterhubonpodman

# まとめ

本稿ではCentOS Stream 8のインストールおよびPodmanのインストールを進めました。
そして、JupyterHub on Podmanに挑戦し、何とか動く状態を確認できました。
が、JupyterHubさん本家からサポートされない方式ではあるので、注意が必要という結論でした。
JupyterHubさん本家のDoc[^7][^8]にて、公式サポートの吉報をみんなで待ちましょう！

あと、細々とした設定方式（podman.sockのアクセス権やら、JupyterHub自体のコンテナ起動やら）が残ってそうですが、それらはまた機会があれば。

[^1]: Using Podman | https://jupyterhub-dockerspawner.readthedocs.io/en/latest/spawner-types.html#using-podman
[^2]: CentOS Download | https://www.centos.org/download/
[^3]: CentOS Mirrors List | http://isoredirect.centos.org/centos/8-stream/isos/x86_64/
[^4]: Changelog - JupyterHub  documentation | https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html
[^5]: Changes in DockerSpawner | https://jupyterhub-dockerspawner.readthedocs.io/en/latest/changelog.html
[^6]: podman-system-service - Podman  documentation | https://docs.podman.io/en/latest/markdown/podman-system-service.1.html
[^7]: JupyterHub - JupyterHub  documentation | https://jupyterhub.readthedocs.io/en/stable/
[^8]: GitHub - jupyterhub/jupyterhub: Multi-user server for Jupyter notebooks | https://github.com/jupyterhub/jupyterhub