[CPU編]AWS ParallelCluster SlurmにCPU系ジョブを投げるとクラスターがどうスケールするか試してみた

ジョブスケジューラーにSlurmを利用したAWS ParallelClusterではコンピューティングノードがAutoScaling Groupと連動してスケーリングします。 CPU系ジョブを投げて、どのようにスケールするか試してみました。
2020.08.27

AWS ParallelClusterのジョブスケジューラーに伝統的なスケジューラーを利用すると、コンピュートフリートはAmazon EC2 Auto Scaling Group(ASG)で管理され、ASGの機能を用いてスケールします。

ジョブスケジューラーのSlurmCPUベースのジョブを投げ、ジョブがどのようにノードに割り振られ、フリートがスケールするのか、主に ユーザー↔ジョブスケジューラー(Slurm)間のジョブ操作の観点から試してみました。

GPU 編はこちら

環境

  • AWS ParallelCluster : 2.7
  • ジョブスケジューラー : Slurm Workload Manager (Slurm)
  • Intel ハイパースレッディング : 無効
  • コンピュートノードのvCPU : デフォルトでは8vCPU。HTを無効化にして4vCPUで動作
  • ノードの台数 : 最大8(max_queue_size=8)

やり方

AWS ParallelCluster で ハイパースレッディング(HT)を無効化したコンピュートノード構成します。

利用可能なノード数・vCPU数によってジョブスケジューラーがどのようにジョブを割り振り、フリートをスケールさせるのか確認します。

実行に5分かかるスクリプトを用意します。

#!/bin/sh
srun -l /bin/hostname
sleep 300

このスクリプトを元に、以下の様なジョブを投げます。

  • job1 : 2 ノード、合計4タスク(2タスク/ノード) $ sbatch -N2 -n4 --job-name job1 my.script
  • job2 : 3 ノード、合計3タスク(1タスク/ノード) $ sbatch -N3 -n3 --job-name job2 my.script
  • job3 : 1 ノード、合計2タスク(2タスク/ノード) $ sbatch -N1 -n2 --job-name job3 my.script

ジョブ投入時のコンピュートフリートのアイドルなノード数によって、ジョブがどのように振り分けられ、ノードがどのように増えるのか確認します。

各ノードのvCPU数はHTを無効にした状態で4です。

シナリオ1 : フリートが 0 台の時にジョブを投げる

コンピュートノードが0台のフリートを用意します。

$ sinfo
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
compute*     up   infinite      0    n/a

この状態でジョブを投げます。

$ sbatch -N2 -n4 --job-name job1 my.script
Submitted batch job 2
$ sbatch -N3 -n3 --job-name job2 my.script
Submitted batch job 3
$ sbatch -N1 -n2 --job-name job3 my.script
Submitted batch job 4

3ジョブがキューイングされます。

$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 2   compute     job1   centos PD       0:00      2 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                 3   compute     job2   centos PD       0:00      3 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                 4   compute     job3   centos PD       0:00      1 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)

$ squeue --long
Thu Aug 13 02:00:53 2020
             JOBID PARTITION     NAME     USER    STATE       TIME TIME_LIMI  NODES NODELIST(REASON)
                 2   compute     job1   centos  PENDING       0:00 UNLIMITED      2 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                 3   compute     job2   centos  PENDING       0:00 UNLIMITED      3 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                 4   compute     job3   centos  PENDING       0:00 UNLIMITED      1 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)

/var/log/jobwatcher を確認すると、ジョブに必要なスロットをもとに、新規に 3ノード起動しようとしています。 内部的には、Auto Scaling Groupのdesired capacity を 3 にしています。

2020-08-13 02:00:05,293 INFO [utils:get_optimal_nodes] Processing job that requested 2 nodes and the following resources: {'gpus': 0, 'slots': 4}
2020-08-13 02:00:05,293 INFO [utils:get_optimal_nodes] Processing job that requested 3 nodes and the following resources: {'gpus': 0, 'slots': 3}
2020-08-13 02:00:05,293 INFO [utils:get_optimal_nodes] Processing job that requested 1 nodes and the following resources: {'gpus': 0, 'slots': 2}
2020-08-13 02:00:05,293 INFO [utils:get_optimal_nodes] Computed following allocation for required nodes [{'slots': 1, 'gpus': 0}, {'slots': 1, 'gpus': 0}, {'slots': 1, 'gpus': 0}]
2020-08-13 02:00:05,293 INFO [jobwatcher:_poll_scheduler_status] 3 nodes requested, 0 nodes busy or unavailable
2020-08-13 02:00:05,293 INFO [jobwatcher:_poll_scheduler_status] Setting desired to 3 nodes, requesting 3 more nodes from asg.
2020-08-13 02:01:05,333 INFO [utils:get_asg_settings] ASG min/desired/max: 0/3/8

ノードが3台立ち上がると、ジョブが3ノードに割り振られています。

$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 2   compute     job1   centos  R       1:01      2 ip-10-1-21-[30,95]
                 3   compute     job2   centos  R       1:01      3 ip-10-1-21-[30,95,179]
                 4   compute     job3   centos  R       1:01      1 ip-10-1-21-179

# 簡易表示
$ sinfo
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
compute*     up   infinite      3    mix ip-10-1-21-[30,95,179]

