作業の目的
AWS上にSlurmクラスターを構築する必要があったので、Terraformを使って完全自動化で構築できるようにした。
構築した環境
- フロントエンドサーバ: Slurmコントローラー(slurmctld)を実行
- ジョブサーバ: Slurmワーカー(slurmd)を実行
- 固定IPアドレス: Elastic IPでIPアドレスを固定
- 共有ストレージ: EFSとEBSボリューム(1TB)をNFSで共有
- 完全自動化: インスタンス起動時にすべての設定が自動実行
アーキテクチャ

作業の流れ
最初にterraform planを実行したところ、以下のエラーが発生した:
Error: Self-referential block on main.tf line 53, in resource "aws_security_group" "frontend_job_sg": 53: security_groups = [aws_security_group.frontend_job_sg.id] Configuration for aws_security_group.frontend_job_sg may not refer to itself.
セキュリティグループが自分自身を参照していた。同じセキュリティグループ内のリソース間で通信を許可するには、self = trueを使う必要があった。修正した:
ingress { description = "NFS for EFS" from_port = 2049 to_port = 2049 protocol = "tcp" self = true # これで解決 }
セキュリティグループを設定した。SSHとNFSの通信を許可する必要があった:
resource "aws_security_group" "frontend_job_sg" { name = "frontend-job-sg" description = "Allow SSH from internet and NFS inside SG" vpc_id = var.vpc_id # SSH access ingress { description = "SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = var.ssh_allowed_cidrs } # NFS for EFS and shared storage ingress { description = "NFS for EFS" from_port = 2049 to_port = 2049 protocol = "tcp" self = true } }
ストレージの設定
EFSの追加
最初はEFSだけを設定した。Slurmの設定ファイルやMunge keyを共有するために必要だった:
resource "aws_efs_file_system" "work" { creation_token = "work-efs" lifecycle_policy { transition_to_ia = "AFTER_30_DAYS" } tags = { Name = "work-efs" } }
1TBのEBSボリュームを追加
後から、作業用の1TBディスクが必要になった。フロントエンドサーバにアタッチして、NFSでジョブサーバと共有することにした:
resource "aws_ebs_volume" "shared_storage" { availability_zone = aws_instance.frontend.availability_zone size = 1024 # 1TB type = "gp3" encrypted = true tags = { Name = "shared-storage-1tb" } } resource "aws_volume_attachment" "shared_storage_attach" { device_name = "/dev/sdf" volume_id = aws_ebs_volume.shared_storage.id instance_id = aws_instance.frontend.id }
固定IPアドレスの設定
インスタンスを再起動しても同じIPアドレスを使いたかったので、Elastic IPを設定した:
resource "aws_eip" "frontend_eip" { domain = "vpc" tags = { Name = "frontend-server-eip" } } resource "aws_eip_association" "frontend_eip_assoc" { instance_id = aws_instance.frontend.id allocation_id = aws_eip.frontend_eip.id }
インスタンスの自動設定
フロントエンドサーバのuser_data
インスタンス起動時に自動で設定されるように、user_dataスクリプトを書いた。以下の処理を自動化した:
- ホスト名の設定
- パッケージのインストール(Emacs、Docker、Makeなど)
- EFSのマウント
- Slurmコントローラーのインストールと設定
- Munge keyの生成とEFSへの共有
- EBSボリュームのフォーマットとマウント
- NFSサーバの設定
EBSボリュームの自動マウントで苦労した
最初は、EBSボリュームがマウントされない問題に遭遇した。df -hを実行しても/mnt/sharedが表示されなかった。
原因を調べたところ:
- user_dataスクリプトがEBSボリュームのアタッチを待たずに実行されていた
- デバイス名の検出が不十分だった(nvme1n1を固定で指定していた)
確認方法:
lsblk ls -la /dev/nvme*
解決方法:以下のように設定した:
# Setup 1TB shared storage (EBS volume)
# Wait for EBS volume to be attached (max 5 minutes)
MAX_WAIT_EBS=300
ELAPSED_EBS=0
DEVICE=""
while [ -z "$DEVICE" ] && [ $ELAPSED_EBS -lt $MAX_WAIT_EBS ]; do
# Check for NVMe devices (modern instances)
for nvme_dev in /dev/nvme[1-9]n1; do
if [ -e "$nvme_dev" ]; then
# Check if it's not the root device
if ! mountpoint -q "$nvme_dev" 2>/dev/null; then
DEVICE="$nvme_dev"
break
fi
fi
done
if [ -z "$DEVICE" ]; then
sleep 5
ELAPSED_EBS=$((ELAPSED_EBS + 5))
echo "Waiting for EBS volume... ($ELAPSED_EBS/$MAX_WAIT_EBS seconds)"
fi
done
# Format and mount
if ! blkid $DEVICE > /dev/null 2>&1; then
mkfs.ext4 -F $DEVICE
fi
mkdir -p /mnt/shared
echo "$DEVICE /mnt/shared ext4 defaults,nofail 0 2" >> /etc/fstab
mount $DEVICE /mnt/shared
# Install and configure NFS server
apt-get install -y nfs-kernel-server
chmod 777 /mnt/shared
echo "/mnt/shared *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
exportfs -ra
systemctl enable nfs-kernel-server
systemctl restart nfs-kernel-server
この改善により、NVMeデバイスを動的に検出し、最大5分間待機するようになった。