AWS Batch で OpenMPI を使った並列ジョブの実行をやってみた

2023.05.03

AWS Batch で並列ジョブの動作検証しようと思ったときにちょうどいいワークショップを見つけました。ワークショップの中から私が確認したかった部分を抜粋してやってみたところを紹介します。

  • AWS Batch で並列ジョブを実行する
  • Docker コンテナ間のノード間通信する(OpenMPI)ジョブを投げる
  • 10台の EC2 インスタンス上でコンテナが10個起動し Hello World を返す

やってみた感想

AWS Batch 側の設定はマルチノードの並列を有効にするオプションを有効にするだけでした。ノード間通信をサポートしている MPI や Apache MXNet, TensorFlow, Caffe2 を利用したコンテナ化したアプリ開発が肝で、AWS のインフラ側は難しい設定はありませんでした。

AWS Batch の並列ジョブとは

複数の EC2 インスタンスにまたがって単一のジョブを実行できます。AWS Batch の配列ジョブの機能のひとつに Multi-node parallel jobs があります。

Multi-node parallel jobs - AWS Batch

要は密結合 HPC ワークロードを AWS Batch で実行できます。つまりDocker コンテナ間(ノード間)通信した演算が可能です。

画像引用: (1) サービスアップデート Compute 編:HPC on AWS アップデート - YouTube

ワークショップをやってみた

Cloud9 で AWS Batch で実行する OpenMPI を使ったコンテナイメージを準備します。

項目
AMI名 Cloud9AmazonLinux2-2023-04-14T11-38
AMI ID ami-0f978bc607315b3c8
OS Amazon Linux 2

環境構築は基本的にはワークショップの手順に沿って進めば問題ありませんでした。個人的に気になった点をピックアップします。

Cloud9 の AWS CLI のバージョン2 へ

Amazon Linux 2 ベースのイメージのため、AWS CLI のバージョンが 1 でした。以下の手順で AWS CLI v2 を利用可能にしています。

Multi-node Parallel Jobs on AWS Batch

Cloud9 は良い感じ開發環境を用意してくれていたイメージがあったので、勝手にバージョン 2 を使っているかと思いっていました。

Cloud9 上で OpenMPI のセットアップ

Docker コンテナで OpenMPI を使うのになぜローカル環境となる Cloud9 にも OpenMPI をインストールする必要あるのか謎でした。 手順を進めていくとわかるのですが、C で書かれた OpenMPI 実行スクリプトをビルドするのはローカル環境(Cloud9)だったためインストールが必要でした。ちなみに生成したバイナリは EFS に保存して Docker コンテナから実行する構成を取っていました。

OpenMPI はバージョン4.1.1を利用しています。現時点では 4.1 代が最新です。

wget https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.1.tar.gz
tar -xvf openmpi-4.1.1.tar.gz
cd openmpi-4.1.1
./configure --prefix=/opt/openmpi --enable-mpirun-prefix-by-default
make -j $(nproc)
sudo make install

環境変数の追加も忘れずに!パスが通っていないとmpiccでコンパイルするときに「コマンドが見つからない」となりますのでご注意ください。

export PATH=$PATH:/opt/openmpi/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/openmpi/lib

Dockerfile 作成

OpenMPI を使った動作確認用のコンテナイメージを作成します。

Dockerfile

From centos:7

ENV SSHDIR /root/.ssh

RUN \
#install openssh/supervisor and other dependencies
yum install openssh-server openssh-clients wget python3-pip make gcc-c++ perl iproute -y && \
yum clean all && \
pip3 install supervisor && \
#set passwordless ssh keygen
mkdir -p /var/run/sshd && \
cd /etc/ssh && \
sed -i 's/#PermitRootLogin/PermitRootLogin/g' sshd_config && \
sed -i 's/#PubkeyAuthentication/PubkeyAuthentication/g' sshd_config && \
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/g' sshd_config && \
echo "Host *" >> ssh_config && echo " StrictHostKeyChecking no" >> ssh_config && \
mkdir -p ${SSHDIR} && \
cd ${SSHDIR} && \
touch sshd_config && \
ssh-keygen -t rsa -f id_rsa -N '' && \
mv id_rsa.pub authorized_keys && \
echo "    IdentityFile ${SSHDIR}/id_rsa" >> /etc/ssh/ssh_config && \
#install openmpi
cd /tmp && \
wget --quiet https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.1.tar.gz && \
tar -xvf openmpi-4.1.1.tar.gz && \
cd openmpi-4.1.1 && \
./configure --prefix=/opt/openmpi --enable-mpirun-prefix-by-default && \
make -j $(nproc) && \
make install && \
cd .. && \
rm -rf openmpi-4.1.1

