🔎

CDKでAWS Batch環境を作成する

2023/07/14に公開

はじめに

久しぶりにAWS BatchをCDKで書こうとしたら、過去のソースが動かなくなっていました。
リリースノートをみたところ、v2.74.0にて
コンピューティング環境ジョブ定義の2つの定義方法が変わったみたいです。

https://github.com/aws/aws-cdk/releases/tag/v2.74.0

ComputeEnvironment has been removed and replaced by ManagedEc2EcsComputeEnvironment, ManagedEc2EksComputeEnvironment, and UnmanagedComputeEnvironment.
JobDefinition has been removed and replaced by EcsJobDefinition, EksJobDefinition, and MultiNodeJobDefinition

ECSとEKSとそれ以外を基準に分かれているので、便利になりました!
(ECS以外での環境でAWS Batchを利用したことはないですが…)。

2.87.0-alpha.0時点でのドキュメントを見つつ変更をしていたのですが、
ちらほらとハマったので全体的な構成を共有します。
EC2とECSを利用してのAWS Batch環境になっています。

2.74.0-alpha.0以降の書き方

BatchStack.ts
import {
  App,
  Size,
  Stack,
  StackProps,
  aws_ec2,
  aws_ecr,
  aws_ecs,
  aws_iam,
  Duration,
} from 'aws-cdk-lib';
import * as aws_batch_alpha from '@aws-cdk/aws-batch-alpha';

const prefix = "zenn-example"
const ecrTag = "v0.1.0"

interface IParameters {
  vpcId: `vpc-${string}`
  ecrRepositoryArn: string
}

export class BatchStack extends Stack {
  constructor(scope: App, id: string, params: IParameters, props?: StackProps) {
    super(scope, id, props);

    const vpc = aws_ec2.Vpc.fromLookup(this, "vpc", {
      vpcId: params.vpcId
    })

    const securityGroup = new aws_ec2.SecurityGroup(this, "security-group", {
      vpc,
      securityGroupName: `${prefix}-security-group`,
      allowAllOutbound: true,
    })

    const instanceRole = new aws_iam.Role(this, "instance-role", {
      roleName: `${prefix}-instance-role`,
      assumedBy: new aws_iam.ServicePrincipal('ec2.amazonaws.com'),
      managedPolicies: [
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, `AmazonEC2ContainerServiceforEC2Role-batch`, "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
        ),
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, `CloudWatchLogsFullAccess-batch`, "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
        )
      ]
    })
    const instanceProfile = new aws_iam.CfnInstanceProfile(this, "instance-profile", {
      instanceProfileName: `${prefix}-instance-profile`,
      roles: [instanceRole.roleName]
    })

    const batchComputeEnvironment = new aws_batch_alpha.ManagedEc2EcsComputeEnvironment(this, "batch-environment", {
      vpc,
      securityGroups: [securityGroup],
      maxvCpus: 16,
      minvCpus: 0,
      instanceRole: instanceRole,
      vpcSubnets: {
        subnetType: aws_ec2.SubnetType.PUBLIC,
      },
      computeEnvironmentName: `${prefix}-compute-environment`,
      spot: true,
      useOptimalInstanceClasses: false,
      instanceTypes: [
        new aws_ec2.InstanceType("c6i.2xlarge"),
      ],
    })

    const containerImage = aws_ecs.ContainerImage.fromEcrRepository(
      aws_ecr.Repository.fromRepositoryArn(this, "ecr", params.ecrRepositoryArn), ecrTag
    )
    const jobRole = new aws_iam.Role(this, `batch-job-role`, {
      roleName: `${prefix}-batch-job-role`,
      assumedBy: new aws_iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
      managedPolicies: [
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, "AmazonECSTaskExecutionRolePolicy-job", "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
        ),
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, "CloudWatchLogsFullAccess-job", "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
        )
      ]
    })

    const batchJobQueue = new aws_batch_alpha.JobQueue(this, "batch-job-queue", {
      jobQueueName: `${prefix}-job-queue`,
      computeEnvironments: [
        {
          computeEnvironment: batchComputeEnvironment,
          order: 1
        }
      ],
      priority: 1
    })

    const batchJobDefinition = new aws_batch_alpha.EcsJobDefinition(this, "batch-job-definition", {
      jobDefinitionName: `${prefix}-job-def`,
      timeout: Duration.hours(2),
      container: new aws_batch_alpha.EcsEc2ContainerDefinition(this, 'containerDefn', {
        image: containerImage,
        memory: Size.gibibytes(15),
        cpu: 8,
        readonlyRootFilesystem: true,
        privileged: false,
        user: "user",
        jobRole,
        logging: new aws_ecs.AwsLogDriver({streamPrefix: prefix})
      }),
    })
  }
}

