Cloudformationを使ってAWS Batch環境を構築してみる
どうも!西村祐二@大阪です。
2017年8月にCloudformationにてAWS Batchがサポートされましたので、Cloudformationの勉強を兼ねて試してみたいと思います。
さっそくやっていきましょう!
使用できるリソースタイプを確認
ドキュメントよりテンプレート作成時に使用できるリソースタイプを確認します。 AWS Batchでは下記3つのリソースタイプが使用できるようです。
AWS::Batch::ComputeEnvironment AWS::Batch::JobDefinition AWS::Batch::JobQueue
環境
Mac: macOS Sierra 10.12.6 AWS CLI: aws-cli/1.11.145 Python/3.6.1 Darwin/16.7.0 botocore/1.7.3
事前準備
下記ブログで作成したAWS Batch環境を見本に今回作成しますので事前に確認をお願いします。
aws-cliのアップデート
最新のバージョンへアップデートしておいてください。
$ sudo pip install -U awscli
テンプレート作成
今回、AWS Batchの「Compute environments」「Job queues」「Job definitions」を構築するテンプレートを作成していきます。 いきなり、3つの環境を含めてたテンプレートを作成するのは大変なのでそれぞれ分割してテンプレートを作成し、あとで1つにまとめたいと思います。
Compute environmentsのテンプレートを作成
Compute environmentsを構築するためにはテンプレート内で ServiceRole、SecurityGroupIds、Subnets、InstanceRoleを指定する必要があります。 そのため今回はIAMとVPCも含めたテンプレートを作成したいと思います。
IAMのManagedPolicyArns箇所は適時変更ください。 今回は事前準備のところで紹介した記事を参考にしているためInstance roleにCodeCommitとS3の権限を付与しています。(下記コード134-135行目)
IAMは名前を指定すると重複する可能性があるため、特に指定していません。 指定せずにスタック作成すると任意の文字列を付与してくれます。
今回ソースが長くなりすぎてしまうので、割愛していますが、 Compute environmentsのパラメータを Parametersセッションとして外に出しておくと良いかと思います。
テンプレート作成時の必須の項目などはこちらのドキュメントを確認しながら作成するのが良いかと思います。
AWSTemplateFormatVersion: 2010-09-09 Description: Build AWS Batch environment #========VPC========# Resources: # Create VPC MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsSupport: 'true' EnableDnsHostnames: 'true' InstanceTenancy: default Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"vpc" ] ] # Create Public RouteTable PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref MyVPC Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"pub-route" ] ] # Create Private RouteTable PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref MyVPC Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"pri-route" ] ] # Create Public Subnet A PublicSubnetA: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.0.0/24 MapPublicIpOnLaunch: 'true' AvailabilityZone: "ap-northeast-1a" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PublicSunetA" ] ] PubSubnetARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref PublicRouteTable # Create Public Subnet C PublicSubnetC: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.1.0/24 MapPublicIpOnLaunch: 'true' AvailabilityZone: "ap-northeast-1c" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PublicSunetC" ] ] PubSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetC RouteTableId: !Ref PublicRouteTable # Create Private Subnet A PrivateSubnetA: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.10.0/24 AvailabilityZone: "ap-northeast-1a" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PrivateSubnetA" ] ] PriSubnetARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetA RouteTableId: !Ref PrivateRouteTable # Create Private Subnet C PrivateSubnetC: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.11.0/24 AvailabilityZone: "ap-northeast-1c" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PrivateSubnetC" ] ] PriSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetC RouteTableId: !Ref PrivateRouteTable # Create InternetGateway myInternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"igw" ] ] AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref MyVPC InternetGatewayId: !Ref myInternetGateway # Route for InternetGateway or VPNGateway myRoute: Type: AWS::EC2::Route DependsOn: myInternetGateway Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref myInternetGateway #=======IAM========# # Create ecsInstanceRole ecsInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: ## start Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ## end ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role - arn:aws:iam::aws:policy/AWSCodeCommitFullAccess - arn:aws:iam::aws:policy/AmazonS3FullAccess Path: "/" # Set InstanceProfile ecsInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Roles: - !Ref ecsInstanceRole # Create AWSBatchServiceRole AWSBatchServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: ## start Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - batch.amazonaws.com Action: - sts:AssumeRole ## end ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole Path: "/service-role/" ### ### ---ComputeEnvironment--- ### MyComputeEnv: Type: "AWS::Batch::ComputeEnvironment" Properties: Type: MANAGED #作成したIAMから取得 ServiceRole: !GetAtt AWSBatchServiceRole.Arn ComputeEnvironmentName: cfn-env ComputeResources: MaxvCpus: 256 MinvCpus: 0 DesiredvCpus: 0 SecurityGroupIds: #作成したVPCから取得 - !GetAtt MyVPC.DefaultSecurityGroup Type: EC2 Subnets: #作成したVPCから取得 - !Ref PrivateSubnetA - !Ref PublicSubnetA #作成したVPCから取得 InstanceRole: !GetAtt ecsInstanceProfile.Arn InstanceTypes: - optimal Tags: {"Name": "Batch Instance - cfn test"} State: ENABLED
動作確認
▼スタック作成前に構文チェックを行いエラーがないことを確認します。
$ aws cloudformation validate-template --template-body file://batch-env.yml
▼今回スタック名を「batch-env」として作成します。テンプレートにIAMリソースがある場合、capabilities
オプションを付ける必要があります。
$ aws cloudformation create-stack --stack-name batch-env --template-body file://batch-env.yml --capabilities CAPABILITY_IAM
▼作成したスタックのステータスを確認します。 無事スタックの作成が完了したら下記コマンドより「CREATE_COMPLETE」が表示され、 テンプレートで設定した値で「cfn-env」という名前の「Compute environments」が作成されています。 失敗した場合は「ROLLBACK_COMPLETE」となりロールバックされます。
$ aws cloudformation describe-stacks --stack-name batch-env | awk '{print $NF}'
Job queuesのテンプレートを作成
同様にJob queuesのテンプレートを作成していきます。 ComputeEnvironmentを指定する必要があるので、先程作成した名前を指定しておきます。
### AWSTemplateFormatVersion: 2010-09-09 Description: AWS Batch Job queues from cfn ### ###---Resources:JobQueue--- ### Resources: MyJobQueue: Type: AWS::Batch::JobQueue Properties: ComputeEnvironmentOrder: - Order: 1 ComputeEnvironment: cfn-env State: ENABLED Priority: 1 JobQueueName: cfn-queue
動作確認
▼スタック作成前に構文チェックを行いエラーがないことを確認します。
$ aws cloudformation validate-template --template-body file://batch-queue.yml
▼今回スタック名を「batch-queue」として作成します。
$ aws cloudformation create-stack --stack-name batch-queue --template-body file://batch-queue.yml
▼作成したスタックのステータスを確認します。 無事スタックの作成が完了したら下記コマンドより「CREATE_COMPLETE」が表示され、 テンプレートで設定した値で「cfn-queue」という名前の「Job queues」が作成されています。 失敗した場合は「ROLLBACK_COMPLETE」となりロールバックされます。
$ aws cloudformation describe-stacks --stack-name batch-queue | awk '{print $NF}'
Job definitionsのテンプレートを作成
同様にJob definitionsのテンプレートを作成していきます。
ドキュメントには必須とのは記載ありませんが
RetryStrategy
の設定がないとエラーとなるようです。
指定するコマンド、コンテナイメージは下記ブログで作成したものを指定しています。
### AWSTemplateFormatVersion: 2010-09-09 Description: AWS Batch Job definitions from cfn ### ###---Resources:JobDefinition--- ### Resources: MyJobDefinition: Type: AWS::Batch::JobDefinition Properties: Type: container JobDefinitionName: cfn-def ContainerProperties: Command: - sh - /usr/local/init.sh Memory: 2000 Vcpus: 2 Image: 1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/test-repo:latest RetryStrategy: Attempts: 1
動作確認
▼スタック作成前に構文チェックを行いエラーがないことを確認します。
$ aws cloudformation validate-template --template-body file://batch-def.yml
▼今回スタック名を「batch-def」として作成します。
$ aws cloudformation create-stack --stack-name batch-def --template-body file://batch-def.yml
▼作成したスタックのステータスを確認します。 無事スタックの作成が完了したら下記コマンドより「CREATE_COMPLETE」が表示され、 テンプレートで設定した値で「cfn-def」という名前の「Job definitions」が作成されています。 失敗した場合は「ROLLBACK_COMPLETE」となりロールバックされます。
$ aws cloudformation describe-stacks --stack-name batch-def | awk '{print $NF}'
スタックの削除
次に作成したテンプレートを1つにまとめるため、作成したスタックを削除しておきます。 JobQueueではComputeEnvironmentを指定しているため 「batch-env」より先に「batch-queue」を削除してください。
$ aws cloudformation delete-stack --stack-name batch-queue $ aws cloudformation delete-stack --stack-name batch-env $ aws cloudformation delete-stack --stack-name batch-def
1つのテンプレートにまとめる
個別に作成した3つのテンプレートを1つのテンプレートにまとめてみます。
ポイントとして、「JobQueue」のリソースを作成する際に「ComputeEnvironment」を指定する必要があるので、!Ref MyComputerEnv
として、値を指定しています。
AWSTemplateFormatVersion: 2010-09-09 Description: AWS Batch ALL #========VPC========# Resources: # Create VPC MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsSupport: 'true' EnableDnsHostnames: 'true' InstanceTenancy: default Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"vpc" ] ] # Create Public RouteTable PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref MyVPC Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"pub-route" ] ] # Create Private RouteTable PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref MyVPC Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"pri-route" ] ] # Create Public Subnet A PublicSubnetA: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.0.0/24 MapPublicIpOnLaunch: 'true' AvailabilityZone: "ap-northeast-1a" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PublicSunetA" ] ] PubSubnetARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref PublicRouteTable # Create Public Subnet C PublicSubnetC: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.1.0/24 MapPublicIpOnLaunch: 'true' AvailabilityZone: "ap-northeast-1c" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PublicSunetC" ] ] PubSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetC RouteTableId: !Ref PublicRouteTable # Create Private Subnet A PrivateSubnetA: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.10.0/24 AvailabilityZone: "ap-northeast-1a" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PrivateSubnetA" ] ] PriSubnetARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetA RouteTableId: !Ref PrivateRouteTable # Create Private Subnet C PrivateSubnetC: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.11.0/24 AvailabilityZone: "ap-northeast-1c" Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"PrivateSubnetC" ] ] PriSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetC RouteTableId: !Ref PrivateRouteTable # Create InternetGateway myInternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Join [ "-", [ "cfn" ,"igw" ] ] AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref MyVPC InternetGatewayId: !Ref myInternetGateway # Route for InternetGateway or VPNGateway myRoute: Type: AWS::EC2::Route DependsOn: myInternetGateway Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref myInternetGateway #=======IAM========# # Create ecsInstanceRole ecsInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: ## start Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ## end ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role - arn:aws:iam::aws:policy/AWSCodeCommitFullAccess - arn:aws:iam::aws:policy/AmazonS3FullAccess Path: "/" # Set InstanceProfile ecsInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Roles: - !Ref ecsInstanceRole #InstanceProfileName: # Create AWSBatchServiceRole AWSBatchServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: ## start Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - batch.amazonaws.com Action: - sts:AssumeRole ## end ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole Path: "/service-role/" #========Batch========# # Create ComputeEnvironment MyComputeEnv: Type: "AWS::Batch::ComputeEnvironment" Properties: Type: MANAGED ServiceRole: !GetAtt AWSBatchServiceRole.Arn ComputeEnvironmentName: cfn-env ComputeResources: MaxvCpus: 0 MinvCpus: 0 DesiredvCpus: 0 SecurityGroupIds: - !GetAtt MyVPC.DefaultSecurityGroup Type: EC2 Subnets: - !Ref PrivateSubnetA - !Ref PublicSubnetA InstanceRole: !GetAtt ecsInstanceProfile.Arn InstanceTypes: - optimal Tags: {"Name": "Batch Instance - cfn test"} State: ENABLED # Create JobQueue MyJobQueue: Type: AWS::Batch::JobQueue Properties: ComputeEnvironmentOrder: - Order: 1 ComputeEnvironment: !Ref MyComputeEnv State: ENABLED Priority: 1 JobQueueName: cfn-queue # Create JobDefinition MyJobDefinition: Type: AWS::Batch::JobDefinition Properties: Type: container JobDefinitionName: cfn-def ContainerProperties: Command: - sh - /usr/local/init.sh Memory: 2000 Vcpus: 2 Image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-repo:latest RetryStrategy: Attempts: 1
動作確認
▼スタック作成前に構文チェックを行いエラーがないことを確認します。
$ aws cloudformation validate-template --template-body file://batch.yml
▼今回スタック名を「batch」として作成します。 スタック作成完了までに多少時間がかかるため、完了までwaitしてくれるコマンドをつなげておきます。
$ aws cloudformation create-stack --stack-name batch --template-body file://batch.yml --capabilities CAPABILITY_IAM && aws cloudformation wait stack-create-complete --stack-name batch
▼作成したスタックのステータスを確認します。 無事スタックの作成が完了したら下記コマンドより「CREATE_COMPLETE」が表示され、 テンプレートで設定したAWS Batchの環境が作成されています。 失敗した場合は「ROLLBACK_COMPLETE」となりロールバックされます。
$ aws cloudformation describe-stacks --stack-name batch | awk '{print $NF}'
スタックの削除
AWS Batchの環境が必要なくなったらスタックを削除することで環境をまとめて削除することができます。 削除完了まで多少時間がかかるため、削除が完了するまでwaitしてくるコマンドをつなげておきます。
$ aws cloudformation delete-stack --stack-name batch && aws cloudformation wait stack-delete-complete --stack-name batch
さいごに
いかがだったでしょうか。 テンプレートの作成は慣れるまでなかなか大変ですが、 自在に扱えるようになればとても効率よく作業が行えるため これからも積極的に利用していきたいと思います。
誰かの参考になれば幸いです。