マイLab手帖

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

Slurm Installその後4. mpi4pyでOpenMPI並列処理

はじめに

これまでの記事ではSlurmの

  • アカウンティング設定
  • リソース制限(Limit)-> acount/user associationsやQoSでの制限適用

をやってみました。

今回ですが、折角ジョブスケジューラを扱うので並列処理のSlurmでの設定も少し勉強してみたいと思いました。僕の環境の虚弱な計算ノード達ではありますが、並列処理の環境準備をやってみたいと思います。

目次

OpenMPI / mpi4py

並列処理の実行については、SlurmでいくつかのMPIがサポートされています。

MPI Users Guide slurm.schedmd.com

今回はそのうちの一つのOpenMPIを使うことなります。ただ、僕がC/C++/Fortranなどを通っていないので、(まだ親近感が多少ある)pythonを使って、そのバインディングである「mpi4py (MPI for Python)」で並列処理を動かしていきたいと思います。

ちなみにOpenMPIPythonモジュールで扱えるものは今の所mpi4py一択のようです。今後困る事がなければ、マイLabではmpi4pyを使っていくつもりです。


必要なもの

環境はこれまでと同じで、以下がベースです。

・Slurm 22.05

・Master + Node1~4は全てRocky linux 8.6 (Virtual Box)

まず、今回の並列化環境に追加で必要なものを整理しておきます。

OpenMPI

MPI(Message Passing Interface)は、プロセス並列の為の標準的APIを提供するライブラリを指す。MPIの実装としてOSS利用可能なメジャーなものの一つがOpenMPI。

OpenMPI: https://www.open-mpi.org/

pmi

PMI(Process Management Interface)は元々はMPICHの一部として開発・配布されていたものっぽい。 MPI実装からプロセス管理機能が独立・標準化されたインターフェイスとして提供されるAPIで、リソースマネージャやジョブスケジューラから利用される事を想定している。

大まかなバージョンは

  • PMI-1、PMI-2(PMI-1の改良番)、PMIx v1-v4 (xは「Exsascale」の意。またPMI-1,2と下位互換)。v5も開発が進んでいるそう。

(大規模な並列環境で)大量のMPIジョブプロセスを迅速にスケーラブルできる事を主眼に、プロセスの起動サポート、メモリ効率利用、各種MPI実装の分離開発、など可能にする。

参考:

deepdive into PMI(x) : https://container-in-hpc.org/artefacts/isc/2022/hpcw/pdf/5_hpc/3_PMIx-Deep-Dive.pdf

PMIx: Process Management for Exascale Environments : http:// https://dl.acm.org/doi/pdf/10.1145/3127024.3127027

mpi4py

MPI for Python — MPI for Python 3.1.3 documentation


設定1. 並列化

ではこれから実際に設定をやっていきます。

masterとNode1~4の全マシンに対して行います。

OpenMPI / PMI

mpi4pyを利用する為にもまずは、OpenMPIを入れておく必要があります。

またPMIですが、OpenMPIにもPMIはembededされていて、ビルド時に"--with-pmi"、"--with-pmix"オプションをつける事で「PMI」「PMIx」がビルドされるようです。

OpenMPIバージョンごとの対応は以下 Which Environments Include Support for PMIx? | OpenPMIx

SlurmでもPMIを認識させる必要があり(srun --mpi=listで確認)Ver 22.05ではPMIx v2.x、v3.x、v4.x、v5.x をサポートしているようです。PMIx v5は開発中みたいですが...

ただ、SlurmのドキュメントをみるとSlurmインストール時点でPMIxは構築しておく必要があった(?)のか、後からPMIx追加をトライするも出来手こずりそうでした。なので今回はSlurmがデフォルト対応している「PMI-2」を使う事にしたい思います。

memo: 自前でPMIxをビルドする場合は以下やっておく
dnf -y install flex libev libevent-devel hwloc

