HPCシステムズではエンジニアを募集しています。詳しくはこちらをご覧ください。
HPCシステムズのエンジニア達による技術ブログ

Tech Blog

AWSが拓いたLustreファイルシステム新時代 -3-

前置き

前回の記事で「短期決戦・スクラッチ向き」とか書いた途端に、永続的ストレージの発表があり、頭を抱えた筆者です。多数のユーザーが混在するクラウド上においてはパフォーマンスと同時にセキュリティも重要ですが、条件付きながらデータ転送も保存データも暗号化されています。普通のLustreでは行われていないOSTの2重化すら実現しています。さらにパフォーマンス向上も実現しています。これでAWS上に「普通のLustre」をつないだ「普通のスパコン」を運用する上で必要な、ある意味それ以上の「モノ」はほぼ揃ってしまいました。今回はその作り方と使い方を簡単に勉強しておきましょう。

AWSとHPC環境について

地球シミュレータや京、中国のSunway TaihuLightは、一般には入手不可能な専用ハードウエアを使った特殊なコンピュータでしたが、現在多くのスパコンは汎用機材を組み上げて作られるクラスターマシンです。つまり、多数のマシンをつなぎ合わせれば同様なものができてしまいます。それを効率的に使えるようにしたりする仕組みも用意されているので、クラウド上にスパコンを作りたいユーザー側の技術的な困難は大幅に削減されました。もちろんそれを実現するバックグラウンドには驚嘆すべき技術が盛り込まれています。

かつてEC2の発表とほぼ同時に、それを科学技術計算に使おうという試みは始まり、様々なベンチマークが取られました。初期のものはユーザーが満足できるものではなかったわけですが、一貫して大規模計算に向けた環境が用意され続けており、Top500でもEC2上に作られたクラスターが昨年6月に136位、11月に同システムが179位にランクインしています。そうした中でMITで開発されたStarClusterは、2009年にGuthubへバージョン0.9が登録されていて、簡単な設定ファイルを用意すればクラスターを自動構成でき、運用と利用の一連の作業が完結できるソフトウエアです。しかし使えるインスタンス名が直書きされていたり、いちいちAPIとsshを連携させるような作りになっていて、言ってみれば手作業でクラスターを作る流れをスクリプト化したのに近いものでした。それゆえ昨今のクラウド利用方法とは相容れず、バージョン1.0に至る前に開発が止まり、AWSから発表されたCfnClusterへその座を譲りました。CfnClusterはAWSに馴染みがある人にはおわかりの通り、CloudFormationの仕組みを使ったものですが、ユーザーから見た使い勝手はStarClusterをほぼ踏襲しています。そして2018年11月のタイミングでParallel Clusterに名称が変更されました。それ以降も絶えず機能強化が行われ、今回とりあげているFSx for Lustreにも対応かつ、シンプルに使えるようになっています。

ちなみにParallel Clusterには詳しいマニュアル、しかも日本語のものがあります(機械翻訳らしく、たまに変なところがあります)。VPCやEC2に関する初歩的な知識さえあれば読める内容で、使うだけならCloudFormationの知識は一切不要です。インストールなどについては他に譲るとして、早速使ってみましょう。以下で解説するのは標準的なHPCクラスターに関するものですが、ここではあえてスパコンと呼んで煽り気味に行ってみましょう。

スパコンを作る

HPCクラスターは基本的に、ユーザーがログインして使うホストと呼ばれるマシン(マスターノードやホストノードと呼ばれる)と、アカウント情報を共有した多数のマシン(計算ノード)からなり、共有ファイルシステムで同一のパスで同じファイルが見えるようにしてあります。そのためどのマシンにログインしてもホスト名以外に違いはない状態を作っています。ここでは初期コンフィグとほぼ変らない以下の設定を使います。外部からログインを可能にしているマスターノードと、そうでない計算ノードは別々のサブネットに割り当てられています。