一応以下に2.73.0-alpha.0以前の書き方も残しておきます。

以前の書き方。
BatchStack.ts
import {
  App,
  Stack,
  StackProps,
  aws_ec2,
  aws_ecr,
  aws_ecs,
  aws_iam,
} from 'aws-cdk-lib';
import * as aws_batch_alpha from '@aws-cdk/aws-batch-alpha';

const prefix = "zenn-example"
const ecrTag = "v0.1.0"

interface IParameters {
  vpcId: `vpc-${string}`
  ecrRepositoryArn: string
}

export class BatchStack extends Stack {
  constructor(scope: App, id: string, params: IParameters, props?: StackProps) {
    super(scope, id, props);

    const vpc = aws_ec2.Vpc.fromLookup(this, "vpc", {
      vpcId: params.vpcId
    })

    const securityGroup = new aws_ec2.SecurityGroup(this, "security-group", {
      vpc: vpc,
      securityGroupName: `${prefix}-security-group`,
      allowAllOutbound: true
    })

    const instanceRole = new aws_iam.Role(this, "instance-role", {
      roleName: `${prefix}-instance-role`,
      assumedBy: new aws_iam.ServicePrincipal('ec2.amazonaws.com'),
      managedPolicies: [
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, `AmazonEC2ContainerServiceforEC2Role-batch`, "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
        ),
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, `CloudWatchLogsFullAccess-batch`, "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
        )
      ]
    })
    const instanceProfile = new aws_iam.CfnInstanceProfile(this, "instance-profile", {
      instanceProfileName: `${prefix}-instance-profile`,
      roles: [instanceRole.roleName]
    })

    const containerImage = aws_ecs.ContainerImage.fromEcrRepository(
      aws_ecr.Repository.fromRepositoryArn(this, "ecr", params.ecrRepositoryArn), ecrTag
    )

    const batchComputeEnvironment = new aws_batch_alpha.ComputeEnvironment(this, "batch-environment", {
      computeEnvironmentName: `${prefix}-compute-environment`,
      computeResources: {
        vpc: vpc,
        maxvCpus: 16,
        minvCpus: 0,
        securityGroups: [securityGroup],
        instanceRole: instanceProfile.attrArn,
        type: aws_batch_alpha.ComputeResourceType.SPOT,
        instanceTypes: [
        	new aws_ec2.InstanceType("c6i.2xlarge")
        ]
      }
    })

    const jobRole = new aws_iam.Role(this, `batch-job-role`, {
      roleName: `${prefix}-batch-job-role`,
      assumedBy: new aws_iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
      managedPolicies: [
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, "AmazonECSTaskExecutionRolePolicy-job", "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
        ),
        aws_iam.ManagedPolicy.fromManagedPolicyArn(
          this, "CloudWatchLogsFullAccess-job", "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
        )
      ]
    })

    const batchJobQueue = new aws_batch_alpha.JobQueue(this, "batch-job-queue", {
      jobQueueName: `${prefix}-job-queue`,
      computeEnvironments: [
        {
          computeEnvironment: batchComputeEnvironment,
          order: 1
        }
      ],
      priority: 1
    })

    const batchJobDefinition = new aws_batch_alpha.JobDefinition(this, "batch-job-definition", {
      jobDefinitionName: `${prefix}-job-def`,
      container: {
        image: containerImage,
        jobRole: jobRole,
        vcpus: 8,
        memoryLimitMiB: 14000,
        logConfiguration: {
          logDriver: aws_batch_alpha.LogDriver.AWSLOGS
        },
        readOnly: true,
        privileged: false,
        user: "user"
      }
    })
  }
}

おわりに

元々@aws-cdk/aws-batch-alphaはalpha版なので大きな変更は入るかなと思っていましたが、
実際に変更が入るとびっくりしました。

GitHubで編集を提案

Discussion