コア数が多いマシンが手に入ったのでslurmで計算クラスターにする

slurmはオープンソースのジョブスケジューラーだ。

これは何かというと、例えば、俺は今から1000種類の条件でシミュレーションを投げるぞ! ウオオ という時に、サーバーにログインして、シェルのfor文で1000個のバッチスクリプトを登録し、そのままログアウトして家で寝ていると勝手に順次実行されていくという便利ソフトウェアだ。

普通こういうのはサーバーラックに挿す系の大型クラスターとか、複数台のデスクトップマシンをネットワークで繋いだものとかで色々大変な設定をして使うものだが、今回は全てが面倒に感じたので(あと手元にコントロールノードに使えそうないい感じのマシンがなかったので)、1台をコントロールノードかつ計算ノードとして完結させることにした。共有ストレージとか必要なくなる(自明に共有されている)ので多分色々楽。

それくらいの気持ちなので割と適当にやっていきます。OSはUbuntu 18.04。やりながら書いてるわけではない(history見て要約して書いてる)ので、何か必須手順忘れてるかも。その時は頑張って下さい。

まず、slurmのコントロールノード用デーモンslurmctldと計算ノード用デーモンslurmdをaptで入れる。Ubuntu 18で入るやつは17.11.2でちょっと古い(これを書いている時点で最新版は20.02.0)が見なかったことにする。

$ sudo apt install slurmctld slurmd

最新版を使いたい場合は本家からダウンロードする。

www.schedmd.com

aptを使えば依存関係はもちろんslurmユーザーの登録とsystemctlへのserviceファイルの配置まで済んでいるので楽。あと必要なのはクラスターの構成を書くためのslurm.confだ。

これを簡単に作るためのインターフェースがhtmlファイルとして提供されているので、これを開いて適当に書いていく。CPUとかメモリはあるだけ書く。コントロールノードと計算ノードのIPと名前を同じにしておく。Resource SelectionをCons_resにして、あとは全部デフォルト。

$ firefox  /usr/share/doc/slurmctld/slurm-wlm-configurator.easy.html

出てきたファイル(slurm.conf)を/etc/slurm-llnlにコピーして、所有者をslurm:slurmにする。

$ sudo cp slurm.conf /etc/slurm-llnl
$ sudo chown slurm:slurm /etc/slurm-llnl/slurm.conf

多分これで大丈夫なので、必要なやつを全部systemctlに立ち上げてもらうために再起動。手動でsystemctlからやったほうが良いのかも知れないけど面倒だったので再起動した。

再起動したら、slurmdslurmctldがちゃんと動いているか確認。その後sinfoで何か出力されるか確認。おかしくなってたらエラーメッセージ見てググって下さい。

$ sudo systemctl status slurmd
$ sudo systemctl status slurmctld
$ sinfo

ちゃんと動いていたら何かテストスクリプトを投げてみる。

#!/bin/bash
echo "sleeping..."
sleep 10
echo "done!"
$ sbatch test.sh
$ squeue
(ジョブが流れているという表示)
$ ls
slurm-1.out test.sh

ちゃんと動いてるようなら終わりです。


(3/11追記)

スルーしていたslurm.confの中身だが、hyperthreadingを使って全スレッドを動かすための設定はハマりがちっぽいので書いておこうと思う。

まず、スケジューリングの際に各ノードで複数のジョブが走ることを許す必要がある。slurm.confのスケジューリング関係の設定を以下のようにする。

# ...
# SCHEDULING
FastSchedule=1               # これはデフォルト
SchedulerType=sched/backfill # これもデフォルト
SelectType=select/cons_res   # これにする
SelectTypeParameters=CR_CPU # またはCR_Core
# ...

ハイパースレッディングを使わず、単に1ノードでコア数分の複数ジョブを動かす場合はこれだけでよい。

続いて、Partitionでオーバーサブスクライブを許す(複数の異なるジョブがハイパースレッディングでコアを共有することを許す)。デフォルトで生成したslurm.confには、最後に以下のような行があるはずだ。ここでOverSubscribe=FORCEにする。YESだと一つのジョブ内ではハイパースレッディングができても、異なるジョブ同士はコアを共有しないようだ。

PartitionName=<name> Nodes=<yournode> OverSubscribe=FORCE Default=YES MaxTime=INFINITE State=UP

これで動くはずだ。

作業上の注意点として、slurm.confを書き換える際にはsystemctl stop slurmd slurmctldでslurmを落としておこう。書き換え終わったら、systemctl start slurmd slurmctldで起動して動くかどうか確かめる。

また、上で書いた環境だとslurm.confの中にtypoなどがある状態でslurmctldstartすると、slurmctldsystemctl stop slurmctldで止まってくれなくなることがある。この場合、落ち着いてslurm.confを動く状態のものに戻してからsystemctl restart slurmctldをすると正しく動き始め、かつ正しく止められるようになる。

今適当な値を入れて作りなおしたが、概ねこんな感じになると思う。ノードの名前やIP、リソースの量は入れ替えないといけないが、それをするくらいならslurmのeasy-configuratorを使ったほうが速い。確認用ということで。

# slurm.conf file generated by configurator easy.html.
# Put this file on all nodes of your cluster.
# See the slurm.conf man page for more information.
#
ControlMachine=linux0
ControlAddr=10.1.1.100
#
#MailProg=/bin/mail
MpiDefault=none
#MpiParams=ports=#-#
ProctrackType=proctrack/pgid
ReturnToService=1
SlurmctldPidFile=/var/run/slurm-llnl/slurmctld.pid
#SlurmctldPort=6817
SlurmdPidFile=/var/run/slurm-llnl/slurmd.pid
#SlurmdPort=6818
SlurmdSpoolDir=/var/lib/slurm-llnl/slurmd
SlurmUser=slurm
#SlurmdUser=root
StateSaveLocation=/var/lib/slurm-llnl/slurmctld
SwitchType=switch/none
TaskPlugin=task/none
#
#
# TIMERS
#KillWait=30
#MinJobAge=300
#SlurmctldTimeout=120
#SlurmdTimeout=300
#
#
# SCHEDULING
FastSchedule=1
SchedulerType=sched/backfill
#SchedulerPort=7321
SelectType=select/cons_res
SelectTypeParameters=CR_CPU
#
#
# LOGGING AND ACCOUNTING
AccountingStorageType=accounting_storage/none
ClusterName=cluster
#JobAcctGatherFrequency=30
JobAcctGatherType=jobacct_gather/none
#SlurmctldDebug=3
SlurmctldLogFile=/var/log/slurm-llnl/slurmctld.log
#SlurmdDebug=3
SlurmdLogFile=/var/log/slurm-llnl/slurmd.log
#
#
# COMPUTE NODES
NodeName=linux0 NodeAddr=10.1.1.100 RealMemory=32031 Sockets=1 CoresPerSocket=6 ThreadsPerCore=2 State=UNKNOWN
PartitionName=debug Nodes=linux0 Default=YES MaxTime=INFINITE State=UP

どうやらRealMemoryにslurmから見えている量以上の値を入れると起動した時のstateがdrainになって、scontrolから状態をidleにしないとジョブが入らないらしい。 計算ノードでslurmd -Cというコマンドを流すとメモリの量を確認できるので、適当な値を書くよりはこの値をコピペするのが無難だろう。

(追記) systemctl status slurmctldしたときに「pidファイルがない」みたいなエラーで落ちている場合、slurm.confslurmctld.service*.pidファイルのpathが異なっている可能性がある。それを確認し、適切に揃えてみてほしい。

(追記2) 訂正:SelectTypeParameter -> SelectTypeParameters