では、現時点でのOpenMPI最新版を入れてみます。(公式 https://www.open-mpi.org/)

インストール先は/usr/local/openmpiとしておきます。

パス、ライブラリパス

PATHとLD_LIBRARY_PATHの設定が必要です。

今後、ユーザを適用に作って増やしたりすると思いますので/etc/profile.d配下にopenmpi.shを作成してユーザ共通にしておきます。

sudo cat <<EOF > /etc/profile.d/openmpi.sh
export PATH="\$PATH:/usr/local/openmpi/bin"
export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:/usr/local/openmpi/lib"
EOF
mpi4py

(なければ)先にpython3-develを入れておきます。

dnf -y install python3-devel

pipでインストールできます。

$ pip3 install mpi4py
Collecting mpi4py
  Downloading https://files.pythonhosted.org/packages/20/50/d358fe2b56075163b75eca30c2faa6455c50b9978dd26f0fc4e3879b1062/mpi4py-3.1.3.tar.gz (2.5MB)
    100% |████████████████████████████████| 2.5MB 389kB/s 
Installing collected packages: mpi4py
  Running setup.py install for mpi4py ... done
Successfully installed mpi4py-3.1.3

→Ver3.1.3が入りました。ひとまずこれでOKとしましょう。

全マシン共通の設定は以上です。


設定2. NFS共有

NFS

続いてですが、折角の機会なのでNFS共有の仕組みを入れます。

  • masterをNFSサーバ
  • node1~4をNFSクライアント

(割愛)NFS共有の設定手順は割愛します。

※色んな方がWebに上げてくださっているので、検索して上位Hitしてくるやりかたで問題ないと思います。

僕の環境は/mnt/shareというディレクトリをマスタ(master.science.com)とNode1~4でNFS共有しました。

以下はnode1の例ですが、他のnode2~4でも同様に見える状態です。

[john@node1 ~]$ df -Th | grep nfs
master.science.com:/mnt/share nfs4       70G  6.3G   64G   9% /mnt/share

サンプル並列処理

今回の検証で使うpythonスクリプトファイルです。

mpi4pyをインポートして標準出力を行う、だけのサンプルです。

NFS共有ディレクトリを利用して/mnt/share/[user]/mpitest.pyと配置しておきます。

from mpi4py import MPI

comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
name = MPI.Get_processor_name()

print(f"Hello, world! from rank {rank} out of {size} on {name}")

補足

  • MPIプログラムで生成される各プロセスは一意な値が設定され、get_rank()メソッドで取得できる
  • コミュニケータ(通信を実施するためのプロセスのグループ)のサイズはget_size()メソッドで取得できる
  • 生成されるプロセスの名前がGet_processor_name()メソッドで取得できる

設定3. slurm.conf

/etc/slurm/slurm.conf

以下のようにデフォルトを変更しておきます。 noneのままでもコマンドラインのオプションで--mpi=pmi2と毎回入力して実行もできます。

#MpiDefault=none
MpiDefault=pmi2

.confを変更したので以下も実施しておきます。

  • /etc/slurm/slurm.confをnode1~4に配布してslurmdを再起動

  • masterもslurmctldを再起動

設定はこれで以上です。

では、サンプルの並列処理を動かしていきたいと思います。



動かしてみる

では動かして行きます。前回から引き続きjohnユーザを使います。

今まではずっとsbatchでしたが、今回はインタラクティブに動かしてみようと思います。

  • salloc
  • srun

salloc

Slurmのマシンリソースの割当を要求します。

johnユーザでnode1から実行します。ノードへの計算負荷は全くない状況です。

確認しておくと/etc/slurm/slurm.confは以下の設定です。

PartitionName=partition1 Nodes=node[1-2] Default=YES MaxTime=INFINITE State=UP
PartitionName=partition2 Nodes=node[3-4] Default=YES MaxTime=INFINITE State=UP

リソースは--nodes=2で2ノードの割当を要求してみます。

[john@node1 ~]$ salloc --nodes=2 bash
salloc: Granted job allocation 376
[john@node1 ~]$ 

Granted job allocation 376と、無事リソースが割当てられたようです。 (--ptyをオプションにつけると割当されたノードのリモートホスト上で、対話側シェルが起動された状態になります)

この段階でsqueueを確認してみます。

[john@master john]$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
               376 partition interact     john  R       0:11      2 node[3-4]

Node3、4が割り当てられている事がわかります。NAMEは指定しなければ"interact"と表示されるようです。

それではsrunを実行してみます。

/mnt/shareNFS共有ディレクトリで実行しているので、mpitest.pyを各Nodeへ配布する必要はありません。



するとsrun python3 /mnt/share/john/mpitest.pyを実行した所で、

ImportError: libmpi.so.12: cannot open shared object file: No such file or directory
srun: error: node3: tasks 0-1: Exited with exit code 1

libmpi.so.12がないと言われます。mpi4pyでは「libmpi.so.12」としてOpenMPIのライブラリが参照されるようで、

  • cd /usr/local/openmpi/lib
  • sudo ln -s libmpi.so.40.30.4 libmpi.so.12

でopenmpi4.1.4のライブラリを参照できるようにしました。

(ちなみにOpenMPIを1.10.2のバージョンでやると上記エラーは発生しませんでした)



それでは気を取り直してsrunを再度行います。

[john@node1 ~]$ srun python3 /mnt/share/john/mpitest.py
Hello, world! from rank 0 out of 2 on node3
Hello, world! from rank 1 out of 2 on node4

2ノード(Node3,4)で並列がそれぞれ実行されました。コミュニケーター内のプロセス総数(=2)で、各プロセスが一意に割当られていそう(rank=0,1)ですね。上手く動いていそうです。

ひとまずexitします。

[john@node1 ~]$ exit
exit
salloc: Relinquishing job allocation 376
salloc: Job allocation 376 has been revoked.
[john@node1 ~]$ 

exitしたタイミングでsqueueをたたくと、割当解除が確認できます。

[john@master john]$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)