# ノード別表示
$ sinfo --Node --long
Thu Aug 13 02:06:13 2020
NODELIST        NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON
ip-10-1-21-30       1  compute*       mixed    4    4:1:1      1        0      1   (null) none
ip-10-1-21-95       1  compute*       mixed    4    4:1:1      1        0      1   (null) none
ip-10-1-21-179      1  compute*       mixed    4    4:1:1      1        0      1   (null) none

sinfo の出力で STATEmix/mixed となっているのは、4vCPU に対して、まだ空きスロット(idle枠)があるためです。 すべて埋まると alloc/allocated になります。

3ノードあれば、3ジョブを並列で全て処理できるため、最小の3ノードだけ起動したものと思われます。

シナリオ2 : フリートが 2 台の時にジョブ投下

同様の手順で、フリートが 2台のときにジョブを投げてみます。

結果、まずはアクティブな2台で、2ノードで処理可能なジョブを処理しました。

$ sinfo --Node --long
Thu Aug 13 01:48:14 2020
NODELIST        NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON
ip-10-1-21-164      1  compute*   allocated    4    4:1:1      1        0      1   (null) none
ip-10-1-21-238      1  compute*       mixed    4    4:1:1      1        0      1   (null) none
$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 7   compute     job2   centos PD       0:00      3 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                 6   compute     job1   centos  R       0:23      2 ip-10-1-21-[164,238]
                 8   compute     job3   centos  R       0:23      1 ip-10-1-21-164

3ノード以上確保できたタイミングで、ジョブ実行に3ノード必要な job2 が処理されました。

$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 7   compute     job2   centos  R       0:08      3 ip-10-1-21-[99,124,164]
$ sinfo --Node --long
Thu Aug 13 01:53:32 2020
NODELIST        NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON
ip-10-1-21-99       1  compute*       mixed    4    4:1:1      1        0      1   (null) none
ip-10-1-21-124      1  compute*       mixed    4    4:1:1      1        0      1   (null) none
ip-10-1-21-164      1  compute*       mixed    4    4:1:1      1        0      1   (null) none
ip-10-1-21-238      1  compute*        idle    4    4:1:1      1        0      1   (null) none
ip-10-1-21-253      1  compute*        idle    4    4:1:1      1        0      1   (null) none

起動ずみ2ノードで処理可能なジョブは処理しつつ、job2 向けに3ノード追加し、合計5台になりました。

CPU の空きスロットを考慮すると、2ノード追加すれば job2 も処理できますが、そのようにはなりませんでした。

ドキュメントに記載されているように、ジョブを実行しているノードはビジーノードとみなされ、job2 向けに別途3ノード追加されたものと思われます。

現在の制限: 自動スケールアップロジックは、部分的にロードされたビジーノードを考慮しません。つまり、ジョブを実行しているノードは、空のスロットがある場合でも、ビジー状態と見なされます。

https://docs.aws.amazon.com/ja_jp/parallelcluster/latest/ug/autoscaling.html

※ 5ノード目の「ip-10-1-21-253」を起動しなくても、job2 は実行できる

さらに

  • ip-10-1-21-99
  • ip-10-1-21-124
  • ip-10-1-21-253

の3台は3ノード必要な job2 のために新規に起動しましたが、job2 の実際の処理は

  • ip-10-1-21-99
  • ip-10-1-21-124
  • ip-10-1-21-164

の3台で行われました。

ノードの追加命令と、ジョブスケジューラーがジョブをどのように割り振るかは独立していることがわかります。

※ ノード追加のトリガーとなったジョブが、実際に追加されたジョブで処理されたとしたら、このようになる。

シナリオ3:フリートが4台のときにジョブ投下

4台 idle のフリートを用意します。

$ sinfo
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
compute*     up   infinite      4   idle ip-10-1-21-[43,164,212,238]

ジョブを投入すると、シナリオ1のときと同じく、3ノードだけですべてのジョブを処理しました。

$ squeue
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 3   compute     job1   centos  R       1:22      2 ip-10-1-21-[43,164]
                 4   compute     job2   centos  R       1:22      3 ip-10-1-21-[43,164,212]
                 5   compute     job3   centos  R       1:18      1 ip-10-1-21-212

空きスロットのあるノードだけで処理できる場合は、全スロットが空いているノードには手を付けないようです。

ジョブスケジューラー(Slurm)↔Auto Scaling Group間のノードのスケーリングについて

Slurm(ジョブスケジューラー)↔Auto Scaling Group間のノードのスケーリングについては、次のドキュメントで詳細に記載されています。

How AWS ParallelCluster works - AWS ParallelCluster

  • ジョブスケジューラーにノード追加が必要化チェックする jobwatcher
  • ASG によりノードの追加・削除されたイベントをジョブスケジューラーに通知する sqswatcher
  • コンピュートノードで idle 状態が続き、ノードの削除が必要化チェックする nodewatcher

などが裏方として活躍しています。

最後に

ParallelCluster がどのようにノードを増減し、ジョブスケジューラーがどのようにジョブをノードに割り振るのか、ある程度把握しておくと

  • ノードを常に何台稼働させておくか
  • ノードを最大で何台まで稼働させるか

といったシミュレーションをしやすくなると思います。

Slurm の CPU リソース管理の詳細については、次のドキュメントを参照ください。

Slurm Workload Manager - CPU Management User and Administrator Guide

それでは。

参考