AWS Parallel Computing Service が EC2 Capacity Blocks for ML をサポートしました

AWS Parallel Computing Service が EC2 Capacity Blocks for ML をサポートしました

2025.09.23

はじめに

AWS Parallel Computing Service (PCS) が Amazon EC2 Capacity Blocks for ML をサポートしました。これにより、GPU インスタンスを事前予約でき GPU リソースの確保が保証され、大規模な機械学習モデルの学習やシミュレーションを計画的に PCS で実行できます。

PCS-CacpacityBlocskforML(2).png

https://aws.amazon.com/jp/about-aws/whats-new/2025/09/aws-parallel-computing-service-ec2-capacity-blocks-ml/

https://aws.amazon.com/jp/blogs/hpc/announcing-capacity-blocks-support-for-aws-parallel-computing-service/

EC2 Capacity Blocks for ML とは

EC2 Capacity Blocks for ML は、GPU インスタンスの容量を事前予約できるサービスです。

  • GPU インスタンスの事前予約、前払い必須
  • 予約価格は需要と供給に基づいて動的に変動(割引を受けられる)
  • 対応インスタンスは P 系、Trn 系をサポート、G 系はサポート対象外

Capacity Blocks for ML の詳細については以下の記事を参照ください。

https://dev.classmethod.jp/articles/understanding-amazon-ec2-for-ml-capacity-blocks/

設定方法

Capacity Blocks for ML で GPU リソースを予約するような予定がないため、本記事では PCS の設定方法を中心に解説します。

PCS-CacpacityBlocskforML(1).png

検証環境

Slurm のバージョンは 24.11 で、ログインノード、コンピュートノードは Amazon Linux 2 を使用しています。

パラレルコンピューティングサービス___ap-northeast-1_🔊.png

PCS は以下の構成で構築しました。Capacity Blocks for ML を設定する前の PCS クラスターをデプロイした CloudFormation テンプレートを参考にしてください。

PCS-CacpacityBlocskforML.png

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 かどうか事前確認必須です。

EC2___ap-northeast-1.png

補足

2025 年 8 月に P 系の中では格安の p5.4xlarge をリリースされました。普段 Capacity Blocks for ML の検証は高額で予約できなかったのですが手の届く価格になってきました。

https://dev.classmethod.jp/articles/ec2-p5-4xlarge-single-h100-gpu-launch/

予約の終了時間が一律 20:30(JST)に設定されています。そのため、すぐに予約して利用する場合は 24 時間を切って予約できる場合もあります。現在は朝 8 時のため残り時間が 10 時間ほどありますが、もっと遅い時間に確認すれば格安で試せる可能性があります。ただし、AZ 単位で指定となるため稼働させたい AZ かどうかは予約前に確認してください。

EC2___ap-northeast-1_🔊.png

起動テンプレートの設定

PCS クラスターで使用する起動テンプレートを作成し、キャパシティ予約の ID を指定します。

テンプレートを変更__新しいバージョンを作成____EC2___ap-northeast-1_🔊.png

起動テンプレートではインスタンスタイプを指定する必要ありません。次の PCS クラスターの設定で指定します。

PCS クラスターの設定

GPU インスタンス用のコンピュートノードグループを作成します。設定時のポイントは以下の 4 つです。

  1. 起動テンプレート: 先ほど作成済みのテンプレートを指定
  2. サブネット: 予約した AZ と同じサブネットを選択
  3. インスタンスタイプ: 予約したインスタンスタイプを指定
  4. 購入オプション: 「キャパシティブロック」を選択

パラレルコンピューティングサービス___ap-northeast-1_🔊-2.png

設定完了

ログインノードに接続して、作成したコンピュートノードグループのキューを利用するだけです。

			
			$ 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 をサポートしているものだと思い込んでいました。

参考

この記事をシェアする

FacebookHatena blogX

関連記事