ENV PATH=$PATH:/opt/openmpi/bin:/data
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/openmpi/lib

WORKDIR /data

Cloud9 に Docker はインストール済みでしたのでイメージのビルドは普通にできます。

$ docker build -t aws/mpi-demo:latest .
$ docker images
REPOSITORY     TAG       IMAGE ID       CREATED             SIZE
aws/mpi-demo   latest    3a22d1ae3def   About an hour ago   455MB
centos         7         eeb6ee3f44bd   19 months ago       204MB

ECR へイメージをプッシュ

Cloud9 の EC2 インスタンスの IAM ロールには AdministoratorAccess の IAM ポリシーを付与するという指示があります。そのため、ECR の操作でつまづくことはありません。

AWS CLI でリポジトリを作成します。

$ aws ecr create-repository --repository-name mpi-repo

ECR のプライベートレジストリの認証を行います。

$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com

ECR へ Cloud9 で作成したイメージをプッシュして完了です。

$ docker tag aws/mpi-demo:latest 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mpi-repo:latest
$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mpi-repo:latest

EFS に実行スクリプトを準備

EFS に実行するスクリプト一式を保存する必要がありました。コンテナイメージに実行ファイル含めた方がいいのでは?(Cloud9 に EFS マウントするの手間だし)と思いましたが説明に書いてありました。スクリプトの内容は変更するかもしれないからあえて EFS へ保存する構成をとったとのことです。

These files can also be packaged into the container image through Dockerfile, in order not to rebuild the image every time we make some changes during development, here we placed them in the shared storage efs.

Multi-node Parallel Jobs on AWS Batch

Cloud9 に EFS をマウントしました。

$ sudo yum install -y amazon-efs-utils
$ mkdir /home/ec2-user/environment/efs
$ sudo mount -t efs -o tls fs-0f1158ade79354809:/ /home/ec2-user/environment/efs

所定のスクリプトと、ビルドした実行ファイルmpi_hello_worldを EFS へ保存しました。

$ ls -l
total 24
-rwxrwxr-x 1 ec2-user ec2-user  528 Apr 29 07:09 entry-point.sh
-rwxrwxr-x 1 ec2-user ec2-user 8464 Apr 29 05:07 mpi_hello_world
-rwxrwxr-x 1 ec2-user ec2-user 3388 Apr 29 07:08 run-mpi.sh
-rw-rw-r-- 1 ec2-user ec2-user  693 Apr 29 07:08 supervisord.conf

AWS Batch の設定

ワークショップの手順ではスポットインスタンスを利用しません。オンデマンドの EC2 インスタンスで演算することになります。ワークショップだから安定のオンデマンドを選択しているのか?と思って調べてみると、マルチノード並列ジョブ実行時はスポットインスタンスの利用ができませんでした。

Multi-node parallel jobs aren't supported on compute environments that use Spot Instances.

Multi-node parallel jobs aren't supported on compute environments that use Spot Instances.

マルチノードの並列を有効にするオプションを有効にします。今回検証したかった機能はここのチェック有無で管理されていました。

コンテナイメージは Cloud9 で作成して ECR へプッシュ済みのイメージを指定します。

実行ジョブのスクリプト内で環境変数が使われています。ジョブ定義に環境変数を入力してジョブへ渡します。

EFS はコンテナからみると/dataへマウントします。

特権付与する指示があったので root での実行設定しています。

あとはマウントする EFS の指定です。

ジョブの実行

今回作成したコンピューティング環境、ジョブキュー、ジョブ定義を使ってジョブをします。

10 台の EC2 インスタンスが起動してきました。

正常に起動できたので、あとはジョブの実行結果を待ちます。

実行結果の確認

メインノードはSUCEED表示で、残りの子ノードはFAIED表示です。この結果は正常なのかと調べてみると、最終的なジョブ実行結果はメインノードのジョブのステータスとありました。メインノードが成功しているため、今回のジョブは正常に実行できたことを意味します。

The final job status (SUCCEEDED or FAILED) is determined by the final job status of the main node. To get the status of a multi-node parallel job, describe the job by using the job ID that was returned when you submitted the job.

Multi-node parallel jobs aren't supported on compute environments that use Spot Instances.

EFS を確認すると新たに出力されたファイルが2つありました。

$ ls -ltr
total 32
-rwxrwxr-x 1 ec2-user ec2-user 8464 Apr 29 05:07 mpi_hello_world
-rwxrwxr-x 1 ec2-user ec2-user 3388 Apr 29 07:08 run-mpi.sh
-rw-rw-r-- 1 ec2-user ec2-user  693 Apr 29 07:08 supervisord.conf
-rwxrwxr-x 1 ec2-user ec2-user  528 Apr 29 07:09 entry-point.sh
-rw-r--r-- 1 root     root      185 Apr 29 08:14 hostfile-1682756085585
-rw-r--r-- 1 root     root        2 Apr 29 08:14 batch-exit-code

