LinuxにTorque 6.1.3を入れてqsubできるシステムを構築する

研究室でできる自作計算機クラスタの作り方。

# Introduction: 計算機クラスタの構築

当研究室では高性能なCPU/GPUを搭載したマシンを数台所有していますが、研究室の学生が計算したいときにアイドル状態の計算機に計算を自動的に割り振るためにジョブキューイングシステムを導入しました。
ちなみに計算機が1台しかない場合でも、キューイングシステムを作っておくことで、前の計算が終わったと同時に次の計算が実行されるようになったりと便利な面があります。

## 環境

- OS: **Ubuntu 22.04** or **Rocky Linux 8.8** or **Almalinux 8**。たぶんRocky/Alma linux 9以降でも動作する。
- Network: Gigabit EthernetとLANケーブル cat.5e以上が望ましい
- Job queuing system: Torque 6.1.3
- **CentOS, Rocky, Almaの場合は、SELinuxをdisabledにしておいた方がおいたほうが良い**（※未検証ですが私はいつもdisabledにしています）

Torque 6.1.3は、（以下の要領でパッチを当てさえすれば）最近のLinux系であればある程度どれでも動くようになっていると思います。

## 登場する計算機

以降の説明では管理ノード（ヘッドノードとも）と計算ノードの2種類が登場します。管理ノード・計算ノードの名前とIPアドレスは以下の通りとします。

- 管理ノード
  - **keisan_master**: 192.168.1.1
- 計算ノード
  - **keisan1**: 192.168.1.2
  - **keisan2**: 192.168.1.3

これらは後ほどの`/etc/hosts`, `/etc/hosts.equiv`設定時に関わってきます。管理ノードの名前や計算ノードの名前・IPアドレスは自由に設定できます。この辺の設定は https://www.server-world.info/query?os=Rocky_Linux_8&p=initial_conf&f=3 などを読んでください。

また、これらの計算機はすべてNISによるアカウント情報の共有、NFS（autofsなどでも可）によるファイルの共有が常時行われているものと仮定します。これにより、管理ノード・計算ノードのいずれにもユーザーアカウントが共有され、ユーザーのファイルが常時同じになるよう更新されます。こうすることで、あるユーザーが管理ノードでジョブを実行するように`qsub`コマンドを入れると、管理ノードは空いている計算ノードに計算を委託する司令を与え、計算ノードはそのユーザー名を用いて計算を行います。そして計算の結果ファイルは自動的に全ノードにリアルタイムで共有されることになります。

NIS, NFSを用いたこれらの計算機の設定はここでは触れません。https://www.server-world.info/query?os=Rocky_Linux_8&p=nis&f=1 とか https://www.server-world.info/query?os=Rocky_Linux_8&p=nfs&f=1 などを参考にしてください。

# Torque 6.1.3

参考にしたログ: https://kojipkgs.fedoraproject.org//packages/torque/6.1.3/8.el9/data/logs/x86_64/build.log

まず`sudo su -`をしてrootユーザーになっておきます。先に必要なパッケージをインストールしておきます。

```bash
# Ubuntuの場合
apt -y install autoconf automake libtool libboost-all-dev libjsoncpp-dev tcl tk tcl-dev tk-dev
# Rocky Linux 8, AlmaLinux 8, RedHat系OSの場合
dnf -y install epel-release
dnf -y install libtool openssl-devel libxml2-devel boost-devel jsoncpp jsoncpp-devel gcc gcc-c++ git
```

次に、fedoraprojectのページからtorqueのソースコードとそれらのパッチをダウンロードしてパッチを当てつつ、`./configure`でインストール準備を開始します。

ところで、以下に書くようなソースコードからのインストールをしなくても`dnf`や`yum`を探せばtorque 6.1.3（ver.9の場合）またはtorque 4.2.10（ver.8の場合）をインストールする事もできますが（参考：https://rpmfind.net/linux/rpm2html/search.php?query=torque(x86-64) ）、これだとnumaコア設定のものしか使えないのが気持ち悪いのでやめておきました。

## Torque本体のインストール

**管理ノード**と**計算ノード**すべてにTorque 6.1.3をインストールします。**Rocky, Almalinux 8の場合、デフォルトのGCCコンパイラのバージョンが8ですが、Torque 6.1.3のコンパイルに対してgcc ver.8ではコンパイルに失敗します**。このため、必ずgcc-9またはgcc-11を使うように設定してください。Ubuntu 22.04やRocky, Almaの9ではデフォルトGCCで大丈夫だと思います。

gccのバージョンはターミナルから`gcc --version`で確認できます。Rocky, Almalinux 8の場合は以下のようにしてgcc-11を入れるのがおすすめです。