試しに先ほどより1台ノードを欲張って、--nodes=3で3ノードを要求してみると...

[john@node1 ~]$ salloc --nodes=3 bash
salloc: Requested partition configuration not available now
salloc: Pending job allocation 377
salloc: job 377 queued and waiting for resources
[john@master john]$ squeue 
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
               377 partition interact     john PD       0:00      3 (PartitionNodeLimit)

→リソース割当可能なPartitionが存在しない場合は、PartitionNodeLimit保留(PD)されてしまうようです。ctrl+zでキャンセルしておきます。


念の為、partitionの設定を変えて挙動の変化があるか?見てみます。

4ノードを全て同じpartition(partition_all)と変更してみました。

[slurm@master ~]$ sinfo
PARTITION      AVAIL  TIMELIMIT  NODES  STATE NODELIST
partition_all*    up   infinite      4   idle node[1-4]

sallocで--nodes=4を要求してみると、

[john@node1 ~]$ salloc --nodes=4 bash
salloc: Granted job allocation 408

→partitionを変更してノードを増やしているので(2→4),、ちゃんと--nodes=4で割当てされましたね。さきほどは2を超えるとpartitionのリソースを超えて保留されました。

squeueでも割当が確認できます(node[1-4])

[john@master john]$ squeue 
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
               408 partition interact     john  R       0:12      4 node[1-4]

では、srunでmpitest.pyを実行します。

[john@node1 ~]$ srun python3 /mnt/share/john/mpitest.py
Hello, world! from rank 0 out of 4 on node1
Hello, world! from rank 2 out of 4 on node3
Hello, world! from rank 3 out of 4 on node4
Hello, world! from rank 1 out of 4 on node2

4ノード(Node1~4)に対して、4並列で問題なく動いていそうです。


今回は以上になります。



最後に

今回は、

  • mpi4run(OpenMPI)を使った並列処理の設定
  • 動作確認(salloc, srun)、partition設定を変えてみる

をやってみました。

今回は手動でsallocなどをやりましたが実運用だと、ノードの数や種類、ジョブのワークロードも様々です。そのあたりをSlurmがいい感じにスケジューリングしてくれると考えると、少しワクワクしてきますね。