メインノードと、子ノード合わせて10台分の EC2 インスタンスの IP アドレスが記録されていました。

hostfile-1682756085585

10.0.1.12 slots=2
10.0.1.4 slots=2
10.0.1.64 slots=2
10.0.1.161 slots=2
10.0.1.166 slots=2
10.0.1.154 slots=2
10.0.1.135 slots=2
10.0.1.27 slots=2
10.0.1.203 slots=2
10.0.1.133 slots=2

ファイル名のとおり AWS Batch の終了コードが記録されているものだと思われます。

batch-exit-code language=

0

メインノードの実行ログを確認してみます。

メインノードには子ノードでの実行した結果が返ってきていました。

-----------------------------------------------------------------------------------------------------------------------------------
|                                                             message                                                             |
|---------------------------------------------------------------------------------------------------------------------------------|
| 2023-04-29 08:14:19,536 INFO Set uid to user 0 succeeded                                                                        |
| 2023-04-29 08:14:19,537 INFO supervisord started with pid 6                                                                     |
| 2023-04-29 08:14:20,540 INFO spawned: 'sshd' with pid 9                                                                         |
| 2023-04-29 08:14:20,542 INFO spawned: 'synchronize' with pid 10                                                                 |
| run-mpi.sh - Running synchronize as the main node                                                                               |
| 2023-04-29 08:14:20,549 INFO success: synchronize entered RUNNING state, process has stayed up for > than 0 seconds (startsecs) |
| run-mpi.sh - main                                                                                                               |
| run-mpi.sh - Running as master node                                                                                             |
| run-mpi.sh - master details -> 10.0.1.12:2                                                                                      |
| run-mpi.sh - 1 out of 10 nodes joined, will check again in 5 second                                                             |
| 2023-04-29 08:14:21,570 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)        |
| run-mpi.sh - 1 out of 10 nodes joined, will check again in 5 second                                                             |
| run-mpi.sh - 1 out of 10 nodes joined, will check again in 5 second                                                             |
| run-mpi.sh - 1 out of 10 nodes joined, will check again in 5 second                                                             |
| run-mpi.sh - 1 out of 10 nodes joined, will check again in 5 second                                                             |
| run-mpi.sh - All nodes successfully joined                                                                                      |
| hostfile:                                                                                                                       |
| 10.0.1.12 slots=2                                                                                                               |
| 10.0.1.4 slots=2                                                                                                                |
| 10.0.1.64 slots=2                                                                                                               |
| 10.0.1.161 slots=2                                                                                                              |
| 10.0.1.166 slots=2                                                                                                              |
| 10.0.1.154 slots=2                                                                                                              |
| 10.0.1.135 slots=2                                                                                                              |
| 10.0.1.27 slots=2                                                                                                               |
| 10.0.1.203 slots=2                                                                                                              |
| 10.0.1.133 slots=2                                                                                                              |
| run-mpi.sh - executing main MPIRUN workflow                                                                                     |
| MPI_THREADS: 20                                                                                                                 |
| Warning: Permanently added '10.0.1.4' (RSA) to the list of known hosts.                                                         |
| Warning: Permanently added '10.0.1.133' (RSA) to the list of known hosts.                                                       |
| Warning: Permanently added '10.0.1.27' (RSA) to the list of known hosts.                                                        |
| Warning: Permanently added '10.0.1.64' (RSA) to the list of known hosts.                                                        |
| Warning: Permanently added '10.0.1.161' (RSA) to the list of known hosts.                                                       |
| Warning: Permanently added '10.0.1.166' (RSA) to the list of known hosts.                                                       |
| Warning: Permanently added '10.0.1.135' (RSA) to the list of known hosts.                                                       |
| Warning: Permanently added '10.0.1.154' (RSA) to the list of known hosts.                                                       |
| Warning: Permanently added '10.0.1.203' (RSA) to the list of known hosts.                                                       |
| Hello world from processor ip-10-0-1-12.ap-northeast-1.compute.internal, rank 1 out of 20 processors                            |
| Hello world from processor ip-10-0-1-12.ap-northeast-1.compute.internal, rank 0 out of 20 processors                            |
| Hello world from processor ip-10-0-1-4.ap-northeast-1.compute.internal, rank 3 out of 20 processors                             |
| Hello world from processor ip-10-0-1-133.ap-northeast-1.compute.internal, rank 18 out of 20 processors                          |
| Hello world from processor ip-10-0-1-27.ap-northeast-1.compute.internal, rank 14 out of 20 processors                           |
| Hello world from processor ip-10-0-1-64.ap-northeast-1.compute.internal, rank 4 out of 20 processors                            |
| Hello world from processor ip-10-0-1-166.ap-northeast-1.compute.internal, rank 9 out of 20 processors                           |
| Hello world from processor ip-10-0-1-135.ap-northeast-1.compute.internal, rank 13 out of 20 processors                          |
| Hello world from processor ip-10-0-1-203.ap-northeast-1.compute.internal, rank 17 out of 20 processors                          |
| Hello world from processor ip-10-0-1-161.ap-northeast-1.compute.internal, rank 6 out of 20 processors                           |
| Hello world from processor ip-10-0-1-154.ap-northeast-1.compute.internal, rank 11 out of 20 processors                          |
| Hello world from processor ip-10-0-1-4.ap-northeast-1.compute.internal, rank 2 out of 20 processors                             |
| Hello world from processor ip-10-0-1-133.ap-northeast-1.compute.internal, rank 19 out of 20 processors                          |
| Hello world from processor ip-10-0-1-27.ap-northeast-1.compute.internal, rank 15 out of 20 processors                           |
| Hello world from processor ip-10-0-1-64.ap-northeast-1.compute.internal, rank 5 out of 20 processors                            |
| Hello world from processor ip-10-0-1-166.ap-northeast-1.compute.internal, rank 8 out of 20 processors                           |
| Hello world from processor ip-10-0-1-135.ap-northeast-1.compute.internal, rank 12 out of 20 processors                          |
| Hello world from processor ip-10-0-1-203.ap-northeast-1.compute.internal, rank 16 out of 20 processors                          |
| Hello world from processor ip-10-0-1-161.ap-northeast-1.compute.internal, rank 7 out of 20 processors                           |
| Hello world from processor ip-10-0-1-154.ap-northeast-1.compute.internal, rank 10 out of 20 processors                          |
| run-mpi.sh - done! goodbye, writing exit code to /data/batch-exit-code and shutting down my supervisord                         |
| 2023-04-29 08:14:48,059 INFO exited: synchronize (exit status 0; expected)                                                      |
| 2023-04-29 08:14:48,059 WARN received SIGTERM indicating exit request                                                           |
| 2023-04-29 08:14:48,059 INFO waiting for sshd to die                                                                            |
| 2023-04-29 08:14:49,061 WARN stopped: sshd (terminated by SIGINT)                                                               |
| entry-point.sh - Reading exit code from batch script stored at /data/batch-exit-code                                            |
-----------------------------------------------------------------------------------------------------------------------------------