```bash
# gcc-toolset-11をインストール
$ dnf install gcc-toolset-11
$ scl enable gcc-toolset-11 bash # または scl enable gcc-toolset-11 zsh
$ which gcc
$ gcc --version # バージョン確認
gcc (GCC) 11.2.1 20220127 (Red Hat 11.2.1-9)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

GCCのバージョンを確認したら、続いてTorqueをインストールします。
以降、`$`で始まるコマンドのみ入力してください。

```bash
# ソースコードのダウンロード
$ wget https://src.fedoraproject.org/lookaside/pkgs/torque/torque-6.1.3.tar.xz/sha512/9cace317a6331c8a043a11115161cd9a852623ce0ccbc6d745fcc16963a8fe63ed8e4c2d0922a9bd950a2aa6273246340ea32488c88d2e9e476067c3429f1b85/torque-6.1.3.tar.xz
# 解凍
$ tar Jxvf torque-6.1.3.tar.xz
$ cd torque
# パッチファイルのダウンロード
# Patch #1 (torque-munge-size.patch)
$ wget https://src.fedoraproject.org/rpms/torque/raw/epel9/f/torque-munge-size.patch
# Patch #2 (torque-6.1.3-port-args.patch):
$ wget https://src.fedoraproject.org/rpms/torque/raw/epel9/f/torque-6.1.3-port-args.patch
# Patch #3は存在していない様子
# Patch #4 (torque-6.1.3-system-jsoncpp.patch):
$ wget https://src.fedoraproject.org/rpms/torque/raw/epel9/f/torque-6.1.3-system-jsoncpp.patch
# Patch #5 (torque-6.1.3-bool-fix.patch):
$ wget https://src.fedoraproject.org/rpms/torque/raw/epel9/f/torque-6.1.3-bool-fix.patch
# Patch #6 (torque-6.1.3-autoconf-fixes.patch):
$ wget https://src.fedoraproject.org/rpms/torque/raw/epel9/f/torque-6.1.3-autoconf-fixes.patch
# 以下パッチを当てていく
$ patch --no-backup-if-mismatch -p1 -b --suffix .munge-size --fuzz=0 < torque-munge-size.patch
# patching file src/include/libpbs.hと表示が出る
$ patch --no-backup-if-mismatch -p1 -b --suffix .port-args --fuzz=0 < torque-6.1.3-port-args.patch
# patching file contrib/init.d/pbs_mom.in
$ patch --no-backup-if-mismatch -p1 -b --suffix .system-jsoncpp --fuzz=0 < torque-6.1.3-system-jsoncpp.patch
# patching file configure.ac
# patching file src/include/machine.hpp
# patching file src/include/Makefile.am
# patching file src/include/pbs_job.h
# patching file src/lib/Libutils/Makefile.am
# patching file src/resmom/catch_child.c
# patching file src/resmom/mom_server.c
# patching file src/resmom/parse_config.c
# patching file src/resmom/prolog.c
# patching file src/resmom/requests.c
# patching file src/server/job.cpp
# patching file src/server/node_manager.c
# patching file src/server/pbsnode.cpp
# patching file src/test/catch_child/scaffolding.c
# patching file src/test/job/test_uut.c
# patching file src/test/machine/scaffolding.c
# patching file src/test/machine/test_uut.c
# patching file src/test/node_manager/scaffolding.c
# patching file src/test/node_manager/test_uut.c
# patching file src/test/numa_chip/scaffolding.c
# patching file src/test/numa_chip/test_uut.c
# patching file src/test/numa_socket/scaffolding.c
# patching file src/test/numa_socket/test_uut.c
# patching file src/test/parse_config/scaffolding.c
# patching file src/test/parse_config/test_uut.c
# patching file src/test/prolog/scaffolding.c
$ patch --no-backup-if-mismatch -p1 -b --suffix .bool-fix --fuzz=0 < torque-6.1.3-bool-fix.patch
# patching file src/drmaa/src/compat.h
# patching file src/include/pbs_ifl.h
$ patch --no-backup-if-mismatch -p1 -b --suffix .cleanup --fuzz=0 < torque-6.1.3-autoconf-fixes.patch
# patching file acinclude.m4
# patching file buildutils/ac_c_bigendian_cross.m4
# patching file buildutils/acx_pthread.m4
# patching file buildutils/ax_cflags_gcc_option.m4
# patching file buildutils/tac_tcltk.m4
# patching file buildutils/tcl.m4
# patching file configure.ac

$ autoreconf -ifv

$ ./configure CFLAGS="-O2 -Wno-deprecated-declarations -Wno-cast-function-type -fpermissive" \
CXXFLAGS="-O2 -Wno-deprecated-declarations -Wno-cast-function-type -fpermissive" \
--disable-static --with-tcp-retry-limit=2 --without-debug \
--with-default-server=localhost --with-tcl \
--with-tk --with-rcp=/usr/bin/rcp --disable-static --prefix=/usr/local

# configureでエラーが無ければインストール開始
$ make -j8 CC=g++ && make install
```

エラーなく`make install`が終わればOKです。

## Torque packagesのインストール

Torqueの`make install`が終わったら、そのままのディレクトリで`make packages`を入力して必要なパッケージをインストールします。

```bash
$ make packages

Building packages from /root/torque/tpackages

$ rm -rf /root/torque/tpackages

