
AWS Parallel Computing Service が EC2 Capacity Blocks for ML をサポートしました
はじめに
AWS Parallel Computing Service (PCS) が Amazon EC2 Capacity Blocks for ML をサポートしました。これにより、GPU インスタンスを事前予約でき GPU リソースの確保が保証され、大規模な機械学習モデルの学習やシミュレーションを計画的に PCS で実行できます。
EC2 Capacity Blocks for ML とは
EC2 Capacity Blocks for ML は、GPU インスタンスの容量を事前予約できるサービスです。
- GPU インスタンスの事前予約、前払い必須
- 予約価格は需要と供給に基づいて動的に変動(割引を受けられる)
- 対応インスタンスは P 系、Trn 系をサポート、G 系はサポート対象外
Capacity Blocks for ML の詳細については以下の記事を参照ください。
設定方法
Capacity Blocks for ML で GPU リソースを予約するような予定がないため、本記事では PCS の設定方法を中心に解説します。
検証環境
Slurm のバージョンは 24.11 で、ログインノード、コンピュートノードは Amazon Linux 2 を使用しています。
PCS は以下の構成で構築しました。Capacity Blocks for ML を設定する前の PCS クラスターをデプロイした CloudFormation テンプレートを参考にしてください。
PCS クラスター作成に使用した CloudFormation テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Simple AWS PCS cluster for verification - 1 login node (t3.micro) and up to 4 compute nodes (m7i.large) with EFS storage'
Parameters:
ClusterName:
Type: String
Default: 'simple-pcs-cluster'
Description: 'Name of the PCS cluster'
AMIID:
Type: AWS::EC2::Image::Id
Default: 'ami-04b5a084f47547e80'
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: '10.0.0.0/16'
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${ClusterName}-vpc'
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${ClusterName}-igw'
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# Public Subnet
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: '10.0.1.0/24'
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${ClusterName}-public-subnet-1'
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: '10.0.2.0/24'
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${ClusterName}-public-subnet-2'
PublicSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [2, !GetAZs '']
CidrBlock: '10.0.3.0/24'
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${ClusterName}-public-subnet-3'
# Private Subnet
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: '10.0.11.0/24'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-private-subnet-1'
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: '10.0.12.0/24'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-private-subnet-2'
PrivateSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [2, !GetAZs '']
CidrBlock: '10.0.13.0/24'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-private-subnet-3'
# Isolated Subnet
IsolatedSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: '10.0.21.0/24'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-isolated-subnet-1'
IsolatedSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: '10.0.22.0/24'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-isolated-subnet-2'
IsolatedSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [2, !GetAZs '']
CidrBlock: '10.0.23.0/24'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-isolated-subnet-3'
# NAT Gateway
NatGateway1EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway1EIP.AllocationId
SubnetId: !Ref PublicSubnet1
# Route Tables
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${ClusterName}-public-routes'
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
PublicSubnet3RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet3
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${ClusterName}-private-routes-1'
DefaultPrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref NatGateway1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet1
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet2
PrivateSubnet3RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet3
# Security Groups
LoginSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub '${ClusterName}-login-sg'
GroupDescription: 'Security group for PCS login nodes'
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: 'Allow all outbound traffic'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-login-sg'
ComputeSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub '${ClusterName}-compute-sg'
GroupDescription: 'Security group for PCS compute nodes'
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/16
Description: 'SSH access from VPC for PCS requirement'
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: 'Allow all outbound traffic'
Tags:
- Key: Name
Value: !Sub '${ClusterName}-compute-sg'
# Allow all traffic from login to compute
ComputeSecurityGroupIngressFromLogin:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref ComputeSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref LoginSecurityGroup
Description: 'Allow all traffic from login nodes'
# Allow all traffic from compute to login
LoginSecurityGroupIngressFromCompute:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref LoginSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref ComputeSecurityGroup
Description: 'Allow all traffic from compute nodes'
# Self-referencing rules for login nodes
LoginSecurityGroupIngressSelfRef:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref LoginSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref LoginSecurityGroup
Description: 'Allow all traffic within login nodes'
# Self-referencing rules for compute nodes
ComputeSecurityGroupIngressSelfRef:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref ComputeSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref ComputeSecurityGroup
Description: 'Allow all traffic within compute nodes'
EFSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub '${ClusterName}-efs-sg'
GroupDescription: 'Security group for EFS'
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
SourceSecurityGroupId: !Ref LoginSecurityGroup
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
SourceSecurityGroupId: !Ref ComputeSecurityGroup
Tags:
- Key: Name
Value: !Sub '${ClusterName}-efs-sg'
# EFS
EFSFileSystem:
Type: AWS::EFS::FileSystem
Properties:
PerformanceMode: generalPurpose
ThroughputMode: bursting
Encrypted: true
FileSystemTags:
- Key: Name
Value: !Sub '${ClusterName}-efs'
EFSMountTarget1:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnet1
SecurityGroups:
- !Ref EFSSecurityGroup
EFSMountTarget2:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnet2
SecurityGroups:
- !Ref EFSSecurityGroup
EFSMountTarget3:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnet3
SecurityGroups:
- !Ref EFSSecurityGroup
# IAM Role for PCS Instance
PCSInstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'AWSPCS-${AWS::StackName}-${AWS::Region}'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Policies:
- PolicyName: PCSComputeNodePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- pcs:RegisterComputeNodeGroupInstance
Resource: '*'
PCSInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref PCSInstanceRole
# Launch Template for Login Nodes
LoginLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub '${ClusterName}-login-launch-template'
LaunchTemplateData:
ImageId: !Ref AMIID
SecurityGroupIds:
- !Ref LoginSecurityGroup
IamInstanceProfile:
Arn: !GetAtt PCSInstanceProfile.Arn
UserData:
Fn::Base64: !Sub |
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
--==BOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
yum update -y
yum install -y amazon-efs-utils
mkdir -p /shared
echo "${EFSFileSystem}.efs.${AWS::Region}.amazonaws.com:/ /shared efs defaults,_netdev" >> /etc/fstab
mount -a
chown ec2-user:ec2-user /shared
chmod 755 /shared
# Add Slurm commands to PATH for all users
echo 'export PATH=/opt/slurm/bin:$PATH' >> /etc/profile.d/slurm.sh
chmod +x /etc/profile.d/slurm.sh
# Add to ec2-user's bashrc for immediate availability
echo 'export PATH=/opt/slurm/bin:$PATH' >> /home/ec2-user/.bashrc
chown ec2-user:ec2-user /home/ec2-user/.bashrc
--==BOUNDARY==--
MetadataOptions:
HttpTokens: required
HttpPutResponseHopLimit: 2
HttpEndpoint: enabled
# Launch Template for Compute Nodes
ComputeLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub '${ClusterName}-compute-launch-template'
LaunchTemplateData:
ImageId: !Ref AMIID
SecurityGroupIds:
- !Ref ComputeSecurityGroup
IamInstanceProfile:
Arn: !GetAtt PCSInstanceProfile.Arn
UserData:
Fn::Base64: !Sub |
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
--==BOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
yum update -y
yum install -y amazon-efs-utils
mkdir -p /shared
echo "${EFSFileSystem}.efs.${AWS::Region}.amazonaws.com:/ /shared efs defaults,_netdev" >> /etc/fstab
mount -a
chown ec2-user:ec2-user /shared
chmod 755 /shared
# Add Slurm commands to PATH for all users
echo 'export PATH=/opt/slurm/bin:$PATH' >> /etc/profile.d/slurm.sh
chmod +x /etc/profile.d/slurm.sh
# Add to ec2-user's bashrc for immediate availability
echo 'export PATH=/opt/slurm/bin:$PATH' >> /home/ec2-user/.bashrc
chown ec2-user:ec2-user /home/ec2-user/.bashrc
--==BOUNDARY==--
MetadataOptions:
HttpTokens: required
HttpPutResponseHopLimit: 2
HttpEndpoint: enabled
# PCS Cluster
PCSCluster:
Type: AWS::PCS::Cluster
Properties:
Name: !Ref ClusterName
Size: SMALL
Scheduler:
Type: SLURM
Version: '24.11'
Networking:
SubnetIds:
- !Ref PrivateSubnet1
SecurityGroupIds:
- !Ref LoginSecurityGroup
- !Ref ComputeSecurityGroup
# Login Node Group
LoginNodeGroup:
Type: AWS::PCS::ComputeNodeGroup
Properties:
ClusterId: !GetAtt PCSCluster.Id
Name: login
ScalingConfiguration:
MinInstanceCount: 1
MaxInstanceCount: 1
IamInstanceProfileArn: !GetAtt PCSInstanceProfile.Arn
CustomLaunchTemplate:
TemplateId: !Ref LoginLaunchTemplate
Version: 1
SubnetIds:
- !Ref PublicSubnet1
AmiId: !Ref AMIID
InstanceConfigs:
- InstanceType: 't3.micro'
# Compute Node Group
ComputeNodeGroup:
Type: AWS::PCS::ComputeNodeGroup
Properties:
ClusterId: !GetAtt PCSCluster.Id
Name: compute
ScalingConfiguration:
MinInstanceCount: 0
MaxInstanceCount: 4
IamInstanceProfileArn: !GetAtt PCSInstanceProfile.Arn
CustomLaunchTemplate:
TemplateId: !Ref ComputeLaunchTemplate
Version: 1
SubnetIds:
- !Ref PrivateSubnet1
AmiId: !Ref AMIID
InstanceConfigs:
- InstanceType: 'm7i.large'
# Queue
ComputeQueue:
Type: AWS::PCS::Queue
Properties:
ClusterId: !GetAtt PCSCluster.Id
Name: compute-queue
ComputeNodeGroupConfigurations:
- ComputeNodeGroupId: !GetAtt ComputeNodeGroup.Id
Outputs:
ClusterId:
Description: 'The ID of the PCS cluster'
Value: !GetAtt PCSCluster.Id
Export:
Name: !Sub '${AWS::StackName}-ClusterId'
VPCId:
Description: 'The ID of the VPC'
Value: !Ref VPC
Export:
Name: !Sub '${AWS::StackName}-VPCId'
EFSFileSystemId:
Description: 'The ID of the EFS file system'
Value: !Ref EFSFileSystem
Export:
Name: !Sub '${AWS::StackName}-EFSFileSystemId'
PCSConsoleUrl:
Description: 'URL to access the cluster in the PCS console'
Value: !Sub 'https://${AWS::Region}.console.aws.amazon.com/pcs/home?region=${AWS::Region}#/clusters/${PCSCluster.Id}'
Export:
Name: !Sub '${AWS::StackName}-PCSConsoleUrl'
EC2ConsoleUrl:
Description: 'URL to access login node instances via Session Manager'
Value: !Sub 'https://${AWS::Region}.console.aws.amazon.com/ec2/home?region=${AWS::Region}#Instances:instanceState=running;tag:aws:pcs:compute-node-group-id=${LoginNodeGroup.Id}'
Export:
Name: !Sub '${AWS::StackName}-EC2ConsoleUrl'
PCSInstanceRoleArn:
Description: 'ARN of the PCS Instance Role with vended log delivery permissions'
Value: !GetAtt PCSInstanceRole.Arn
Export:
Name: !Sub '${AWS::StackName}-PCSInstanceRoleArn'
キャパシティ予約の作成
AWS マネジメントコンソールまたは AWS CLI で Capacity Block for ML を利用して GPU インスタンスを予約します。予約時に以下を指定します。
- インスタンスタイプ
- インスタンス数
- 予約期間: 最大 182 日
AZ 指定で予約することになります。実行予定のサブネットの AZ かどうか事前確認必須です。
補足
2025 年 8 月に P 系の中では格安の p5.4xlarge をリリースされました。普段 Capacity Blocks for ML の検証は高額で予約できなかったのですが手の届く価格になってきました。
予約の終了時間が一律 20:30(JST)に設定されています。そのため、すぐに予約して利用する場合は 24 時間を切って予約できる場合もあります。現在は朝 8 時のため残り時間が 10 時間ほどありますが、もっと遅い時間に確認すれば格安で試せる可能性があります。ただし、AZ 単位で指定となるため稼働させたい AZ かどうかは予約前に確認してください。
起動テンプレートの設定
PCS クラスターで使用する起動テンプレートを作成し、キャパシティ予約の ID を指定します。
起動テンプレートではインスタンスタイプを指定する必要ありません。次の PCS クラスターの設定で指定します。
PCS クラスターの設定
GPU インスタンス用のコンピュートノードグループを作成します。設定時のポイントは以下の 4 つです。
- 起動テンプレート: 先ほど作成済みのテンプレートを指定
- サブネット: 予約した AZ と同じサブネットを選択
- インスタンスタイプ: 予約したインスタンスタイプを指定
- 購入オプション: 「キャパシティブロック」を選択
設定完了
ログインノードに接続して、作成したコンピュートノードグループのキューを利用するだけです。
$ sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
gpu-queue up infinite 4 idle~ compute-[1-4]
まとめ
AWS PCS が EC2 Capacity Blocks for ML をサポートしたことで、大規模な GPU ワークロードの実行が安心して実行できるようになりました。HPC や機械学習のワークロードで GPU を必要としている方は、PCS でも Capacity Blocks for ML をぜひ活用してください。
おわりに
てっきり PCS は Capacity Blocks for ML をサポートしているものだと思い込んでいました。