はじめに
本記事では, 複数台のAlmaLinuxを用いて, 並列計算を行うための環境構築を行います. AlmaLinuxは, CentOS8のサポート終了に伴い, その後継として開発されたLinuxディストリビューションです. 本記事は, LinuxのインストールができるMPI初心者を対象としています.
MPIとは
MPI (Message Passing Interface) とは, プロセス間で通信を行うための規格です. よく利用されるものには, MPICHやopenMPI があります. これらを利用して並列計算を実現させます.使用環境
・AlmaLinux 8.5
MPIの環境構築
本記事で構築する分散システム(図1)によって, 以下の作業が可能になります.
・複数台を用いて分散メモリ型の並列処理を行う.
・子が親を経由してインターネットに接続する(各マシンのアップデートに便利!).
・応用として, n対nのデータ通信をしながら数値計算を行う.
図1 分散システム
この環境を構築するために, 以下の手順が必要です.
手順 | 内容 | キーワード |
---|---|---|
- | OSをインストールする(本記事の対象外) | |
1 | ネットワークを構築する | IPマスカレード, SSH |
2 | ファイルを共有する(マウントを行う) | NFS |
3 | mpirun を実行する |
1. ネットワークを構築する
(a) ネットワークで用いるIPアドレス範囲を定める
IPアドレスを決めます. プライベートIPアドレスとして自由に使用できる範囲は表の通りです.
クラス | IPアドレス範囲 |
---|---|
A | 10.0.0.0 ~ 10.255.255.255 |
B | 172.16.0.0 ~ 172.31.255.255 |
C | 192.168.0.0 ~ 192.168.255.255 |
本記事のネットワークの構成を図2に示します. 今回は8台のAlmaLinuxを用いて, 並列計算を行います. ホスト名をALMA01 ~ ALMA08とし, 図のように「IPアドレス/サブネットマスク」を定めました. このとき, ネットワークアドレス, およびブロードキャストアドレスはホストに割り当てられないので注意してください. (今回の割り当てでは, 最大で254台の分散システムを構築することができますね!)
以降では, 全体の通信を制御するマスターノード(1台)と, 指示を受けてデータを送信するスレーブノード(複数)という用語を用います.
図2 ネットワークの構成
ネットワークの構成が決まったら, 各ノードでネットワーク設定を開き, IPアドレス, サブネットマスク, ゲートウェイ の設定を行います. スレーブノードのゲートウェイはマスターノードのIPアドレス(今回の場合は192.168.1.1)です.
その後, 今回は小規模の閉じたネットワークであるため, 全ノードで/etc/hosts
ファイルを編集して, 名前解決を行います.
; 次の行を追加する
192.168.1.1 ALMA01 ; IPアドレス ホスト名
192.168.1.2 ALMA02
...
192.168.1.8 ALMA08
ヒント
OSを1台ずつインストールする場合は, ここまで進めましょう.
以降では, SSH (Secure Shell) を用いてスレーブノードに接続し, 作業を行います.
SSHがインストールされていない場合は, 1-(c) の一行目を実行してください.
図2を参考にしてハブと各ノードをLANで接続し, 物理的な環境を構築 します.
ping
コマンドを利用して, 互いに認識できるかどうかを確認してください.
$ ping ALMA08
あとで必要になるため, マスターノードでifconfig
コマンドを実行し, ネットワークインターフェース名(ここではeno1
とeno2
)を確認しておきます.
$ /sbin/ifconfig
eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx broadcast xxx.xxx.xxx.xxx
inet6 xxxx::xxxx:xxxx:xxxx:xxxx prefixlen 64 scopeid 0x20<link>
ether xx:xx:xx:xx:xx:xx txqueuelen 1000 (Ethernet)
RX packets 160418 bytes 19653083 (18.7 MiB)
RX errors 0 dropped 94720 overruns 0 frame 0
TX packets 3332 bytes 1228201 (1.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 16
eno2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255
...
(b) ファイアウォールでIPマスカレードを設定する
スレーブノードからインターネットに接続するには, マスターノードにルーターの働きをさせる必要があります. Linuxでは, プライベートIPアドレスをグローバルIPアドレスに変換する技術のことを IPマスカレード といいます.
インターネットにつながったeno1
とプライベートネットワークにつながったeno2
は独立しています. この2つを接続するために, マスターノードで/etc/sysctl.conf
ファイルを編集し, IPフォワーディングを有効化 します.
; 次の一行を追加する
net.ipv4.ip_forward=1
次のコマンドで設定を反映させます.
# sysctl -p
net.ipv4.ip_forward = 1
設定が反映されているかどうかを確認します.
$ cat /proc/sys/net/ipv4/ip_forward
1
マスターノードでゾーンの設定を行います.
# firewall-cmd --permanent --zone=home --change-interface=eno1
The interface is under control of NetworkManager, setting zone to 'home'.
success
# firewall-cmd --permanent --zone=internal --change-interface=eno2
The interface is under control of NetworkManager, setting zone to 'internal'.
success
ゾーンの設定を確認します.
# firewall-cmd --get-active-zones
home
interface: eno1
internal
interface: eno2
...
マスターノードで IPマスカレードを有効化 し, ファイアウォールの永続設定を反映させます.
# firewall-cmd --permanent --zone=home --add-masquerade
success
# firewall-cmd --reload
success
それぞれのゾーン(home
とinternal
)で設定が反映されているかどうかを確認します. activeの状態かどうか, interfaces, masqueradeの設定を確認してください.
# firewall-cmd --list-all --zone=home
home (active)
target: default
icmp-block-inversion: no
interfaces: eno1
sources:
services: cockpit dhcpv6-client mdns samba-client ssh
ports:
protocols:
forward: no
masquerade: yes
forward-ports:
source-ports:
icmp-blocks:
rich rules:
# firewall-cmd --list-all --zone=internal
internal (active)
target: default
icmp-block-inversion: no
interfaces: eno2
sources:
services: cockpit dhcpv6-client mdns samba-client ssh
ports:
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
以上で, スレーブノードからマスターノードを経由してインターネットへ接続できるようになります. しかし, このままではMPIを実行することができません. そこで, マスターノードで以下のリッチルールを追加し, 反映させます. 長いので\
を用いて改行していますが, \
なしの1行で実行してください.
# firewall-cmd --permanent --zone=internal \
--add-rich-rule='rule family="ipv4" source address="192.168.1.2" accept'
success
...
# firewall-cmd --permanent --zone=internal \
--add-rich-rule='rule family="ipv4" source address="192.168.1.8" accept'
success
# firewall-cmd --reload
success
次のコマンドで設定が反映されているかどうかを確認します.
# firewall-cmd --list-rich-rules --zone=internal
rule family="ipv4" source address="192.168.1.7" accept
rule family="ipv4" source address="192.168.1.3" accept
...
(c) パスフレーズなしのSSHで接続する
MPIを用いて各ノードを通信させるとき, SSHが利用されます. 通常, SSHを利用する際にはパスワードを設定しますが, 並列処理による通信のたびにパスワードを入力することは現実的ではありません. そのため, パスフレーズなしの公開鍵認証を利用する必要があります.
SSHでは, クライアント(SSHで接続する)側とサーバー(SSHで接続される)側にわかれます. マスターノードにはクライアント, スレーブノードにはサーバーの設定を行います. (本記事では, マスターノードにサーバーの設定も行っています. )
全ノードでSSHに必要なパッケージをインストールします.
# dnf install -y openssh openssh-server
マスターノードは追加で必要なパッケージをインストールします.
# dnf install -y openssh-clients
全ノードで/etc/ssh/sshd_config
ファイルを編集し, SSHの 公開鍵認証を有効化 します.
; 次の行の#を外す
#PubkeyAuthentication yes -> PubkeyAuthentication yes
全ノードでsshdを再起動し, 設定を反映させます.
# systemctl restart sshd
全ノードで同じユーザー名(ここではuser
)のアカウントを作成し, 任意のパスワードを設定します.
# useradd user
# passwd user
/* 任意のパスワードを入力する */
マスターノードでユーザーを切り替えます.
# su user
マスターノードでパスフレーズなしの鍵を作成します.
$ ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
(またはssh-keygen
コマンドを実行し, 何も書かずにEnterを押すと同じ出力が得られます. )
これより, 秘密鍵 (id_rsa) と 公開鍵 (id_rsa.pub) が作成されました.
マスターノードで生成した公開鍵をスレーブノードに転送し, authorized_keysに書き込みます. 次のコマンドを実行してください.(実行するときに接続先のパスワードを求められます. )
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@ALMA02 # ユーザー名@接続先
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@ALMA03
...
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@ALMA08
注意
公開鍵を渡す方法は他にもあります. このとき, ディレクトリやファイルの権限が正しくないとMPIの実行時にパスワードを聞かれることがあります.
必ず, chmod 600 authorized_keys, およびchmod 700 .ssh となっていることを確認してください.(上記の意味が理解できない場合はssh-copy-idコマンドを用いて自動登録した方がよいでしょう. )
ssh
コマンドを用いてパスフレーズなしで入れるかどうかを確認します.
$ ssh ALMA08 -l user -Y
2. ファイルを共有する(マウントを行う)
(a) NFSを設定する
MPIを実行するためには, 全ノードでディレクトリやファイルが共有されている必要があります. そこで, NFS(Network File System)を用いて, マスターノードのディレクトリを共有してあげましょう. このように, 外部のファイルシステムにアクセスできるようにすることを マウント といいます.
全ノードでNFSに必要なパッケージをインストールします.
# dnf install nfs-utils
マスターノードで共有するディレクトリを作成します. 今回は/DATA
ディレクトリとします.
# mkdir /DATA
user
が利用できるようにディレクトリの所有者を変更(またはアクセス権限を付与)します.
# chown user:user /DATA
マスターノードで/etc/exports
ファイルを編集し, 公開するディレクトリの設定を行います. アクセスできる範囲には, ネットワークアドレスを指定しています. また, *(rw,async)
とすることでアドレスの制限なしにも設定できます.
; 次の1行を追加する
/DATA 192.168.1.0/24(rw,async) ; アクセスできる範囲(読み書き可, 非同期書き込み)
次のコマンドで設定を反映させます.
# /sbin/exports -ra
設定が反映されているかどうかを確認します.
# /sbin/exports -v
/DATA 192.168.1.0/24(async,wdelay,hide, ...
マスターノードでファイアウォールにNFSの許可を与えます. (1-(b)で追加したリッチルールにより, 許可を与えなくても動作します. )
# firewall-cmd --permanent --zone=internal --add-service=nfs
success
# firewall-cmd --reload
success
マスターノードでNFSサーバを起動させます. 自動起動も有効化しておきましょう.
# systemctl start nfs-server # NFSサーバの起動
# systemctl enable nfs-server # 自動起動の有効
(b) マウントする
あとは, 各スレーブノードで設定を行い, マウントしていきます.
スレーブノードでマウントポイントを作成します.
# mkdir /DATA
スレーブノードで/etc/fstab
ファイルを編集し, マウントの設定を行います.
; 次の1行を追加する
ALMA01:/DATA /DATA nfs defaults,hard,intr,noauto 0 0
準備が整いました. マウントを実行します.(エラーが出なければOKです. )
# mount /DATA
3. mpirun を実行する
それでは, 実際に並列計算を行ってみます.
今回はmpich
を使用します. 全ノードでインストールしてください.
# dnf install -y mpich mpich-devel
マスターノードでmpich
のパスを通します. パスの永続化については, 使用しているシェルに合わせて各自で行ってください. (次のコマンドは, 一時的にパスを通す例です. )
$ export PATH=$PATH:/usr/lib64/mpich/bin # bashの場合
$ set path=($path /usr/lib64/mpich/bin) # tcshの場合
以上で, マウント済みのディレクトリ内でMPIが実行可能です. cd
コマンドで移動します.
$ cd /DATA
例えば, /DATA
ディレクトリ内に, 次のような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;
}
実行には, ホストファイルが必要になります. /DATA
ディレクトリ内にhosts
ファイルを準備してください. ファイル名は自由です.
; 計算資源として利用するノードを記述する
ALMA01
ALMA02
...
ALMA08
mpicc
コマンドを用いて, コンパイルします.
$ mpicc smaple.c
実行してみましょう. ファイアウォールやSSHの設定上, マスターノードでのみ実行可能です.
$ mpirun --hostfile hosts -n 8 ./a.out
Hello world from ALMA01! Rank is 0.
Hello world from ALMA07! Rank is 6.
Hello world from ALMA06! Rank is 5.
Hello world from ALMA05! Rank is 4.
Hello world from ALMA04! Rank is 3.
Hello world from ALMA03! Rank is 2.
Hello world from ALMA08! Rank is 7.
Hello world from ALMA02! Rank is 1.
/DATA/hosts
ファイルの内容や, -n 8
の値を変化させて実行してみましょう.
4. 応用例
分散システムを用いて数値計算を行うとき, 境界データのやり取りが必要になるため, n対nのデータ通信を行いながらMPIを進める必要があります. ここでは, 上記環境に加えて必要な手順を示します.
(a) プライベートネットワーク内のセキュリティを無効化する
閉じたネットワークであるため, 面倒な設定を省いてセキュリティを無効化します.(または, 1-(b)を参考にリッチルールを追加することで解決します. )
スレーブノードのファイアウォールを停止します. 自動起動も無効化しておきましょう.
# systemctl stop firewalld # ファイアウォールの停止
# systemctl disable firewalld # 自動起動の無効
(b) 互いにパスフレーズなしのSSHで接続する
n対nのMPI通信では, 各ノードが互いにパスフレーズなしのSSHで接続できる必要があります. そこで, 1-(c)を参考に, どのノード間でも移動できるように鍵を渡します.
スレーブノードで以下のコマンドを実行します. なお, 接続先に自分自身を含める必要はありません.
$ ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa # 鍵の生成
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@ALMA01 # ユーザー名@接続先
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@ALMA02
...
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@ALMA08
以上で, n対nのデータ通信のほか, どのノードからでも実行可能になります.
まとめ
本記事では, 複数台のAlmaLinuxを用いて, 並列計算を行うための環境構築を行いました. 本記事がネットワーク技術のハンズオン教材となれば幸いです.
最後まで読んでいただきありがとうございました!
参考文献