研究室のHPCクラスター環境において、再現性の高い計算環境の構築とソフトウェア管理の効率化を目的とし、 NVIDIAが開発したenroot[1]/pyxis[2]によるコンテナ基盤の導入を行いました。 本記事では、導入の技術的背景、実装の詳細、そして運用上の課題についてまとめました。
HPC環境におけるコンテナの課題
HPCクラスターでコンテナを利用する際には、通常のDockerとは異なる要件と制約があることは広く知られています。 従来のDockerは、Dockerデーモンがroot権限での実行を前提としているため、共有クラスター環境ではセキュリティリスクとなりえます。 一方、それを回避するためのrootless Dockerではありますが、cgroupによる適切なリソース分離に制限があるなどの問題もあるようです。 また、rootless dockerの要となるUID/GID remappingが、HPC環境で広く使われているGPFSやLustreなどの並列ファイルシステムと相性が悪い[3]、 複数ノードにまたがるMPIジョブの実行が複雑化するといった課題もあります。
他の選択肢との検討
今回の導入にあたり、いくつかの選択肢を検討しました。
まず、rootless Dockerについては、UID/GID remapping (subuid, subgid)の設定が面倒、使用するユーザーごとに設定が必要、などを理由に不採用としました。 次に、Podmanについても検討しましたが、弊ラボの環境(Ubuntu 22.04)で簡単に試せなかったため、こちらも見送りました。 Singularity/Apptainerについては、HPC環境で最も広く使われているコンテナランタイムではありますが、ユーザーにとってDockerとの違いが混乱を招く可能性がある(=単に面倒くさそう)と考え見送りました。
これらの検討(=独断と偏見)の結果、NVIDIAが開発したenroot/pyxisを採用することにしました。 Enroot/pyxisの大きな特徴として、
- 極めてシンプルな設計(enrootのコードは500行未満)
- GPUサポートがネイティブに統合されている(libnvidia-container使用)
- Dockerイメージを利用可能であること
- Slurmとの緊密な統合が可能(pyxis経由)
などが挙げられます。 また、Dockerレジストリから3〜5倍高速にイメージをインポートでき、 UID remappingによる安全なマルチユーザー対応が可能で、 ほぼゼロのパフォーマンスオーバーヘッドであるという利点も魅力的です。
enrootの技術的な話
enrootは「modern chroot」というコンセプトで設計されており[4]、 Linuxカーネルの名前空間機能を活用した軽量なサンドボックスを提供します。 一方で悪く言えば、これは単にchrootしているのに結構近い状態ですので、実行しているプロセスなどがpsやtop等で他のユーザーから丸見えです。
特に重要な特徴の一つがUID remapping機構です。enrootでは、ホスト側でユーザーID 1000として動作しているユーザーが、コンテナ内ではroot(UID 0)として実行されます。
このアプローチには、/etc/subuidや/etc/subgidに依存しない、ノード間でのUID範囲の同期が不要、同一ノード上でのUID範囲の競合が発生しないといった利点があります。
ユーザーはコンテナ内でrootとして振る舞えますが、ホスト側の権限は持たないため、この仕組みによってコンテナ内でapt-get installなどのパッケージ管理操作が安全に実行できます。
また、enrootはDockerイメージをSquashFS形式に変換して保存します。 SquashFSは読み取り専用の圧縮ファイルシステムで、高圧縮率を実現しつつ、ファイルをディスクに展開せずに直接読み込めるため、メモリ効率に優れています。(ただこれは後述のようにpyxis経由では使えなかったりします、、、)
pyxisによるSlurm統合
pyxisはSlurmのSPANKプラグインとして実装されています。これにより、srunコマンドに直接コンテナ関連のオプションが追加されます。
pyxisは、
- イメージインポート: Docker URIまたはローカルsqshファイルを指定
- 名前空間作成: コンテナ用の Linux 名前空間を設定
- タスク起動: Slurm ジョブのタスクをコンテナ内で起動
- 自動クリーンアップ: ジョブ終了時に名前なしコンテナを削除
という一連のプロセスでコンテナを管理します。
実装の詳細
弊ラボの環境はUbuntu 22.04 LTSで、今回はenroot version 3.5.0、pyxis version 0.20.0を使用しました。
Slurmはversion 24.05.1をソースからビルドして/opt/slurmにインストールしています。
enrootのインストールは、GitHubのリリースページからdebパッケージをダウンロードして行いました。
具体的には、dpkg --print-architectureでアーキテクチャを確認した後、enroot本体とenroot+capsの両方をインストールしています。
arch=$(dpkg --print-architecture)
curl -fSsL -O https://github.com/NVIDIA/enroot/releases/download/v3.5.0/enroot_3.5.0-1_${arch}.deb
curl -fSsL -O https://github.com/NVIDIA/enroot/releases/download/v3.5.0/enroot+caps_3.5.0-1_${arch}.deb
sudo apt install -y ./*.deb
設定については、/etc/enroot/enroot.confで行いますが、それぞれデフォルト値が設定されています。ここでは、デフォルト値では問題があったので
ENROOT_RUNTIME_PATHENROOT_CACHE_PATHENROOT_DATA_PATH
を設定しました。特に、当初はtmpfsに配置していましたが、大きなイメージのコンテナを複数たてると簡単にあふれてしまったため、
最終的には高速なSSDがマウントされている/var/tmp以下に配置するようにしました。
pyxisのビルドとインストールについては、以下のように行いました。
# ソースの取得
git clone https://github.com/NVIDIA/pyxis.git
cd pyxis
# ビルド(Slurmのインクルードパスを指定)
CPPFLAGS=-I/opt/slurm/include/ make prefix=/opt/slurm
# インストール
sudo make prefix=/opt/slurm install
# プラグインの配置確認
ls -l /opt/slurm/lib/slurm/spank_pyxis.so
特に弊ラボの環境では上述のようにSlurmを/opt/slurmにインストールしているため、
ビルド時にこのような環境変数や引数などの指定が必要でした。
あと、plugstack.confの設定が必要です。
Slurmのconfigless機能を使用している場合は、slurmctldが動いているcontrol nodeのみ設定が必要ですが、
そうでない場合は各計算ノードに設定が必要でしょう(多分)。
# /opt/slurm/etc/plugstack.conf
required /opt/slurm/lib/slurm/spank_pyxis.so \
runtime_path=/run/pyxis \
runtime_pathはpyxisが一時的なsquashfsイメージを保存する場所を指定します。
動作確認
基本的な動作確認として、以下のようなケースを実行できるかどうか確認しました。
# コンテナを指定してコマンド実行
srun --container-image=ubuntu:22.04 cat /etc/os-release
# インタラクティブシェル
srun --pty --container-image=ubuntu:22.04 bash
# GPU付きコンテナ
srun --gpus=1 \
--container-image=nvcr.io#nvidia/pytorch:24.09-py3 \
python -c "import torch; print(torch.cuda.is_available())"
運用上の問題点
導入を進める中で、いくつかの問題点に遭遇しました。
PyxisがSquashFSを直接mountしてくれない
Enrootのdocumentをみると、 sqfs形式のイメージを使う場合は、fuse-overlayfsを使って直接mountする的なことが書かれています。 実際これでコンテナを起動すると、前触れの通り一瞬で起動しめちゃくちゃ速いです。 しかし実はpyxisはこの機能を使ってくれません。いちいちunsuqshfsでENROOT_DATA_PATHに展開してからコンテナを起動します。 unsuqshfsにはイメージが大きいとそれなりに時間がかかる上に、ENROOT_DATA_PATHにも十分な空き容量が必要になってきます(ので、上述のようにtmpfsにすると簡単に溢れてしまう)。
pyxisのissueでは、作者はこの問題には気づいているようですが、fuse-overlayfsが遅いという理由で対応していないようです。 コンテナ起動に時間がかかるのと、fuse-overlayfsが遅くなるのとのトレードオフのような気がしますので、両方使えるように実装し選べるようにして欲しいものです。 ただ、fuse-overlayfsを使いたければpyxisを使わずに直接enrootをsbatchなどから呼び出せば済む話なので、結局pyxisいらんのでは?という気になっています。
レイヤーキャッシュが効いていない?
enrootのドキュメントやissueでは「Dockerレイヤーごとにキャッシュできる」と記載されています。理論的には、レイヤーが共有されている2つのイメージをpullした場合、2個目はキャッシュが効いていれば速くなるはずです。しかし、実際には毎回同じ時間がかかっているように見えます。
上述のように、pyxis経由でdockerからイメージを持ってくると、sqshへの変換、unsuashfsによる展開、を毎回いちいち実行しているようです。 なのでレイヤーのキャッシュはきかなくても不思議ではないです。結果として、コンテナの起動はdockerより結構遅い気がします。 こういった理由で、enrootに書かれているようなdockerに対する速度のアドバンテージはあまりなくなってしまっているように思います。
container-nameの管理とストレージ圧迫
一方で、--container-nameを指定して先にイメージを明示的に展開しておくと、コンテナの起動は当たり前ですが爆速になります。
これは、展開されたコンテナがファイルシステムに残り続けるというわけで、大規模なイメージ(10GB以上)が複数蓄積するとストレージを圧迫する、イメージを更新しても古いコンテナが残り続ける、ユーザーが手動でenroot removeする必要があるといった問題が出てきます。
(一方--container-nameを指定せずに作成した名前なしコンテナはジョブ終了時に自動削除されます)。
運用方針としては、
- 毎回起動は遅くても自動クリーンアップされる名前なしコンテナの利用を推奨する
- 指定期間以上アクセスされていないコンテナを削除する定期的なクリーンアップスクリプトを用意する
- pyxis 0.16以降で導入された
container_scope=jobを設定してジョブ終了時に自動削除されるようにする
といった対応が必要でしょう。
HTTPS問題とDocker Hubアクセス
In-houseで運用しているlocal docker registryにアクセスするために、enroot.confでENROOT_ALLOW_HTTP yesを指定してHTTPを有効化すると、
Docker HubやNGC(nvcr.io)などhttpsを必須とする外部レジストリにアクセスできなくなるという問題が起こりました。
これは結構不便です。Docker daemonにはhttp→httpsへのフォールバック機能がありますが、enrootには同様の機能がないように見えます。
とりあえず、local registryにDocker HubやNGCからpullしてきたものをpushしておけば、理屈上は使えるので何とかこれで対応しています。
Local docker registryをhttpsで運用すれば解決する問題かもしれませんが、証明書をどうするかなど面倒くさそうです。 一方で、enroot自体は環境変数でENROOT_ALLOW_HTTP設定を上書きできるので、外部のdocker hubをアクセスする場合のみhttps使用に切り替えられますが、pyxis経由でこれをやる方法がわかりませんでした。
NFSからのsqshファイル読み込み問題
NFSにsqshファイルを配置し、複数ノードから同時に多数コンテナを起動すると、起動に無限に時間がかかるという問題が発生しました。 おそらく、複数ノードからの同時アクセスでNFSサーバーに大量のリクエストが発生して固まってしまっていると考えられます。
この問題への対策として、同じイメージをたくさん立てる場合は同一のコンテナインスタンスを共有できるらしいので、その辺うまいこと設定すれば問題解決するかもしれません(TODO)。 また、LustreやBeeGFSなど、並列アクセスに最適化されたファイルシステムを使用すればよいかもしれません。 あるいは、コンテナレジストリを使ってhttpでイメージをdocker registryから取ってくればNFS問題は回避されるものの、imageの変換が必要なので、その分遅くなるというトレードオフがあるでしょう。
まとめと今後の課題
enroot/pyxisの導入により、Dockerイメージが直接利用可能になり、ソフトウェア環境構築の時間が(多分)短縮されました。 また、GPU利用が極めてシンプルになったことも大きな成果です。 マルチノードMPIジョブも使えるらしいですが、これは今後の課題として実際に検証していく予定です。
残る課題としては、
- レイヤーキャッシュが効いていないように見える原因の調査と解決、
- イメージやcontainer-nameの管理方法の確立
- 共有ファイルシステム(NFS, etc)からのイメージ読み込みの最適化
などがあります。これらについては、運用を続けながら知見を蓄積し、より良い運用方法を模索していく予定です。
宣伝
計算創薬科学研究室では、本記事で紹介したようなHPC技術やプログラミングに興味があり、 これらの技術を創薬研究に応用したいという意欲のある大学院生を募集しています。 ぜひ研究室のWebサイトをご覧いただくか、お問い合わせください。
本記事は2025年11月の知見に基づいています。最新の情報は公式ドキュメントをご確認ください。
参考文献
-
NVIDIA. “enroot - A simple yet powerful tool to turn traditional container/OS images into unprivileged sandboxes.” GitHub. https://github.com/NVIDIA/enroot ↩
-
NVIDIA. “pyxis - Container plugin for Slurm Workload Manager.” GitHub. https://github.com/NVIDIA/pyxis ↩
-
Apptainer. “Installing Apptainer — Apptainer Admin Guide.”https://apptainer.org/docs/admin/main/installation.html ↩
-
Abecassis, F., & Calmels, J. (2020). “Distributed HPC Applications with Unprivileged Containers.” FOSDEM 2020. https://fosdem.org/2020/schedule/event/containers_hpc_unprivileged/ ↩