$ mkdir /root/torque/tpackages
Building ./torque-package-server-linux-x86_64.sh ...
libtool: warning: remember to run 'libtool --finish /usr/local/lib'
Building ./torque-package-mom-linux-x86_64.sh ...
libtool: warning: remember to run 'libtool --finish /usr/local/lib'
Building ./torque-package-clients-linux-x86_64.sh ...
libtool: warning: remember to run 'libtool --finish /usr/local/lib'
Building ./torque-package-gui-linux-x86_64.sh ...
Building ./torque-package-devel-linux-x86_64.sh ...
libtool: warning: remember to run 'libtool --finish /usr/local/lib'
Building ./torque-package-doc-linux-x86_64.sh ...
Makefile:1202: warning: ignoring prerequisites on suffix rule definition
Makefile:1206: warning: ignoring prerequisites on suffix rule definition
Makefile:1210: warning: ignoring prerequisites on suffix rule definition
Makefile:1214: warning: ignoring prerequisites on suffix rule definition
Makefile:1202: warning: ignoring prerequisites on suffix rule definition
Makefile:1206: warning: ignoring prerequisites on suffix rule definition
Makefile:1210: warning: ignoring prerequisites on suffix rule definition
Makefile:1214: warning: ignoring prerequisites on suffix rule definition
Makefile:1202: warning: ignoring prerequisites on suffix rule definition
Makefile:1206: warning: ignoring prerequisites on suffix rule definition
Makefile:1210: warning: ignoring prerequisites on suffix rule definition
Makefile:1214: warning: ignoring prerequisites on suffix rule definition
Makefile:1202: warning: ignoring prerequisites on suffix rule definition
Makefile:1206: warning: ignoring prerequisites on suffix rule definition
Makefile:1210: warning: ignoring prerequisites on suffix rule definition
Makefile:1214: warning: ignoring prerequisites on suffix rule definition
Makefile:1202: warning: ignoring prerequisites on suffix rule definition
Makefile:1206: warning: ignoring prerequisites on suffix rule definition
Makefile:1210: warning: ignoring prerequisites on suffix rule definition
Makefile:1214: warning: ignoring prerequisites on suffix rule definition
Done.
```

続いて次のコマンドを実行します。`torque-package-server-linux-x86_64.sh`,`torque-package-mom-linux-x86_64.sh`と`torque-package-devel-linux-x86_64.sh`の3種類がありますが、キューイングシステムにおける司令塔となる**管理ノード**には`./torque-package-server-linux-x86_64.sh`を、管理ノードから司令を受けて計算を引き受ける**計算ノード**には`./torque-package-mom-linux-x86_64.sh`と`./torque-package-clients-linux-x86_64.sh`をインストールしておきます。ただ別に3つとも実行しておいて問題ないのでそうしておくと大丈夫です。

```bash
# torqueパッケージのインストール
$ ./torque-package-server-linux-x86_64.sh --install

Installing TORQUE archive...

Done.

$ ./torque-package-mom-linux-x86_64.sh --install
$ ./torque-package-clients-linux-x86_64.sh --install
```

次に、管理ノードと計算ノードの両方に**管理ノード**のサーバーの名前（`keisan_master`）を`/var/spool/torque/server_name`というファイルに書き込みます。このサーバー名は後ほど設定することになる`/etc/hosts`ファイルに書き込むホスト名（マシン名）と対応している必要があります。

```bash
$ echo "keisan_master" > /var/spool/torque/server_name
$ cat /var/spool/torque/server_name
keisan_master
```

## firewall-cmdコマンドでtorqueのためにポートを開放する

**Redhat系OS(Fedora, CentOS, Almalinux, RockyLinux)の場合は、ポート開放処理をする必要があります**。Ubuntu OSの場合はデフォルトでポートが開放状態になっていますが、ufwなどでファイアウォールを起動している場合は、ufwについて指定のポート番号を開放する処理を行う必要があります。

```bash
#Redhat系OS(CentOS, Almalinux, RockyLinux)の場合
$ firewall-cmd --add-port=15001/tcp --permanent
$ firewall-cmd --add-port=15002/tcp --permanent
$ firewall-cmd --add-port=15003/tcp --permanent
$ firewall-cmd --add-service=rsh --permanent
# 最後にかならずポート開放設定を反映させる
$ firewall-cmd --reload
```

## torque.setup

管理ノードで`torque.setup`を実行し、初期設定を行います。

```bash
$ cd /root/torque
$ bash torque.setup root
initializing TORQUE (admin: root)