$ cat ~/.parallelcluster/config
[aws]
aws_region_name = us-west-1
[global]
cluster_template = default
update_check = true
sanity_check = true
[aliases]
ssh = ssh {CFN_USER}@{MASTER_IP} {ARGS}
[cluster default]
key_name = tech-blog@hpc.co.jp
base_os = centos7
scheduler = slurm
master_instance_type = t3.micro
compute_instance_type = c5n.xlarge
maintain_initial_size = true
vpc_settings = default
cluster_type = spot
[vpc default]
use_public_ips = false
vpc_id = vpc-XXXXXXXXXXXXXXXXX
master_subnet_id = subnet-YYYYYYYYYYYYYYYYY
compute_subnet_id = subnet-ZZZZZZZZZZZZZZZZZ

この設定でpcluster create tech-blogとすると、AWSの上で様々な機構が動き出します。10分弱かかりますがtech-blog という名前のクラスターがデフォルト設定で作られ、t3.microの小さなマスターノードだけが起動します。マスターノードにsshログインしてみましょう。

$ pcluster create tech-blog
Beginning cluster creation for cluster: tech-blog
Creating stack named: parallelcluster-tech-blog
Status: parallelcluster-tech-blog - CREATE_COMPLETE
ClusterUser: centos
MasterPrivateIP: 10.0.0.147
$ pcluster ssh tech-blog
Last login: Wed Mar 18 03:28:29 2020
[centos@ip-10-0-0-147 ~]$ 

パブリックIPアドレスを知る必要もなく、ユーザーcentosでsshログインまでできました。

多数の計算処理(ジョブ)を多数のノード上で実行するわけですが、その割り当てを人手でやるのはいささか面倒です。ましてユーザーが複数いたらリソースの奪い合いで戦争が起こります。そこでわんこそばのように、状況をモニタリングしつつ空いたリソースにジョブを自動的に投げ込んでくれるソフトウエアがあります。ジョブスケジューラと呼ばれるそれは、有償製品としてはIBM Spectrum LSF SuiteUniva Grid EngineAdaptive Computing Moab HPCなどが存在します。Parallel Clusterが標準で用意しているのはフリーのTorque, SGE, Slurmの3つですが、もうひとつ、自分で作成したコンテナベースでジョブ実行をコントロールするAWS Batchを使うことも可能です。最近のアップデートで、AWS BatchコンテナからFSx for Lustreにアクセスできるようになりました。ここでは最近よく使われるSlurmを使う設定にしています。

スパコンの使い方

まず指定したスケジューラが使えるかの確認をします。

$ lsid
Slurm 19.05.5, May 1 2019
Copyright SchedMD LLC, 2010-2017.
My cluster name is parallelcluster
My master name is ip-10-0-0-147

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

lsidは元々はLSF由来のコマンドですが、スケジューラの名称などを確認できます。バージョン19.05.5のSlurmが使えています。sinfoはクラスターの状態を表します。多数のノードやスペックが異なるマシンが混在している場合に、それらを別々に扱うパーティションという概念があります。ここでは唯一computeというパーティションだけあります(末尾の*はデフォルトを示しています)が、ノード数は0になっています。リストも空っぽです。

アプリケーションの起動コマンドやファイルパスはマスターノードも計算ノードも同一になっているので、ユーザーはそれをマスターノードからスケジューラに投げ、あとは待っているだけという使い方をします。スクリプトを投げっぱなしにする場合はsbatchコマンド、インタラクティブに実行するsrunコマンド、他のジョブが入り込んでこないよう予約しておいて使うsallocコマンドがあります。ユーザーはジョブがどのマシンで動いているかを気にする必要はありません。ジョブはキューという待ち行列で管理されます。ファミレスの入り口でいつもやる名前と人数などを書き込んでおくアレと一緒で、席が空いたら店員(スケジューラ)が先頭から順番に割り当てていきます。計算ノードにログインして勝手にジョブを起動してしまうことは出来ますが、そういうマナー違反は社会的に抹殺されることでしょう。ちゃんとスケジューラを使うようにして(させて)ください。待ち行列のことをキューと呼び、キューの状況を見るのがsqueueコマンドです。

