まえがき
端的にいうと ハードウェア以外は「0円」 で 「俺様の専用機」 を作る、というつもりのものです。 基本的な仕様や構成は以下のような感じ。
eth1 +------+ eth0 ---------------+ mck1 +--------------+ /home/kameyama 外向きの +------+ 192.168.10.1 | IP アドレス | +------+ eth0 | | mck2 +--------------+ /home/kameyama +------+ 192.168.10.2 | -> 192.168.10.1:/home/kameyama | +------+ eth0 | | mck3 +--------------+ /home/kameyama +------+ 192.168.10.3 | -> 192.168.10.1:/home/kameyama | +------+ eth0 | | mck4 +--------------+ /home/kameyama +------+ 192.168.10.4 -> 192.168.10.1:/home/kameyama
- Debian GNU/Linux 10 (buster) の amd64 を使う。
- コンパイラは GNU のものを使う。 ライセンスがどうとかの問題を考えたくなくなったので、Intel-free な計算機環境を作りたいが故。
- 行列計算ライブラリとして、OpenBLAS (+ これに付随する LAPACK) を利用。 亀山が触った感じでは、Debian 標準の BLAS/LAPACK とか ATLAS よりも OpenBLAS が速い (もしかして Intel® MKL とさほど遜色ないくらいなのかも)。 ただし wheezy 以前の OpenBLAS には LAPACK をがついていないようで。
- ノード間並列は OpenMPI で実現させる。 各 PC のマザーボードについている LAN の口 (eth0) を使って、クラスタ内部のネットワークを構成する。 ただし「親機」となる1台 (mck1) は、外向きのLANの口 (eth1) も用意する。
- 記憶領域 (ハードディスク) は「親機」となる1台 (mck1) のみに持たせる。 他の「子機」はディスクレスとし、tftp によりネットワーク経由でブートし、ホームディレクトリやシステムファイルも「親機」から NFS でマウントする。
- アカウントは「親機」と「子機」で個別に用意する (わざわざ NIS を使うのも面倒だし)。 ただし「子機」のルートファイルシステムは NFS で共用しているので、「子機」どうしではアカウント情報の使い回しが効いている。
- キューイングシステムのような、コッたものは使わない (ロードの監視なんかもしない)。
親機に Debian GNU/Linux をインストールする
こういう時に亀山がとる手順は以下のような感じ。
- インストーラを用意する。
http://www.debian.org/CD/ から必要なもの (例えば amd64 用の netinst CD イメージ) をダウンロードしてくる。 その後、例えば以下のような手順で、ダウンロードしてきた CD イメージを (中身が潰れても惜しくない) USB メモリに書き込む。
# cat debian-10.0.0-amd64-netinst.iso > USBメモリのデバイス名 (/dev/sdb とか)
当然ながら、デバイス名の指定は慎重に。 間違った指定をすると、最悪の場合は内蔵ハードディスクを潰してしまうこともありえます。 - 最小限のシステムのインストール。
上で作った USB メモリから起動すると、インストーラが立ち上がり、インストールへと進む。 亀山がやる場合、この時点では (ほとんど) 何のパッケージもインストールしない。 tasksel の選択画面が起動しても、全て解除して進める。 終わったら再起動。
- 必要なパッケージのインストール。
再起動したら、追加でパッケージのインストールを進める。 だいたいは以下をインストール指定しておけば、他に必要なパッケージも「いもづる」式にインストールされる。
# apt install g++ gfortran libopenblas-dev libopenmpi-dev make openssh-server openmpi-bin
その他、亀山は (自分の好みの問題で) 以下もインストール指定する。
# apt install bzip2 less sudo tcsh xauth
- ネットワークの設定。
/etc/network/interfaces を以下のような内容に修正。 内向き LAN (この例では eth0) の設定はこの通りだが、外向き LAN (この例では eth1) の設定はネットワーク環境によって異なるので、それぞれ正しいものに書き換えるべし。 また、Debian 9 (stretch) 以降のバージョンでは、ネットワークインターフェースがこれとは違った新しい規則で命名されることにも注意。
auto lo iface lo inet loopback allow-hotplug eth0 iface eth0 inet static address 192.168.10.1 netmask 255.255.255.0 network 192.168.10.0 broadcast 192.168.10.255 gateway 0.0.0.0 allow-hotplug eth1 iface eth1 inet static address ???.???.???.??? netmask ???.???.???.??? network ???.???.???.??? broadcast ???.???.???.??? gateway ???.???.???.???
子機をネットワークでブートするための準備
ここの設定も全て親機上で行う。 親機 の /srv/nfsroot に子機用のシステムを構築する。 また子機は親機から DHCP で IP アドレスを受け取り、親機内の /srv/tftp 以下にあるカーネルイメージを用いて PXE でブートさせる。
- 必要なパッケージのインストール
# apt install tftpd-hpa pxelinux initramfs-tools isc-dhcp-server nfs-kernel-server ntp
- tftpd と PXE の設定。
tftpd の設定ファイル /etc/default/tftpd-hpa はデフォルトのままで使う。
- /srv/tftp/pxelinux.cfg/default を以下の内容で作成。
当然ながら NFS 親機の IP アドレスは内向きのものを。
またルートファイルシステムを read-only にした場合 (安全を期そうとしたのだが) には、子機からうまく NFS マウントできなかった。
LABEL linux DEFAULT vmlinuz root=/dev/nfs initrd=initrd.img nfsroot=192.168.10.1:/srv/nfsroot ip=dhcp init=/bin/systemd rw quiet
- PXE ブートに必要なファイルの準備。
上記の設定により、子機のブート用のカーネルイメージは vmlinuz という名前にしておくことに注意。
# cp /boot/vmlinuz* /srv/tftp/vmlinuz # cp /usr/lib/syslinux/modules/bios/ldlinux.c32 /usr/lib/PXELINUX/pxelinux.0 /srv/tftp
- ブートのための RAM ディスクを作る。
この際に注意すべきであろうことは、親機用の initrd を作るためのディレクトリ /etc/initramfs-tools/ とは別の作業用ディレクトリを用意して作業すること (さもなくば、親機の initrd が書き換えられてしまいます)。
以下の例では、/srv/initramfs-tools という作業ディレクトリを新たに作って作業することにする。
- 作業ディレクトリを準備する
# mkdir /srv/initramfs-tools # cp -Rp /etc/initramfs-tools/modules /etc/initramfs-tools/scripts /srv/initramfs-tools/
- 子機の RAM ディスク用の設定ファイルとして、/srv/initramfs-tools/initramfs.conf というファイルを作成し、以下の記述を行う。
なお、以下で使用する mkinitramfs コマンドの仕様により、ファイル名は必ず initramfs.conf にしておくこと。
MODULES=netboot BUSYBOX=y KEYMAP=n COMPRESS=gzip DEVICE= NFSROOT=auto BOOT=nfs
- 次のコマンドにより /srv/tftp/initrd.img を作る。
# mkinitramfs -d /srv/initramfs-tools/ -o /srv/tftp/initrd.img
- 作業ディレクトリを準備する
- tftp 用ファイルのパーミッションの設定
# chmod -R 777 /srv/tftp
# systemctl restart tftpd-hpa
- /srv/tftp/pxelinux.cfg/default を以下の内容で作成。
当然ながら NFS 親機の IP アドレスは内向きのものを。
またルートファイルシステムを read-only にした場合 (安全を期そうとしたのだが) には、子機からうまく NFS マウントできなかった。
- DHCP サーバーの設定。
- /etc/dhcp/dhcpd.conf を編集する。
たぶん以下のようなものがあればOKのはず。
filenameは PXE ブート用のブートローダとなるファイル名を指定し、そのファイルを持っているサーバーの IP アドレスを next-server に指定する。
子機の MAC アドレスは事前に調べておきましょう。
例えばどうにかして Linux を起動しておき、/sys/class/net/(インターフェース名)/address の中身をみれば MAC アドレスが分かります。
authoritative; allow booting; allow bootp; subnet 192.168.10.0 netmask 255.255.255.0 { option broadcast-address 192.168.10.255; } filename "pxelinux.0"; host mck02 { next-server 192.168.10.1; hardware ethernet ??:??:??:??; fixed-address 192.168.10.2; option host-name "mck02"; } host mck03 { next-server 192.168.10.1; hardware ethernet ??:??:??:??; fixed-address 192.168.10.3; option host-name "mck03"; } host mck04 { next-server 192.168.10.1; hardware ethernet ??:??:??:??; fixed-address 192.168.10.4; option host-name "mck04"; }
- /etc/default/isc-dhcp-server を編集し、INTERFACESv4= に内向き LAN のインターフェース名 (この例では eth0) を追記。
ただしバージョン 9 (stretch) 以降の Debian を新規でインストールした場合には、ネットワークインターフェースがこれとは違った新しい規則で命名されることに注意。
INTERFACESv4="eth0"
# systemctl restart isc-dhcp-server
- /etc/dhcp/dhcpd.conf を編集する。
たぶん以下のようなものがあればOKのはず。
filenameは PXE ブート用のブートローダとなるファイル名を指定し、そのファイルを持っているサーバーの IP アドレスを next-server に指定する。
子機の MAC アドレスは事前に調べておきましょう。
例えばどうにかして Linux を起動しておき、/sys/class/net/(インターフェース名)/address の中身をみれば MAC アドレスが分かります。
- NFS サーバーの設定。
/etc/exports を編集。
たぶん以下のようなものがあればOKのはず。
/home 192.168.10.0/255.255.255.0(rw,async,no_root_squash,no_subtree_check) /srv/nfsroot 192.168.10.0/255.255.255.0(rw,async,no_root_squash,no_subtree_check)
しかしバージョン10 (buster) の Debian になってからは、これだけだと子機で走らせていたジョブが (たぶん NFS 関係のトラブルで?) 途中で死ぬというトラブルが頻発した。 その対策のために、以下を追加してみるといいかも。- /etc/idmapd.conf の中の以下の行のコメントを外す。
Domain = localdomain
- /etc/default/nfs-common に以下の行を追加する。
NEED_IDMAPD=yes
# systemctl restart nfs-kernel-server
- /etc/idmapd.conf の中の以下の行のコメントを外す。
- NTP サーバーの設定。
/etc/ntp.conf を編集。
たぶん以下のようなものがあればOKのはず。
server your_ntp_server restrict 192.168.10.0 mask 255.255.255.0 nomodify notrap
その後 ntpd の再起動。# systemctl restart ntp
- ホスト名の登録。
/etc/hosts を以下のように編集する。
192.168.10.1 mck1 192.168.10.2 mck2 192.168.10.3 mck3 192.168.10.4 mck4
子機のシステムをインストールする
まずは親機上でシステムを作るところから。 既存のシステムを丸ごと複製する手もあるようだが、ここは Unix/Linux システムからの Debian GNU/Linux のインストール よろしく、debootstrap で作り直す。
- 必要なパッケージのインストール。
# apt install debootstrap
- システムの基本部分をインストール
# mkdir /srv/nfsroot # debootstrap --arch=amd64 buster /srv/nfsroot http://ftp.jp.debian.org/debian
- 子機に必要なパッケージをインストール。
まずはシステムの稼働に必要なものを
# chroot /srv/nfsroot apt install console-setup locales nfs-common ntp openssh-server
続いて、計算ジョブの実行に必要なものを。# chroot /srv/nfsroot apt install g++ gfortran libopenblas-dev libopenmpi-dev make openmpi-bin
その他、亀山は (自分の好みの問題で) 以下もインストール指定する。# chroot /srv/nfsroot apt install bzip2 less sudo tcsh xauth
- /srv/nfsroot/etc/ntp.conf を編集。
たぶん以下のようなものがあればOKのはず。
server 192.168.10.1
また pool を指している行は無効にしておくのがよさげかも。 - /srv/nfsroot/etc/network/interfaces を以下の通り修正。
auto lo iface lo inet loopback iface eth0 inet manual
- /srv/nfsroot/etc/fstab を以下の通り修正。
ここで /var/run を tmpfs とかにマウントしてしまうと、systemd が困ってしまうので注意。
また jessie の時と違って、/var/lock を tmpfs とかにマウントするのもダメらしい。
なお root ファイルシステムの指定は起動時になされているので、ここで書かなくても大丈夫のはず。
proc /proc proc defaults 0 0 tmpfs /tmp tmpfs defaults 0 0 tmpfs /var/tmp tmpfs defaults 0 0 tmpfs /var/log tmpfs defaults 0 0 192.168.10.1:/home /home nfs defaults 0 0
- NFS 関係の設定。
念のため子機にも親機と同じ設定をば。
- /etc/idmapd.conf の中の以下の行のコメントを外す。
Domain = localdomain
- /etc/default/nfs-common に以下の行を追加する。
NEED_IDMAPD=yes
- /etc/idmapd.conf の中の以下の行のコメントを外す。
- 子機に /etc/hostname はいらないので消す。
# rm /srv/nfsroot/etc/hostname
- 子機にも /etc/hosts はいるので、親機からもらってくる。
# cp -p /etc/hosts /srv/nfsroot/etc
- タイムゾーンの設定。
正式には chroot環境下で timedatectl set-timezone Asia/Tokyo とするのだろうが、/proc/ がマウントされていないと動かないようなので。
また /srv/nfsroot/etc/localtime は標準状態では /usr/share/zoneinfo/Etc/UTC へのシンボリックリンクになっているので、単純にコピーするのは危ないかも。
# ln -sf /usr/share/zoneinfo/Asia/Tokyo /srv/nfsroot/etc/localtime
- アカウントの調整。
root のパスワードをつけたり、一般ユーザーのアカウントを作ったり。
# chroot /srv/nfsroot passwd
SSHの設定
クラスタを構成する全てのPCに、パスワードなしでログインできるようにしたい。
- 鍵の作成。
以下の手順で、指示通りに進めていくと、秘密鍵 ~/.ssh/id_rsa と公開鍵 ~/.ssh/id_rsa.pub ができる。
$ ssh-keygen -t rsa
- 公開鍵の登録。
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys $ chmod 600 ~/.ssh/authorized_keys
- クラスタ内の他のPCに、パスワードなしで ssh でログインできることを確認。
OpenMPI の動作確認
例えばこんな Fortran プログラムはどうでしょう。
program psample implicit none include 'mpif.h' character(len=MPI_MAX_PROCESSOR_NAME) :: hostname integer :: myrank,nprocs,ierr,hostname_len call mpi_init(ierr) call mpi_comm_size(mpi_comm_world,nprocs,ierr) call mpi_comm_rank(mpi_comm_world,myrank,ierr) call mpi_get_processor_name(hostname,hostname_len,ierr) write(*,'("Hello, I am rank",i4," out of",i4," running on ",a,".")') & myrank,nprocs,trim(hostname) call mpi_finalize(ierr) end program psampleこれを psample.f90 として作成し、
$ mpif90 psample.f90 $ mpirun -np 2 ./a.out $ mpirun -np 4 -host mck1,mck2,mck3,mck4 ./a.outなどとして、しかるべく動けばOK。