マイLab手帖

普段はサイエンス業界でSRE的な仕事をやっています。主に自宅環境でのハンズオンの備忘録。

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 --rootlesscentos_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 ~

で確認する場合、

  1. 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.jsonprocess{}.terminalをtrue→falseに変更するとエラーなくcreaterできると思います。 (※ Slurmでもconfig.jsonは読み込んだ後に、処理内でterminalはfalseに変更されていました。)

  1. また、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.jsonprocess{}.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 アップ

taqqu.hatenablog.com


感想

今後のバージョン(23~)で、DockerやPodmanなどのインターフェイスとSlurm間での連携の直感性が上がると嬉しいですが、 もう少し重たいコンテナワークロードなどを動かして検証してみたい、とは思いました。

Singularityの設定方法もちゃんと見てはいないですが(見ておけよと)色んなHPC運用環境があると思うのでコンテナ最適解を選ぶにも、このあたりが充実していくのは良いことですね。

(あとC言語も勉強しよっと)