$ squeue
        JOBID PARTITION     NAME     USER ST      TIME  NODES NODELIST(REASON)

まだ何もしてないので空っぽです。 そこで以下のようなジョブスクリプトを書いて、どさっとキューに投げ込んでみましょう。sbatchコマンドを使うのでした。

$ cat job.sh
#!/bin/bash 
date >> jobs.log
hostname >> jobs.log
sleep 180

$ sbatch job.sh    # これを15回ほど繰り返す。
Submitted batch job 2
$ sbatch job.sh
Submitted batch job 3
......

ジョブにはユニークなIDがふられていき、この番号でジョブを管理できます。さてキューの状態を見てみましょう。

$ squeue 
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 2   compute   job.sh   centos PD       0:00      1 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                 3   compute   job.sh   centos PD       0:00      1 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
 ...中略... 
                14   compute   job.sh   centos PD       0:00      1 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)
                15   compute   job.sh   centos PD       0:00      1 (Nodes required for job are DOWN, DRAINED or reserved for jobs in higher priority partitions)

処理を実行するための計算ノードがないのでステータス(ST)がペンディング(PD)のままです。しかし3〜5分ほどしてsinfoを実行すると以下のようにノードが7に増えて、NODELISTにホスト名が並びます。そしてsqueueの結果も変わり、ジョブのステータスは実行中(R)となり、実際に割り当てられた計算ノードのホスト名が表示されます。

$ sinfo 
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
compute*     up   infinite      7  alloc ip-10-0-20-123,ip-10-0-21-4,ip-10-0-23-68,ip-10-0-24-245,ip-10-0-27-19,ip-10-0-28-110,ip-10-0-29-54

$ squeue 
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 2   compute   job.sh   centos  R       0:13      1 ip-10-0-20-123
                 3   compute   job.sh   centos  R       0:13      1 ip-10-0-20-123
...中略...
                 4   compute   job.sh   centos  R       0:13      1 ip-10-0-21-4
                 5   compute   job.sh   centos  R       0:13      1 ip-10-0-21-4

ここで起動しているc5.largeはコア数が2のインスタンスなので、2〜15の14個のジョブを同時に終わらせるには7ノードあれば実現できるため、7インスタンスが起動されています。スケジューラは起動して利用可能になった計算ノードへ、オプションでコア数などの要求がない今回のジョブについて1つずつを割り当てて実行しています。

デフォルトでは計算ノード数は最小値が0、最大値が10となっていますが、起動が許されているインスタンス数までは拡大可能ですし、最小値に0以外を設定すればノードを常時起動させておくことも出来ます。キューが空いてジョブが数分間投入されなければ、自動的に計算ノードはシャットダウンされ、無駄な課金を防ぎます。マスターとクラスター設定は残るので、またジョブを投入したら計算ノードが起動します。HPCでは計算ノードにハイスペックなインスタンスを使うことが多いので、それを多数起動していてはお金がいくらあっても足りません。configファイルでspotとなっているのに気づいた方はいらっしゃるかもしれませんが、指値を入れなければオンデマンド料金が自動的に設定されます。素晴らしいですね

さて、出力結果jobs.logを見てみましょう。

$ cat jobs.log
Mon Mar 23 02:32:24 UTC 2020
Mon Mar 23 02:32:24 UTC 2020
ip-10-0-23-68
ip-10-0-23-68
32:24 UTC 2020
ip-10-0-20-123
2:24 UTC 2020
ip-10-0-27-19ipip-10-0-29-54
ip-10-0-27-19

データが上書きされて壊れていることが分かります。ホームディレクトリはホストノードのものをNFS共有していますが、たった十数個でも複数プロセスからNFS上の単一ファイルに書き込もうとするとこういうことが起こります。NFSはファイルのロックや同期に関して厳密ではなく、ばらばらに動いているうちは良いのですが、複数のプロセスがファイルへ書き込む必要がある場合や、あるジョブの出力に依存して次のジョブが実行される場合などには、かなり意識していないと問題が顕在化します。Lustreの上でこれをやると、行の順番こそ保証されませんがデータの上書きなどが生ずることはありません。ちょっとやってみましょう。