次に子ノードの実行ログを1件分確認してみます。

子ノードの実行結果をメインノードへ報告するといったログがあります。main nodeではなくmaster nodeとなっているのは master - slave 廃止の動きから名称変更がどこかのタイミングで入ったものの完全に修正しきれていない名残ではないかと思います。

-----------------------------------------------------------------------------------------------------------------------------------
|                                                             message                                                             |
|---------------------------------------------------------------------------------------------------------------------------------|
| 2023-04-29 08:14:39,965 INFO Set uid to user 0 succeeded                                                                        |
| 2023-04-29 08:14:39,966 INFO supervisord started with pid 7                                                                     |
| 2023-04-29 08:14:40,968 INFO spawned: 'sshd' with pid 10                                                                        |
| 2023-04-29 08:14:40,971 INFO spawned: 'synchronize' with pid 11                                                                 |
| run-mpi.sh - child                                                                                                              |
| 2023-04-29 08:14:40,977 INFO success: synchronize entered RUNNING state, process has stayed up for > than 0 seconds (startsecs) |
| run-mpi.sh - I am a child node -> 10.0.1.4:2, reporting to the master node -> 10.0.1.12                                         |
| Warning: Permanently added '10.0.1.12' (RSA) to the list of known hosts.                                                        |
| run-mpi.sh - done! goodbye                                                                                                      |
| 2023-04-29 08:14:41,108 INFO exited: synchronize (exit status 0; expected)                                                      |
| 2023-04-29 08:14:42,110 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)        |
-----------------------------------------------------------------------------------------------------------------------------------

おわりに

AWS Batch で並列ジョブの実行する時間と機会がなくはじめて実行してみました。ワークショップ最後の Gromacs 実行まで試せていませんが、最低限並列ジョブの実行方法は確認できました。

密結合な HPC ワークロードであれば AWS ParallelCluster を採用するケースが多かったのですが近年はコンテナ化がだいぶ進みました。今後は実行手段の1つとして AWS Batch も検討してみたいです。

アプリケーションをコンテナ化しても AWS ParallelCluster で動かすケースもありました。マネージドな AWS Batch のマネージドな部分をカスタマイズしたいような要件次第だとは思いますので使い分けはいければよいかなと思います。