はじめに
前回記事にてWSL2を使い、1台のサーバ上でApptainerコンテナのプロセスをMPIにて並列実行できることを確認しました。
今回はその続きとして、2台のサーバ上で別々に稼働しているApptainerコンテナをMPIにて並列実行できるかを確認していきます。最終的には姫野ベンチマークを2ノード間で実行できるところまでをゴールとします。
実行環境 / 前提条件
今回、Apptainer + MPIを実行した環境は下記の通りです。
安価に複数ノード検証を実現する方法として手元のラズパイを使っていました。
ラズパイ3と4を使用しているため、CPU/メモリが少々異なっています。
■サーバ1 (ホスト名:sakuya / ラズパイ4)
・OS:Debian GNU/Linux 12 (bookworm)
・CPU:Cortex-A72 (4コア/ARM)
・Memory:4 GB
・Open MPI:4.1.4 (ホストマシンにインストールするMPI)
・Apptainer version:1.3.3
■サーバ2 (ホスト名:sakura / ラズパイ3)
・OS:Debian GNU/Linux 12 (bookworm)
・CPU:Cortex-A53 (4コア/ARM)
・Memory: 1 GB
・Open MPI:4.1.4 (ホストマシンにインストールするMPI)
・Apptainer version:1.3.3
なお、上記のホスト名はそれぞれ名前解決ができるよう事前に/etc/hostsに
IPアドレスとホスト名の組をそれぞれ登録していました。
また、事前に両マシンには同じ名前のLinuxユーザ(ro644)を作成済みであることを記載しておきます。
ラズパイへのApptainerインストール
前回と同様、まずはApptainerのインストールから始めました。これは本題ではないため完結に記載しますが、今回はWSL2ではなくラズパイを使っているためCPUのアーキテクチャがx86_64ではなく、arm64となっています。
そのため、前回記事にて導入していた手順のように、debファイルをダウンロードする手順が利用できませんでした (arm64用のdebファイルがreleaseページに見当たらなかった)
やむを得ず、下記手順でソースコードをダウンロードしインストールしました。
### レポジトリより一式をclone
> git clone https://github.com/apptainer/apptainer.git
> cd apptainer/tools
### ユーザレベルでのインストールが一番作業量を少なくできたためコレで
> bash install-unprivileged.sh -a aarch64 /home/ro644/
### PATHにapptainerコマンドを追加する
> echo 'export PATH=$PATH:/home/ro644/aarch64/bin' >> ~/.bashrc
> source ~/.bashrc
### 正常にインストールできたことを確認
> apptainer --version
apptainer version 1.3.3-1.el8
MPI実行用コンテナ1つの作成
無事にApptainerがインストールできたので、片方(今回はラズパイ4)にてMPI実行用のコンテナを作成します。後述しますが、もう一方のマシンにはコンテナイメージ(SIFファイル)を読み込ませて、1つ目のコンテナのコピーを作成することで構築します。以下は前回と重複する部分もありますが改めて記載します。
### ubuntuの最新版docker imageをダウンロード
> apptainer build --sandbox mpi-multinodes docker://ubuntu:latest
### fakerootを使うことで一般ユーザ権限でも疑似的にコンテナ内でroot権限を握る
> apptainer shell --fakeroot -w mpi-multinodes
### ここからコンテナ内の操作
> apt update
> apt-get install openmpi-bin/noble openmpi-doc/noble libopenmpi-dev/noble
### ホストマシン側とOpen MPIのversionをそろえた方がよいかもしれない
> mpirun --version
mpirun (Open MPI) 4.1.6
### 正常動作を確認後、次の作業のためコンテナから抜ける
> exit
補足事項として、fakerootオプションを利用する際、当該マシン上でsubuid/subgidが利用できないとコンテナの起動に失敗する可能性があるため、事前にインストールしておくとよいかと思います。 (自戒の意味も込めて記載)
SIFファイルからもう1つのコンテナを作成
前ステップにて片方のラズパイ上でMPIを実行できるコンテナイメージが作成できたため、それをSIFファイルに固めて、もう一方のラズパイ上にコピーします。
下記のコマンド群にてコンテナからSIFファイルを生成し、他方のラズパイへSIFファイルを転送しました。
もし、この段階で既に並列実行する想定の実行ファイル等が手元にあるならば、先にコンテナ内に格納した上でSIFファイルに固めておくことを勧めます(後述)
### 用意したコンテナからimage(mpi-multinodes.sif)を作成する。
### fakerootを利用しないと権限不足のエラーが出力されるため付与
> apptainer build --fakeroot mpi-multinodes.sif mpi-multinodes/
> ls -l mpi-multinodes.sif
-rwxr-xr-x 1 ro644 ro644 206M Sep 1 12:22 mpi-multinodes.sif
### SIFファイルを転送する。
> scp mpi-multinodes.sif sakura:/home/ro644
上記にてSCPなどで転送完了後はSIFファイルからコンテナを作成します。
転送先のラズパイへSSH接続し、下記のようにhomeディレクトリ上からbuildコマンドを実行しました。
### 転送したimageからコンテナを生成する
> apptainer build --sandbox mpi-multinodes ./mpi-multinodes.sif
WARNING: No default remote in use, falling back to default keyserver: https://keys.openpgp.org
INFO: Starting build...
INFO: Verifying bootstrap image mpi-multinodes.sif
INFO: Creating sandbox directory...
INFO: Build complete: mpi-multinodes
以上にて2つのコンテナの作成は完了です。必要に応じて、SIFファイルから生成した方のコンテナについても、起動/動作確認をしてください。
ホストマシン間のSSH設定
本手順はApptainerというより、MPIを複数ノードで実行する際に必要な手順となります。実施することとしては、各ノード間でSSH接続する際にパスワード認証を要求されないようにするため、秘密鍵/公開鍵を生成し、登録しておきます。
私は2台のラズパイ間でパスワードを要求されないように、下記作業を双方向で実施しました。
もし、どちらかのマシンをマスターノードとして定めているのであれば、
マスターノード → 計算ノードの方向でSSH接続した際にパスワード要求されないように設定できればOKです。
### 今回はed25519で、パスフレーズを無しにして鍵を生成しました
> ssh-keygen -t ed25519 -N ''
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ro644/.ssh/id_ed25519):
Your identification has been saved in /home/ro644/.ssh/id_ed25519
Your public key has been saved in /home/ro644/.ssh/id_ed25519.pub
The key fingerprint is:
(中略)
> cat /home/ro644/.ssh/id_ed25519.pub
### catの結果を他方のラズパイ(計算ノード)上の.ssh/authorized_keysに追記する
上記変更に加えて、SSH接続先のsshd_configに含まれるPubkeyAuthenticationを下記のようにnoからyesに修正しておき、sshdを再起動することで設定を反映させます。
以上の手順にてパスワード認証をせずにSSH接続ができるようになります。
fingerprintの確認メッセージを表示させないようにする意味を込めて事前に1度はSSH接続できることを確認しておくとよいかと思います。
### sshd_configの中の下記項目をnoからyesに変更
PubkeyAuthentication yes
### serviceの確認
> sudo systemctl status sshd.service
### serviceの再起動
> sudo systemctl restart sshd.service
### serviceの確認 (念のため)
> sudo systemctl status sshd.service
### サービス起動確認後、ssh接続できることを確認する
> ssh sakura
ホストマシン上へのMPIインストール
ここまでの手順を経て、最後にOpenMPIをホストマシン側にもインストールします。2台のマシン上で下記を実行し、一式をインストールしました。
また、このタイミングでmpirunを実行する際、どのノードでプロセスを動かすかを指定するため、ホスト名をまとめて記述したhostlistというファイルを作成しておきました。
### mpiccにてコンパイルおよびmpirunにて実行できるようにしておく
> sudo apt-get install openmpi-bin libopenmpi-dev openmpi-common
### 正常に動作することを確認する (versionを揃えておけばよかったかも(n=2))
> mpirun --version
mpirun (Open MPI) 4.1.4
### hostlistという名称でノード名をまとめたファイルを作成
> cat hostlist
sakuya
sakura
プログラムのテスト実行 + 失敗談
まずはMPIのプログラムがノード跨ぎ、かつコンテナ跨ぎで並列実行できるのかHello Worldを表示するサンプル使って試してみました。こちら[1]のsample.cのプログラムをお借りしました。
#include <stdio.h>
#include <mpi.h>
int main(int argc, char *argv[]) {
int rank; // ランク
int name_length = 32; // ホスト名の長さ
char name[name_length]; // ホスト名
MPI_Init(&argc, &argv); // MPIの初期化
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // ランクの取得
MPI_Get_processor_name(name, &name_length); // ホスト名の取得
printf("Hello world from %s! Rank is %d.\n", name, rank);
MPI_Finalize(); // MPIの終了
return 0;
}
ここで気が付いたのですが、この段階になってプログラムをコーディング + コンパイルしないで、SIFファイルを作成する前にコンテナ内にプログラムを仕込んでおけば手間が省けてよかったですね。。(失敗談)
泣く泣く、2つのコンテナ内の/home/ubuntu配下に上記プログラムの実行ファイル (a.out) を配備しました。プログラムの配備後、下記のようにコンテナ間MPIを実行しました。
### MPI実行時のコマンド
> mpirun --np 8 --hostfile hostlist --mca btl_vader_single_copy_mechanism none /home/ro644/aarch64/bin/apptainer exec mpi-multinodes /home/ubuntu/a.out
### 下記が実行結果 一部warningが表示されているが成功していそう。
Authorization required, but no authorization protocol specified
(中略)
Authorization required, but no authorization protocol specified
Hello world from sakuya! Rank is 0.
Hello world from sakuya! Rank is 2.
Hello world from sakuya! Rank is 3.
Hello world from sakuya! Rank is 1.
Hello world from sakura! Rank is 6.
Hello world from sakura! Rank is 7.
Hello world from sakura! Rank is 4.
Hello world from sakura! Rank is 5.
実行結果にてホスト名が拾えていることから分かるように、想定通りホストを跨いだコンテナ間MPIができていそうです。
そのため、当初の目的としていた姫野ベンチマークを実行してみます。
ベンチマークの実行
前回記事と同様、ベンチマークプログラムはこちら[2]のリンクからC + MPI, static allocate versionをダウンロードして利用しました。
ダウンロードしたソースコードをラズパイ上のコンテナ外で[2]の手順に従い、
下記パラメータを与えて実行ファイルを生成しました。今回、ベンチマークの実行には並列に動かすプロセス数が8つになるよう設定しました。
### プロセス数を調整するため、下記のように設定してmake
> paramset.sh S 1 2 4
> make
> ls -l bmt
-rwxr-xr-x 1 ro644 ro644 72608 Sep 3 12:08 bmt
(生成したbmtファイルは2つのコンテナ内にコピーしました。ただし今回はコマンドが長くなってしまう点から、コンテナ内のPATHが通っている /usr/bin 配下にコピーしています)
実際に姫野ベンチマークを実行した際の結果は下記の通りです。
ro644@sakuya:~ $ mpirun --np 8 --hostfile hostlist --mca btl_vader_single_copy_mechanism none /home/ro644/aarch64/bin/apptainer exec mpi-multinodes bmt
Authorization required, but no authorization protocol specified
(中略)
Authorization required, but no authorization protocol specified
Sequential version array size
mimax = 65 mjmax = 65 mkmax = 129
Parallel version array size
mimax = 65 mjmax = 35 mkmax = 35
imax = 64 jmax = 33 kmax =33
I-decomp = 1 J-decomp = 2 K-decomp =4
Start rehearsal measurement process.
Measure the performance in 3 times.
MFLOPS: 759.172310 time(s): 0.065075 3.294656e-03
Now, start the actual measurement process.
The loop will be excuted in 2766 times
This will take about one minute.
Wait for a while
cpu : 55.531830 sec.
Loop executed for 2766 times
Gosa : 3.007379e-05
MFLOPS measured : 820.243942
Score based on Pentium III 600MHz : 9.901544
上記の結果だけでは「本当に2つのサーバ上でベンチマークが実行されていたのか分からない」とも思ったため、確認のため、mpstatを使い、ベンチマーク実行時のCPU利用率について3秒ごとに取得し状況調査しました。その結果が下図です。
左がラズパイ4、右がラズパイ3になっています。いずれも実行前は、CPUの利用率がほぼ100%でidle状態だったものの、ベンチマーク実行中はいずれのマシンも%usr, %sysの使用率が上昇していることが分かります。今回はラズパイ4(左側)からmpirunを実行してみたのですが、その影響かカーネルレベルでのリソース使用率が左側の方が高くついてしまいました。
以上、上記の結果から2台のラズパイを使ってノードを跨いだコンテナ間MPIの実行には成功したと判断しました。
おわりに
本記事の結果から、複数ノード上で稼働しているApptainerコンテナ同士でもプロセス並列が実現できることを確認できました。今回はラズパイ上での試行にはなったものの、DGXなど大規模な計算ノード上でも同様の手順を経て、ジョブを投入できることの裏付けにはなったかと理解しています。
Dockerでは手間を要するコンテナ間でのMPI通信に対して、Apptainerを活用してみるという選択肢が持てるようになったのは大きな知見だと感じています。
余力があれば今回の結果について、並列数や実験のサイズを変更した場合にパフォーマンスがどの程度変わるのかをまとめてられればと考えています。
次回の記事は少し簡潔な内容で、障害発生時の切り分けに使用するためのDockerコンテナについて投稿できればと考えています。
コメントを残す