You have selected to start pbs_server in create mode.
If the server database exists it will be overwritten.
do you wish to continue y/(n)?
```

`y`を押して次に進みます。

## nodesファイルの作成

計算に使用するノード（※管理ノードにも計算させる場合その名前も含む）の設定をすべて書いたファイルを、管理ノードの`/var/spool/torque/server_priv/nodes`に書き込みます。

```:/var/spool/torque/server_priv/nodes
keisan_master np=8
keisan1 np=16 gpus=1
keisan2 np=16 gpus=1 alphafold
```

ここで`nodes`ファイルの書き方は https://docs.adaptivecomputing.com/torque/4-1-4/help.htm#topics/1-installConfig/serverNodeFileConfig.htm や https://www.hpc.co.jp/images/pdf/manual/TorqueManual.pdf などのページを参考にしてください。npにはそのマシンのコア数を、gpusのところには搭載されているGPUの台数を書くことで、その上限値を設定することができます。またその後に**プロパティ(properties)** と呼ばれる属性を書くことが可能です。このプロパティはユーザーがジョブ投入時に指定することで効果を発揮します。

このファイルは後からも変更可能です。計算ノードを追加した場合、このファイルを追記して後述のpbs_serverプロセスを再起動することで有効になります。

## 計算ノード名とIPアドレスの設定

**管理ノード**と**計算ノード**のすべての`/etc/hosts`と`/etc/hosts.equiv`ファイルを編集して、計算クラスタに関わる全ノードのホスト名（マシン名）とIPアドレスのアドレスの対応表を作っておく必要があります。書式は以下の通りです。

`/etc/hosts`の方

```:/etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.1.1 keisan_master
192.168.1.2 keisan1
192.168.1.3 keisan2
```

`/etc/hosts.equiv`の方

```:/etc/hosts.equiv
keisan_master
keisan1
keisan2
```

**後で計算ノードを追加する場合、忘れずにこれらのファイルも全ノードにおいて更新しておく必要があります**。

## pbs_serverの起動

**管理ノード**には以下の処理を行います。解凍されているtorqueのディレクトリの中にある`contrib`以下にあるサービス制御のファイルを`/usr/lib/systemd/system/`にコピーし、その後`systemctl`コマンドでサービスを起動します。

管理ノードのサービスは`pdb_server`と`trqauthd`と`pdb_sched`です。この3つを起動させ、statusチェックでエラーが出ていないことを必ず確認してください。

```bash
cd /root/torque
cp contrib/systemd/pbs_server.service /usr/lib/systemd/system/
cp contrib/systemd/trqauthd.service /usr/lib/systemd/system/
cp contrib/systemd/pbs_sched.service /usr/lib/systemd/system/