はじめてのLustre

`/.parallelcluster/configに追加するのは最小4行だけです。

[cluster default]
...中略...
fsx_settings = lustre-blog    # ここで名前を付けます。

[fsx lustre-blog]             # ここで名前を付けた設定を書きます。
deployment_type = SCRATCH_2   # SCRATCH_1(デフォルト), SCRATCH_2, PERSISTENT_1 が利用可能
shared_dir = /lustre          # 各ノードでの共有ディレクトリ
storage_capacity = 1200       # 容量(GB)

deployment_typeはSCRATCH_1が従来通りのFSx for Lustreで、SCRATCH_2が最近アップデートされたバーストできるもの、そして冗長構成が入った永続的ストレージと呼ばれるモードがPERSISTENT_1になります。目的に応じて設定します。容量が最小1.2TBで、2.4TB、3.6TBとなっているのは、OSTのサイズが1.2TBごとなのでしょう。そしてスループットが容量指定できまるのは、OSS/OSTが増えていくことに対応しているのだと理解できます。逆に数百GBを超えるようなファイルを抱える場合には、ストライプ設定を入れないと厳しいかもしれません。実際にLustreの構成情報をとってみましょう。

$ lfs df      # ターゲット別に空き容量を見ます。
UUID                   1K-blocks        Used   Available Use% Mounted on
3ovshbmv-MDT0000_UUID    36030976        5248    36023680   0% /lustre[MDT:0]
3ovshbmv-OST0000_UUID  1168351232        4608  1168344576   0% /lustre[OST:0]
filesystem_summary:   1168351232        4608  1168344576   0% /lustre

$ lfs df -i   # i-node数(保存可能ファイル数)に関する空き容量を見ます。
UUID                      Inodes       IUsed       IFree IUse% Mounted on
3ovshbmv-MDT0000_UUID     7188215         221     7187994   0% /lustre[MDT:0]
3ovshbmv-OST0000_UUID   241884486         224   241884262   0% /lustre[OST:0]
filesystem_summary:      7188215         221     7187994   0% /lustre

MDTが36GB、OSTが1.2TBが1つずつという構成で、そもそも並列にすらなっていません。ジョブ実行スクリプトを書き換えて/lustre/jobs.logへ追記するようにし、sbatchコマンドで20個同時に投げ込んでみます。

$ for i in {1..20}; do sbatch jobs.sh; done
Submitted batch job 3
Submitted batch job 4
...中略...
$ cat /lustre/jobs.log 
Mon Mar 18 06:52:04 UTC 2020
Mon Mar 18 06:52:04 UTC 2020
ip-10-0-17-130
ip-10-0-17-130
Mon Mar 18 06:52:04 UTC 2020
Mon Mar 18 06:52:04 UTC 2020
ip-10-0-17-162
ip-10-0-17-162
Mon Mar 18 06:52:04 UTC 2020
Mon Mar 18 06:52:04 UTC 2020
ip-10-0-19-32
ip-10-0-19-32
...中略...

ファイルが壊れることもなく、ちゃんとしていますね。今回はスパコン(HPCクラスター)を作る練習と使う練習をして、LustreとNFSの顕著な違いと実際に起こりうる問題を見てもらいました。前回少し触れたように、Lustreはクラスターの多数のノードを一つのシステムとして見なせるような作りになっていることの片鱗を感じ取っていただければ今回の記事は成功です。

さて、クラスターはしばらく使わないのであれば、pcluster delete tech-blogのようにして止めておきましょう。この設定で起動しているマスターノードはスポットで起動しているt3.microなので課金も大したことはないのでしょうが、Lustreは常時稼働です。不要なものを起こしておいて良いことはありません。

次回こそは事例をやりたいと思います。