Slurm Installその後5. Slurm with OCI Container Runtime (Rootless Docker)
はじめに
今回はSlurmでのコンテナ起動設定をやってみたいと思います。
コンテナといえば思い浮かぶのはDockerが一般的ですが、root権限でdockerdを常駐させる仕組みです。
root権限での操作は資源共有を行うHPCジョブスケジューラ環境にとっては深刻なセキュリティリスクで、そのままSlurmでは使えません。
ただし、OCI準拠コンテナランタイムを非特権で動かすのはSlurmでネイティブサポートされているとの事なので、今回はsbatch(srun,salloc)に実装された「--containerオプション」(21.08〜)を使って、コンテナ実行ができるか?
試してみたいと思います。
ちなみに...
Slurmを含めHPC環境でのコンテナワークロード利用は「Singularity」がデファクトスタンダードな世の中な気がしています。が、少し前に開催されたSLUG'22でpodmanなども紹介されてましたし、Slurmでのコンテナワークロード利用環境は今後充実しそうなので個人的には注目したい所です。
(scrunの実装など、色々楽しみですね!!)
https://slurm.schedmd.com/SLUG22/OCI_Containers_with_scrun.pdf
目次
Container with Slurm
今回の作業は主に、以下の公式ドキュメントを確認しながらやりました。
Slurm Workload Manager - Containers Guide
設定は大まかに分けると以下となります。
- Container (Rootless)のインストール
- OCI Container bundle
- oci.conf
構成
今回も、ベースは以前までと同じ環境を使っていきます。
- Slurm 22.05.04
- Master + Node1~4
- Master,Node1~4は全てVirtual Box上に作成したRocky linux 8.6
設定作業
以降で環境を準備していきますが、non-rootのコンテナ起動ユーザは「john」を想定します。
Docker (Rootless)
対象: Master 、 Node1~4
全ノードにDockerをインストールして、Rootless起動のセットアップを行います。
Dockerをインストールしてはいますが、役割として重要なポイントは以下になるかと思います。
- Container Runtimeであるrunc インストール
- non-rootでのOCIコンテナ(runc)起動のセットアップ
- OCI Container bundleの作成
Install Docker
Dockerをdnfでインストールします。
# dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # dnf -y install docker-ce docker-ce-cli containerd
↓インストール時にdocker-ce-rootless-extrasがパッケージに含まれている事を確認しておきます。
========================================================================================================== Package Arch Version Repository Size ========================================================================================================== docker-ce-rootless-extras x86_64 20.10.18-3.el8 docker-ce-stable 4.6 M
Setting Docker Rootless
Rootlessの設定を適用していきます。 基本は以下の手順を参考にします。
Run the Docker daemon as a non-root user (Rootless mode) | Docker Documentation
# dnf install -y fuse-overlayfs # dnf install -y iptables # systemctl disable --now docker.service docker.socket
non-rootでの起動ユーザであるjohnの.bashrcに以下のように追記します。
[john@master ~]$ echo "loginctl enable-linger \$(whoami)" >> ~/.bashrc [john@master ~]$ echo "export XDG_RUNTIME_DIR=/run/user/\$(id -u)" >> ~/.bashrc [john@master ~]$ source ~/.bashrc
dockerd-rootless-setuptool.shを実行します。
[john@master ~]$ dockerd-rootless-setuptool.sh install
最後のstdoutに、以下のように表示されます。
・・・ ・・・ export PATH=/usr/bin:$PATH export DOCKER_HOST=unix:///run/user/1002/docker.sock
→こちらの設定も.bashrcに追記し、シェルに反映させておきます。
[john@master ~]$ echo "loginctl enable-linger \$(whoami)" >> ~/.bashrc [john@master ~]$ echo "export XDG_RUNTIME_DIR=/run/user/\$(id -u)" >> ~/.bashrc [john@master ~]$ source ~/.bashrc
これで、ユーザ権限でdockerdが起動できるようになります。
[john@master ~]$ systemctl --user daemon-reload [john@master ~]$ systemctl --user start docker [john@master ~]$ systemctl --user enable docker
docker infoを確認しておきます。
[john@master ~]$ docker info
以下を確認しておきます。
- Default Runtime: runc
- Security Options: 数行の中に"rootless"が記載
- Docker Root Dir: /home/john/.local/share/docker
dockerd-rootless-setuptool.sh check を実行して、rootless設定を確認します。
[john@master ~]$ dockerd-rootless-setuptool.sh check --force [INFO] Requirements are satisfied
runcが使えることも念の為、確認しておきます。
[john@master ~]$ runc --version runc version 1.1.4 commit: v1.1.4-0-g5fd4c4d spec: 1.0.2-dev go: go1.17.13 libseccomp: 2.5.2
以上です。
ここまでが対象: Master 、 Node1~4での共通部分になります。
Create OCI Container bundle
次にContainer bundleをmasterノード上で作成していきます。(masterが必須という訳ではありません)
前章で、Docker(Rootless)が問題なく設定できていれば、Container bundle作成の為の環境も出来ていることになります。
Container bundleはNFS共有先に格納するつもりなので作業はmasterノードだけで作業します。また、今回のContainer bundleは、Docker imageの「centos:latest」を使って作成してみようと思います。
dockerイメージをpullします。
[john@master john]$ docker pull centos:latest latest: Pulling from library/centos a1d0c7532777: Pull complete Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177 Status: Downloaded newer image for centos:latest docker.io/library/centos:latest[john@master john]$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE centos latest 5d0da3dc9764 12 months ago 231MB
次にdocker exportで、Container bundleを作成します。
Container bundleは全計算ノードで必要な為、NFS共有(/mnt/share)に作成しておきます。
今回は「centos_image」という名前にしましたが、名前は任意です。rootfsは名前を変えてはいけません。
[john@master ~]$ cd /mnt/share/john [john@master ~]$ docker create --name centos centos:latest [john@master ~]$ mkdir -p centos_image/rootfs/ [john@master ~]$ docker export centos | tar -C centos_image/rootfs/ -xf - [john@master ~]$ cd centos_image/ [john@master centos_image]$ runc --rootless=true spec --rootless [john@master centos_image]$ cd ..
→最後のcdの一つ手前、runc --rootless=true spec --rootless
でcentos_image/config.json
が作成されている事も確認しておきます。
[john@master john]$ ls -la centos_image/ total 8 drwxrwxr-x 3 john john 39 Oct 10 05:44 . drwxrwxr-x 3 john john 4096 Oct 10 05:59 .. -rw-rw-r-- 1 john john 2639 Oct 10 05:30 config.json drwxrwxr-x 17 john john 248 Oct 10 05:12 rootfs
では次にContainer bundleからコンテナが起動する事を確認してみます。
Test Container bundle
先ほどの手順で「/mnt/share/john/centos_image」にContainer bundleファイルができました。
コンテナとしての稼働テストをしていきます。 ここからはruncコマンドを使います。
runc runでコンテナのシェルが起動します。
[john@master john]$ runc run --bundle /mnt/share/john/centos_image centos
適当にコマンドを入力すると結果が出力されています。
sh-4.4# grep ^NAME /etc/os-release NAME="CentOS Linux" sh-4.4# exit exit
→Container bundleから作成したコンテナが、無事に動く事が確認できました。
補足.1
上記ではrunc run ~で動作確認しましたが
- runc create ~
- runc start ~
で確認する場合、
- runc createで以下のようなエラーがでるかと思います。
例)runc create centos-test --bundle centos_image/
ERRO[0000] runc create failed: cannot allocate tty if runc will detach without setting console socket
→centos_image/config.json
のprocess{}.terminalをtrue→falseに変更するとエラーなくcreaterできると思います。
(※ Slurmでもconfig.jsonは読み込んだ後に、処理内でterminalはfalseに変更されていました。)
また、runc startで以下のようにエラーがでるかと思います。
例)runc start centos-test
[john@master john]$ sh: cannot set terminal process group (-1): Inappropriate ioctl for device sh: no job control in this shell
→centos_image/config.json
のprocess{}.args[]を"sh"→"echo","hello-world"などにしておくと、ここでのエラーは回避できるかと思います。
補足 以上
oci.conf
それではoci.confを作成します。 今回は以下のように作成しました。
ContainerPath=/home/john/local_image CreateEnvFile=False RunTimeQuery="runc --rootless=true --root=/tmp/ state %n.%u.%j.%s.%t" RunTimeKill="runc --rootless=true --root=/tmp/ kill -a %n.%u.%j.%s.%t" RunTimeDelete="runc --rootless=true --root=/tmp/ delete --force %n.%u.%j.%s.%t" RunTimeRun="runc --rootless=true --root=/tmp/ run %n.%u.%j.%s.%t -b %b"
基本的には公式をそのまま拝借しました。 Slurm Workload Manager - oci.conf
RunTimeCreate
&RunTimeStart
は、RunTimeRun
とは一緒には設定できない。などruncコマンドの挙動と関連します。
ContainerPath
は、計算ノードの処理内(slurmstepd)で読み込んだbundleを展開する為に使うディレクトリのようです。
計算ノード内で未存在のディレクトリ(/home/john/local_image)を指定しておきました。
↓ContainerPath
に実際に前段のbundle作成先の/mnt/share/john/centos_imageを指定すると、sbatch時に計算ノードでエラーが出てしまいます。
[2022-10-08T12:27:45.291] [689.batch] error: \_write_config: unable to open /home/john/centos_image/config.json: File exists [2022-10-08T12:27:45.291] [689.batch] error: setup_container: container setup failed: File exists [2022-10-08T12:27:45.291] [689.batch] error: _step_setup: container setup failed: File exists [2022-10-08T12:27:45.291] error: slurmstepd return code 17: File exists
それでは、oci.confをNode1~4に撒きます(Node1への例)。
scp -p /etc/slurm/oci.conf node1:/etc/slurm
忘れずにslurmd, slurmctldを再起動します。
sudo systemctl restart slurmd
sudo systemctl restart slurmctld
動かしてみる
sbatch --container
それでは実際に動かしてみます。
今回は簡単なサンプルでsbatchをmasterから投げてみます。
[john@master ~]$ sbatch --container /mnt/share/john/centos_image --wrap 'grep ^NAME /etc/os-release' Submitted batch job 779
すぐに処理が終わってsqueueでは視認できなかったので、sacctで履歴を確認してみます。
[slurm@master slurm]$ sacct --alluser --job 779 JobID JobName Partition Account AllocCPUS State ExitCode ------------ ---------- ---------- ---------- ---------- ---------- -------- 779 wrap partition+ chemistry 1 COMPLETED 0:0 779.batch batch chemistry 1 COMPLETED 0:0
→COMPLETEDしているようです。
では、Node1で出力されたログを確認します。
[john@node1 ~]$ cat slurm-779.out NAME="CentOS Linux" slurmstepd: error: _try_parse: JSON parsing error 71 bytes: boolean expected
→grep ^NAME /etc/os-release
の出力NAME="CentOS Linux"
が無事出力されました!!!
...しかし...
その後に slurmstepd: error: _try_parse: JSON parsing error 71 bytes: boolean expected
ちょっと許しがたいですねこれは。。。
→解決したら更新したいと思います。
※解決の時間がしばらくとれないかも知れず、忘れない為に今回はひとまずこの段階で記事にさせていただきました。
補足.2
sbatchを最初に実行した際に、僕の環境で出たエラーです。
[2022-10-07T12:25:40.018] [501.0] debug3: plugin_peek->_verify_syms: found Slurm plugin name:Serializer URL encoded plugin type:serializer/url-encoded version:0x160504 [2022-10-07T12:25:40.018] [501.0] debug3: plugin_peek: dlopen(/usr/local/lib/slurm/serializer_json.so): libjson-c.so.5: cannot open shared object file: No such file or directory
libjson-c.so.5がなかったようでした。
対策をメモしておきます。
json-cをインストールします。Slurm Workload Manager - Download Slurm
\# cd /usr/local \# git clone --depth 1 --single-branch -b json-c-0.15-20200726 https://github.com/json-c/json-c.git json-c \# mkdir json-c-build \# cd json-c-build \# cmake ../json-c \# make \# sudo make install
/usr/local/lib64配下にライブラリが作成(cmake先が微妙でした)
# find / -name ibjson-c.so.5 /usr/local/lib64/libjson-c.so.5
共有ライブラリ参照先に追加しておきます。
# echo "/usr/local/lib64" > /etc/ld.so.conf.d/json-c.conf \# ldconfig \# ldconfig -p | grep libjson libjson-c.so.5 (libc6,x86-64) => /usr/local/lib64/libjson-c.so.5 libjson-c.so.4 (libc6,x86-64) => /lib64/libjson-c.so.4 libjson-c.so (libc6,x86-64) => /usr/local/lib64/libjson-c.so libjson-c.so (libc6,x86-64) => /lib64/libjson-c.so
補足 以上
さいごに
(未解決) slurmstepd: error: _try_parse: JSON parsing error 71 bytes: boolean expected
エラーは残りますが、--containerオプションでOCI Containerが動く、という所を感じるところまでは来ました笑。
エラーが解決したら記事はアップしたいとおもいます。
2022/11/26 アップ
感想
今後のバージョン(23~)で、DockerやPodmanなどのインターフェイスとSlurm間での連携の直感性が上がると嬉しいですが、 もう少し重たいコンテナワークロードなどを動かして検証してみたい、とは思いました。
Singularityの設定方法もちゃんと見てはいないですが(見ておけよと)色んなHPC運用環境があると思うのでコンテナ最適解を選ぶにも、このあたりが充実していくのは良いことですね。
(あとC言語も勉強しよっと)