systemctl enable pbs_server.service
systemctl start pbs_server.service
systemctl enable trqauthd.service
systemctl start trqauthd.service
systemctl enable pbs_sched.service
systemctl start pbs_sched.service
systemctl status pbs_sched
```

`systemctl`はLinuxでサービスを制御するためのコマンドです。簡単な説明と意味は https://qiita.com/wms2a4i1/items/04a1250229b12da33b87 などを読んでください。

エラーがあった場合は原因を特定し修正した上で、`systemctl restart foo.service`でそのサービスを再起動し、`status`で状態を再確認します。（※`.service`部分の入力は省略可能）

## pbs_momの起動

**計算ノード**（計算を実行させるノード）には以下の処理を行います。計算ノードで必要となるサービスは`pbs_mom`だけですが、`trqauthd`もしておくと状態確認上で便利なことがあります。

```bash
cd /root/torque
cp contrib/systemd/pbs_mom.service /usr/lib/systemd/system/
cp contrib/systemd/trqauthd.service /usr/lib/systemd/system/
systemctl enable pbs_mom.service
systemctl start pbs_mom.service
systemctl status pbs_mom
systemctl enable trqauthd.service
systemctl start trqauthd.service
systemctl status trqauthd
```

（あまりないですけれど）httpdを入れてある場合には、`/usr/lib/systemd/system/httpd.service`の`PrivateTmp`を`false`にしておくべきらしいです。

```bash
sed -i -e "s/PrivateTmp=true/PrivateTmp=false/g" /usr/lib/systemd/system/httpd.service
systemctl daemon-reload
systemctl restart httpd
```

## 設定の確認

ここまでの設定がうまく行っていれば`systemctl status なんちゃら`としたときに以下のような表示になります。

```bash
$ systemctl status trqauthd
● trqauthd.service - TORQUE trqauthd daemon
     Loaded: loaded (/lib/systemd/system/trqauthd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2023-08-13 18:53:11 JST; 13min ago
   Main PID: 1568 (trqauthd)
      Tasks: 1 (limit: 38308)
     Memory: 872.0K
        CPU: 3ms
     CGroup: /system.slice/trqauthd.service
             └─1568 /usr/local/sbin/trqauthd -F

 8月 13 18:53:11 keisan_master systemd[1]: Started TORQUE trqauthd daemon.
 8月 13 18:53:11 keisan_master trqauthd[1568]: Currently no servers active. Default server will be listed as active server. E>
 8月 13 18:53:11 keisan_master trqauthd[1568]: Active server name: keisan_master  pbs_server port is: 15001
 8月 13 18:53:11 keisan_master trqauthd[1568]: trqauthd port: /tmp/trqauthd-unix
```

`Active: `の部分が緑色で`active (running)`となっているとOKです。赤色で`failed`となっている場合は稼働していません。この場合は設定を確認または変更したあと`systemctl restart <該当のサービス名>`として再起動し、activeになることを確認します。

```bash
$ systemctl status pbs_sched
* pbs_sched.service - TORQUE pbs_sched daemon
     Loaded: loaded (/lib/systemd/system/pbs_sched.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2023-08-13 19:16:07 JST; 8s ago
    Process: 3272 ExecStart=/usr/local/sbin/pbs_sched -d $PBS_HOME $PBS_ARGS (code=exited, status=0/SUCCESS)
   Main PID: 3273 (pbs_sched)
      Tasks: 1 (limit: 38308)
     Memory: 2.9M
        CPU: 3ms
     CGroup: /system.slice/pbs_sched.service
             `-3273 /usr/local/sbin/pbs_sched -d /var/spool/torque

Aug 13 19:16:07 keisan_master systemd[1]: Starting TORQUE pbs_sched daemon...
Aug 13 19:16:07 keisan_master systemd[1]: Started TORQUE pbs_sched daemon.

```

```bash
$ systemctl status pbs_server
● pbs_server.service - TORQUE pbs_server daemon
     Loaded: loaded (/lib/systemd/system/pbs_server.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2023-08-13 18:53:11 JST; 26s ago
   Main PID: 1569 (pbs_server)
      Tasks: 10 (limit: 38308)
     Memory: 18.0M
        CPU: 17ms
     CGroup: /system.slice/pbs_server.service
             └─1569 /usr/local/sbin/pbs_server -F -d /var/spool/torque

 8月 13 18:53:11 keisan_master systemd[1]: Started TORQUE pbs_server daemon.
 8月 13 18:53:11 keisan_master PBS_Server[1569]: LOG_ERROR::tcp_connect_sockaddr,
```

`pbsnodes -a`と打ったときに、計算ノードすべての表示が現れているとOKです。

```bash
$ pbsnodes -a
keisan_master
     state = free
     power_state = Running
     np = 8
     ntype = cluster
     status = opsys=linux,uname=Linux keisan_master 4.18.0-425.19.2.el8_7.x86_64 #1 SMP Tue Apr 4 22:38:11 UTC 2023 x86_64,sessions=2655 2100 2860,nsessions=3,nusers=1,idletime=2848,totmem=205082836kb,availmem=199625776kb,physmem=196694232kb,ncpus=8,loadave=0.00,gres=,netload=382925861,state=free,varattr= ,cpuclock=Fixed,version=6.1.3,rectime=1697965154,jobs=
     mom_service_port = 15002
     mom_manager_port = 15003

keisan1
     state = free
     power_state = Running
     np = 16
     ntype = cluster
     status = opsys=linux,uname=Linux keisan1 4.18.0-477.27.1.el8_8.x86_64 #1 SMP Wed Sep 20 15:55:39 UTC 2023 x86_64,nsessions=0,nusers=0,idletime=182861,totmem=101251836kb,availmem=100240860kb,physmem=97057536kb,ncpus=48,loadave=0.00,gres=,netload=101274558,state=free,varattr= ,cpuclock=Fixed,macaddr=ac:1f:6b:75:cd:da,version=6.1.3,rectime=1697965133,jobs=
     mom_service_port = 15002
     mom_manager_port = 15003
     gpus = 1

keisan2
     state = free
     power_state = Running
     np = 16
     properties = alphafold
     ntype = cluster
     status = opsys=linux,uname=Linux keisan2 4.18.0-477.27.1.el8_8.x86_64 #1 SMP Wed Sep 20 15:55:39 UTC 2023 x86_64,nsessions=0,nusers=0,idletime=183146,totmem=101251836kb,availmem=100246376kb,physmem=97057536kb,ncpus=48,loadave=0.07,gres=,netload=83318341,state=free,varattr= ,cpuclock=Fixed,macaddr=ac:1f:6b:6c:8c:fe,version=6.1.3,rectime=1697965144,jobs=
     mom_service_port = 15002
     mom_manager_port = 15003
     gpus = 1
```

このうち、**`state = free`であれば管理サーバーからの計算依頼を受け付けている状態ですが、`state = down`の場合は管理サーバー・計算サーバー間でのtorqueでの通信ができておらず、計算を受け入れられない状態です**。この`state = down`は解決しなければならない状態です。

# キューイングシステムの作成

## queueの作成と設定

以降の処理は**管理ノード**において行います。デフォルトで設定する`batch`という名前のqueueタイプを削除して、`default`という名前のqueueタイプを作成します。ちなみにターミナルからは`qmgr -c 'p s'`と入力することで現在の設定の一覧を見ることができます。管理ノードにおいて`qmgr`コマンドをターミナルから入力すると、以降標準入力からインタラクティブに設定をどんどん作ることができます。

```shell
#ターミナルからqmgrを入力してqmgrモードに入る
qmgr

# デフォルトのbatchキューの削除
delete queue batch

# "default"キューの作成
# 最大で連続72時間しか使えないようにする。デフォルトは24時間指定。
create queue default
set queue default queue_type = Execution
set queue default max_running = 32
set queue default resources_max.walltime = 72:00:00
set queue default resources_default.walltime = 24:00:00
set queue default enabled = True
set queue default started = True

# "week"キューの作成
# 最大で連続168時間使える。デフォルトは24時間指定。
create queue week
set queue week queue_type = Execution
set queue week max_running = 4
set queue week resources_max.walltime = 168:00:00
set queue week resources_default.walltime = 24:00:00
set queue week enabled = True
set queue week started = True

#PBS schedulingをONにする.
#これを使うと自動でジョブを振り分けてくれる。`systemctl status pbs_sched`でスケジューラのdaemonが働いているか確認できる。
set server scheduling = True
#他のユーザーからジョブを見られるようにする
set server query_other_jobs = True
#ジョブ終了後3秒でqstatのリストから消えるようにする
set server keep_completed = 3

quit
```

## starving jobsエラーへの対処

デフォルトでは、一定時間ジョブが投入されない状態が続いていると、全ユーザーに対して`Not Running: Draining system to allow starving job to run`というエラーが表示されてジョブが実行されなくなる状態が発生することがあります。正直これは**最初からそうならないように設定しておいたほうがいい**ので、以下のように設定変更しておきます。

管理ノードの`/var/spool/torque/sched_priv/sched_config`の以下の行を変更します。

```shell
help_starving_jobs true ALL
```

これを

```shell
help_starving_jobs false ALL
```

に変更します。この後、PBSスケジューラを再起動するために、`systemctl restart pbs_sched`を実行します。再起動後、`systemctl status pbs_sched`で緑信号（Running）が表示されれば成功です。

## postfixでジョブ終了メール通知を遅れるようにする

（書きかけ）

# Torqueによるジョブキューイングシステム

`qsub`コマンドを使うことで待機状態の計算ノードを自動サーチして計算をさせることができます。

## ジョブ投入テスト

keisan_masterにログインして以下のスクリプトを作ってみて、実行できるかどうか確かめてみましょう。

``` bash
#!/bin/sh
#PBS -l nodes=1:ppn=16
#PBS -q default
#PBS -l walltime=0:30:00

## must include these lines
test $PBS_O_WORKDIR && cd $PBS_O_WORKDIR
# show the calculation node
echo `hostname`
# run the environment module
. /home/apps/Modules/init/profile.sh
## Write your qsub script from here.

# Output message
echo "Hello keisan_master!!"

# wait for 5 seconds.
sleep 5s
```

これをホームディレクトリ上に`test.sh`という名前で作成し、`qsub -N Hellotest test.sh`と入力して実行してみましょう。すると *`JOB_ID`* `.keisan_master`というメッセージが標準出力に現れ、5秒後にはそのディレクトリに`Hellotest.o<JOB_ID>`という名前で、中に`Hello keisan_master!!`という出力のファイルが現れているはずです。ここまでできていれば成功です。

keisan_masterの`qsub`システムではデフォルト設定でこの標準出力ファイル・標準エラー出力ファイルが切り分けられています。

エラーが何も起きなかったときの標準エラー出力ファイル`Hellotest.e<JOBID>`には何もなくて0バイトの空ファイルになっているはずです。今回のテストではエラーは起きていないので空のはずです。

`-M`にメールアドレスを指定すると、そのメールアドレスにジョブの結果が送信されるようになります（postfix連携でメール通知を設定している場合）。

## ジョブの確認

上記の`qsub`は計算処理をkeisan_masterにつながる計算ノードの空いているマシンに割り当てて自動実行させるためのコマンドです。この`qsub`で計算をさせることを **「ジョブを投入する」** と言います。

### qstatコマンド

投入されたジョブがどのマシンで動いているか、ジョブが終了したかを確認するためには`qstat`コマンドを使います。

```shell
$ qstat
Job ID              Name             User            Time Use S Queue
------------------- ---------------- --------------- -------- - -----
9.keisan_master     calc_order.sh    sakakibara      02:16:39 R default
15.keisan_master    Hellotest        moriwaki               0 R default
```

  - `Job ID`は先ほど出てきた、`qsub`したときに返ってくるジョブのIDです。
  - `Name`は`qsub`オプションの`-N`で付けた名前です。この名前をつけておくと便利ですので積極的に使いましょう。
  - `User`は投入したユーザー。
  - `Time Use`は処理開始からのCPUの累計計算時間。
  - `S`はStatusで、`R`が正常運行、`E`はエラー発生、`C`は終了処理中、`Q`は投入可能なマシンが空いたら投入されるのを待っている状態、`H`は予約されているとき（後述）などです。
  - `Queue`はキューの種類です。これは投入するシェルスクリプトの`#PBS -q default`で指定します。

`qstat`コマンドで何も表示されないというのは、ご自身を含めて今は誰もkeisan_master上で計算ジョブを実行させていないということです。Hellotestは`qsub`されてから5秒後くらいには実行が完了して消えるようになっています。

`qstat`は他にも色々便利なオプションがあります。`man qsub`とか`man qstat`とかでマニュアルを確認してみてください。

### 詳細なジョブモニタリング

`qstat -an1st`を実行すると

```shell
$ qstat -an1st

keisan_master:
                                                                           Req'd       Req'd      Elap
Job ID             Username    Queue    Jobname          SessID  NDS   TSK   Memory      Time   S   Time
------------------ ----------- -------- ---------------- ------ ----- ------ --------- --------- - ---------
1485.keisan_master moriwaki    default  3wae             26159     1     16   --       72:00:00 R  00:12:49   keisan1/0-15
   Job started on Fri Jun 01 at 18:41
1486.keisan_master moriwaki    default  4elr             86283     1     16   --       72:00:00 R  00:12:23   keisan2/0-15
   Job started on Fri Jun 01 at 18:41
```

というように表示されます。上記`qstat`コマンドと比べると右端にElapse Time（実際のジョブ作動時間）、計算ノード（上の例で言うとkeisan1, keisan2）、開始時間などの情報が増えています。こっちを使うことを推奨します。

## ジョブの削除

ジョブを消したくなったときには`qdel <jobid>`で消すことができます。例えばジョブ番号25500を消したい場合は`qdel 25500`と入力します。

## ジョブ投入後の計算ノードの変更

「**ジョブを投入したが、その指定ではQueue状態で待機中のままになっているため、別のgroupの空いている計算ノードにジョブを入れ直したい**」という場合、以下の手順で解決できます。

1. `qstat`で`Q`状態（ジョブ投入待ち）になっているジョブ番号を確認する。以下ではその番号を`99999`とする。
2. `qalter`コマンドで投入待ちのジョブのプロパティを変更できる。`qalter -l "nodes=xx:ppn=xx:yyy,walltime=72:00:00" 99999`のような形で実行する。
3. `qstat -f 99999`でジョブのプロパティが変わっていることを確認する。
4. `qhold 99999`として`H`（保留状態）にし、その後`qrls 99999`で保留解除すると、ジョブが動き出す。

つまり、1行でつなげると

```bash
$ jobid=99999 && qalter -l "nodes=1:ppn=16:groupB,walltime=72:00:00" ${jobid} && qhold ${jobid} && qrls ${jobid}
```

のようになります。`jobid`の番号やノードのプロパティを適宜変えて使ってください。

## ジョブの予約

あるジョブAが終わったことを見計らって別のジョブBを実行させたいということがあると思います。このような場合は`qsub`の`-W`オプションを使って以下のようにやります。

```bash
$ qsub jobA.sh
18.keisan_master
$ qsub -W depend=afterany:18 jobB.sh
19.keisan_master
```

こんな感じで`depend=afterany:18.keisan_master`の部分にjobAでのJOBIDを入力します。このとき`qstat`では以下のように表示され処理待ちの**H**が映し出されます。

```bash
Job ID             Name             User           Time Use S Queue
----------------- ---------------- --------------- -------- - -----
18.keisan_master   jobA.sh          moriwaki              0 R default
19.keisan_master   jobB.sh          moriwaki              0 H default
```

  - `-W
    depend=`の後には`afterany`以外にも色々オプションがあります。詳しくは[公式マニュアル](http://docs.adaptivecomputing.com/torque/4-0-2/Content/topics/commands/qsub.htm#dependencies)で。afterany, afterok, afterとかは結構使うんじゃないかな？
  - すでに終了済みのジョブに対して`depend=afterany`をやろうとすると、`qsub: submit error
    (Invalid Job Dependency)`と言われて投入できません。

## ジョブへの引数渡し

コマンドラインでは、`-v 変数1=値1,変数2=値2,...`というオプションを指定することで、`qsub`する実行ファイルに変数を渡すこともできます。たとえば、実行ファイルの中で、実行するコマンドのコマンド引数を次々と変えてqsubしたいときは、以下のサンプルを参考にしてください。

```bash:run_hhblits.sh
#!/bin/sh
#PBS -l nodes=1:ppn=4,walltime=12:00:00
#PBS -q default

test $PBS_O_WORKDIR && cd $PBS_O_WORKDIR

# Error if a variable VAR1 is not set
test $VAR1 || { echo "VAR1 is not set." ; exit 1 ; }

# run the environment module (keisan_master)
. /home/apps/Modules/init/profile.sh

module load hh-suite/3.3

hhblits -cpu 4 \
        -i /home/moriwaki/2021/af2_atp_augumetation/pdb_unifasta/$VAR1.fasta \
        -d /mnt/database/UniRef30_2021_06/UniRef30_2021_06 \
        -oa3m /home/moriwaki/2021/af2_atp_augumetation/hhblits_every_seq/hhblits/$VAR1_out.a3m \
        -n 3 -e 0.001 -maxseq 1000000 -realign_max 100000 -maxfilt 100000 -min_prefilter_hits 1000
```

そしてqsubするときに、

```shell
$ qsub -v VAR1=A999999 run_hhblits.sh
```

とすると、シェルスクリプト`run_hhblits.sh`の`$VAR1`に`A99999`という値が代入された状態でqsubすることができます。これは同じフォーマットを使って入力とするファイル名のみを変更して連続投入したいというときに便利です。

## walltimeとqueueの種類の選択

`#PBS -l walltime=24:00:00`とすると、ジョブ投入から24時間後に自動で強制終了するような設定になります。この上限時間については2つのキューで異なっており、この時間内であれば任意に時間を減らすことが可能です。
現在のところ、keisan_masterクラスタには`default`と`week`の2つのqueueを設定しています。walltimeの指定をしなければ最大 **3日間 (72:00:00)** 処理を続けることができるdefault queueに設定していますが、queueの種類はシェルスクリプトの中で

``` bash
#!/bin/sh
#PBS -l nodes=1:ppn=4,walltime=168:00:00
#PBS -q week

test $PBS_O_WORKDIR && cd $PBS_O_WORKDIR

...
```

とすることで変更することができます。`week`キューは**最大1週間（168時間）** 動作させることができますが、初期値は24時間に設定してあります。

詳細はtorqueシステムのコマンドの1つ`qmgr -c "p s"`で見ることができます。

## 計算マシンの種類と使用するcore数の選択

nodesのところに計算ノード名を直接指定すると、明示的にその計算ノードに投入することができます。

``` bash
#!/bin/sh
#PBS -l nodes=keisan1:ppn=16
#PBS -q default

test $PBS_O_WORKDIR && cd $PBS_O_WORKDIR

...
```

を指定すると、keisan1マシンの16コアを確保して計算を実行するようになります。また、propertyを使った指定も可能で、keisan_master上で`pbsnodes`を入力した時のpropertiesに出ている値を入力することでそのマシン属性を指定することができます。

```bash
#!/bin/sh
#PBS -l nodes=1:ppn=16:gpus=1:alphafold
#PBS -q default
```

また、実は`qsub`するときに、ターミナル上から引数を指定することで上記の設定を追加（または上書き）することもできます。一例として

```shell
[moriwaki@hoge]$ qsub -l 'nodes=keisan2:ppn=16:alphafold' -q default run.sh
```

とすれば、そのノード設定・キュー設定で投入することができます。これも使い方によっては便利です。

「**ジョブを投入したがQueue状態で待機中のままになっており、空いている計算ノードにジョブを入れ直したい**」という場合、以下の手順で解決できるかもしれません。

1. `qstat`で`Q`状態（ジョブ投入待ち）になっているジョブ番号を確認する。以下ではその番号を`99999`とする。
2. `qalter`コマンドで投入待ちのジョブのプロパティを変更できる。`qalter -l "nodes=xx:ppn=xx:yyy,walltime=72:00:00"` 99999のような形で実行する。
3. `qstat -f 99999`でジョブのプロパティが変わっていることを確認する
4. `qhold 99999`として`H`（保留状態）にし、その後`qrls 99999`で保留解除すると、ジョブが動き出す

## GPU数の指定

使用するgpu数の指定を行うことができます。例はこんな感じ（計算ノードをkeisan1に指定、コア数16コア、制限時間72時間の例）。

``` bash
#!/bin/sh
#PBS -l nodes=keisan1:ppn=16:gpus=1,walltime=72:00:00
#PBS -q default

test $PBS_O_WORKDIR && cd $PBS_O_WORKDIR

...
```

2機のGPUを持っている場合、`#PBS -l gpus=2`のようにして最大2機までのGPUを指定することができます。`gpus=1`として1機のみを指定した場合、GPUの様子を観察するコマンド`nvidia-smi`では1台分しか見かけ上検出することができず、GPUのIDも0始まりになるので、コアな使い方をする人は要注意です。

## 標準出力と標準エラー出力の設定 (optional)

``` bash
#!/bin/sh
#PBS -l nodes=1:ppn=1
#PBS -q default
#PBS -k oe

test $PBS_O_WORKDIR && cd $PBS_O_WORKDIR

...
```

このように`#PBS -k oe`を書いておくと、ジョブの実行中でも標準出力と標準エラー出力がユーザーのディレクトリ上に現れるようになります。指定しない場合は、ジョブが終了したときに初めてユーザーのディレクトリ上に表示されるようになります。

## PBS torqueシステムで使える便利な変数

ほとんどのPBS torqueシステムでデフォルトで使える変数の例

  - `$PBS_JOBNAME` User specified jobname
  - `$PBS_O_WORKDIR` Job’s submission directory
  - `$PBS_TASKNUM` Number of tasks requested
  - `$PBS_O_HOME` Home directory of submitting user
  - `$PBS_MOMPORT` Active port for mom daemon
  - `$PBS_O_LOGNAME` name of submitting user
  - `$PBS_NODENUM` Node offset number
  - `$PBS_O_SHELL` Script shell
  - `$PBS_O_JOBID` Unique pbs job id
  - `$PBS_O_HOST` Host on which job script is currently running
  - `$PBS_QUEUE` Job queue
  - `$PBS_NODEFILE File` containg line delimted list on nodes allocated to the job
  - `$PBS_O_PATH` Path variable used to locate executables within job script