1. 概要
AWS CodePipelineのCodeCommitによるCICDのBlue/Greenを実装するには、Deploy(CD)するために、Deploy ApplicationとDeploy Groupの定義が必要である。Deploy Groupを定義するに、ECS サービスのELB(Load Balancer)とTargetGroupの指定が必要である。下記の図をご参照ください。
然し、ECSのスケジュールによる起動するBatchジョッブの殆どは毎日定時に実行するタスクに従いコンテナを作成、実行終了したらコンテナを削除される仕組みである。そのため、タスク定義を変更(パラメータの追加)する場合、CodeCommitのCICD(CodeDeployToECS)が使えない。
そこで、本文はCIのCodeBuilder段階を変更活用し、buildspec.ymlにタスクコンテナの新しい定義ファイルを読み込みと実行し、ECSの新しいタスク定義を作成し、次のBatchジョッブを起動するに更新されたタスク定義を利用して、新しいタスクを実現する。
2. 対処法
Step1:更新するタスク定義ファイルtaskdef-update.jsonを作成する。
“aws ecs register-task-definition --cli-input-json”による新タスク定義をするため、元のタスク定義ファイルtaskdef.json(ECSのGUIにより現タスクの定義をJSONフォーマットで表示して、Copy/Pasteによる作成される)から新しいタスク定義ファイルを作成する。”aws ecs register-task-definition”のインプットファイルに対応しないフィールドがあるので、元のtaskdef.jsonから以下のフィールドを削除する。
インプットファイルによるtask-definitionの登録は下記の文章も参照になれる。
https://dev.classmethod.jp/articles/describe-task-definition-to-register-task-definition/
No. | 削除するフィールド(Tag) | 値 |
---|---|---|
1 | ipcMode | null |
2 | placementConstraints | [] |
3 | compatibilities | EC2, FARGATE |
4 | taskDefinitionArn(must delete) | arn:aws:ecs:xx-xxxxxx-1:xxxxxxxxxxxx:task-definition/xxxx-dev-xxxx-batch-task:5 |
5 | requiresAttributes | targetId, etc |
6 | pidMode | null |
7 | revision | 5 |
8 | status | ACTIVE |
9 | inferenceAccelerators | null |
10 | proxyConfiguration | null |
11 | volumes | [] |
12 | IMAGE | IMAGE1_NAMEから実値に戻る |
13 | containerDefinitions | 値がnullであるlinuxParameters等を削除する |
作成された新しいタスク定義ファイルの例を下記taskdef-update.jsonになる。
Step2:buildspec.ymlにregister-task-definitionとECSのサービス更新を実行する。
“aws ecs register-task-definition --cli-input-json”による新タスク定義をするとタスク定義ファイルのバージョンを更新される。次のBatchを起動するに新しいバージョンの定義ファイルを呼び出すため、ECSのサービス更新が必要である。以下の2つコマンドから実現する。
- aws ecs register-task-definition --cli-input-json file://taskdef-update.json
- aws ecs update-service --cluster xxxx-dev-fargate --service xxxx-dev-xxxx-batch-service --task-definition xxxx-dev-xxxx-batch-task
完成したbuildspec.ymlの例を下記になる。
version: 0.2
env:
parameter-store:
DOCKER_USER: dockerhub-user
DOCKER_TOKEN: dockerhub-token
phases:
install:
runtime-versions:
java: corretto11
pre_build:
commands:
- echo mvn clean...
- mvn clean
- echo Logging in to Amazon ECR...
- $(aws ecr get-login --no-include-email)
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- echo Logging in to Docker Hub...
- echo ${DOCKER_TOKEN} | docker login -u ${DOCKER_USER} --password-stdin
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- /bin/sh mvnw -Dmaven.test.skip=true -DincludeScope=runtime dependency:copy-dependencies package
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker images...
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- echo Writing imageDetail json...
- echo "{\"name\":\"${ContainerName}\",\"ImageURI\":\"${REPOSITORY_URI}:${IMAGE_TAG}\"}" > imageDetail.json
- echo Update taskdefinition of fargate
- aws ecs register-task-definition --cli-input-json file://taskdef-update.json
- echo Update batch service, force to use latest task definitio revision
- aws ecs update-service --cluster xxxx-dev-fargate --service xxxx-dev-xxxx-batch-service --task-definition xxxx-dev-xxxx-batch-task
artifacts:
files: imageDetail.json
Step3:動作確認。
次のBatchJobを起動されたら、ECSのコンテナ定義を開いて確認する。
#3. 動作確認
目的はタスクを実現するコンテナのCPUを256から512に、メモリーを2048に、設定するパラメータXXX_XXX_TEST_TESTを追加する。使用するtaskdef.jsonとtaskdef-update.jsonは下記になる。
"taskDefinitionArn": "arn:aws:ecs:xx-xxxxxx-1:xxxxxxxxxxxx:task-definition/xxxx-dev-xxxx-batch-task:5",
"family": "xxxx-dev-xxxx-batch-task",
"requiresAttributes": [
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
},
…
"networkMode": "awsvpc",
"cpu": "256",
"revision": 5,
"status": "ACTIVE",
"inferenceAccelerators": null,
"proxyConfiguration": null,
"volumes": []
}
taskdef-update.json:
{
"executionRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/xxxx-dev-xxxx-ecs-taskexec-role",
"containerDefinitions": [
{
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/logs/xxxx-dev-xxxx-ecs-batch",
"awslogs-region": "xx-xxxxxx-1",
"awslogs-stream-prefix": "xxxx-dev-xxxx"
}
},
"portMappings": [
{
"hostPort": 8080,
"protocol": "tcp",
"containerPort": 8080
}
],
"cpu": 0,
"secrets": [
{
"valueFrom": "xxxx-DEV-XXXX_SPRING_DATASOURCE_URL",
"name": "xxxx.spring.datasource.url"
},
…
{
"valueFrom": "xxxx-DEV-XXXX_BATCH_API_TRANSACTIONID",
"name": "xxxx.batch.api.transactionId"
}
],
"memoryReservation": 128,
"image": "xxxxxxxxxxxx.dkr.ecr.xx-xxxxxx-1.amazonaws.com/xxxx-dev-xxxx-batch:latest",
"name": "xxxx-dev-xxxx-batch-container"
}
],
"memory": "1024",
"taskRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/xxxx-dev-xxxx-ecs-task-role",
"family": "xxxx-dev-xxxx-batch-task",
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"cpu": "512"
}
taskdef-update.jsonの新しいパラメータによる起動されたコンテナは下記図で確認できる。
#4. 完成したCloudFormationYamlファイル
参照するため、完成したCodePipelineを作成するCloudFormationのCICD Yamlファイルを添付する。
AWSTemplateFormatVersion: 2010-09-09
Description: CodePipeline For ECS Fargate Rolling Update
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
AwsTokyoRegion:
Type: String
Default: xx-xxxxxx-1
Service:
Type: String
Default: xxxx
Env:
Type: String
Default: dev
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
# IAM Roles
# ------------------------------------------------------------#
# CodeWatchEvent??????IAMRole
CloudwatchEventRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub cwe-xxx-${Service}-${Env}-role-batch
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: CloudWatchEventsPipelineExecution
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: codepipeline:StartPipelineExecution
Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
# CodeBuild?????IAMRole
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub codebuild-xxx-${Service}-${Env}-service-role-batch
Path: /
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: SampleCodeBuildAccess
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Resource: '*'
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- Effect: Allow
Resource: !Sub arn:aws:s3:::${ArtifactBucket}/*
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
- Effect: Allow
Action:
- codebuild:CreateReportGroup
- codebuild:CreateReport
- codebuild:UpdateReport
- codebuild:BatchPutTestCases
- codebuild:BatchPutCodeCoverages
Resource: '*'
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:GetRepositoryPolicy
- ecr:DescribeRepositories
- ecr:ListImages
- ecr:DescribeImages
- ecr:BatchGetImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
- ecr:PutImage
Resource: '*'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMFullAccess
# CodePipeline?????IAMRole
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub codepipeline-xxx-${Service}-${Env}-role-batch
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: SamplePipeline
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- iam:PassRole
Resource: '*'
Effect: Allow
Condition:
StringEqualsIfExists:
iam:PassedToService:
- ecs-tasks.amazonaws.com
- Resource:
- !Sub arn:aws:s3:::${ArtifactBucket}/*
Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketVersioning
- Action:
- codecommit:CancelUploadArchive
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:GetRepository
- codecommit:GetUploadArchiveStatus
- codecommit:UploadArchive
Resource: '*'
Effect: Allow
- Action:
- codedeploy:CreateDeployment
- codedeploy:GetApplication
- codedeploy:GetApplicationRevision
- codedeploy:GetDeployment
- codedeploy:GetDeploymentConfig
- codedeploy:RegisterApplicationRevision
- codedeploy:*
Resource: '*'
Effect: Allow
- Action:
- elasticbeanstalk:*
- ec2:*
- elasticloadbalancing:*
- autoscaling:*
- cloudwatch:*
- sns:*
- cloudformation:*
- rds:*
- sqs:*
- ecs:*
Resource: '*'
Effect: Allow
- Action:
- codebuild:BatchGetBuilds
- codebuild:StartBuild
- codebuild:BatchGetBuildBatches
- codebuild:StartBuildBatch
Resource: '*'
Effect: Allow
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMFullAccess
# S3Bucket
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub xxx-${Env}-${Service}-s3-cicd-batch-artifactbucket
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
Tags:
- Key: Name
Value: !Sub xxx-${Env}-${Service}-s3-cicd-batch-artifactbucket
- Key: Service
Value: !Ref Service
- Key: Environment
Value: !Ref Env
# CloudWatchEvent??????
AmazonCloudWatchEventRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.codecommit
detail-type:
- CodeCommit Repository State Change
resources:
- Fn::Join:
- ''
- - 'arn:aws:codecommit:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- ':'
- !Sub xxx-${Env}-${Service}-codecommit-batch
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- main
Targets:
- Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
RoleArn: !GetAtt CloudwatchEventRole.Arn
Id: codepipeline-BatchPipeline
# CodeBuild
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
ServiceRole: !Ref CodeBuildServiceRole
Artifacts:
Type: CODEPIPELINE
Source:
Type: CODEPIPELINE
Environment:
PrivilegedMode: true
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:4.0
Type: LINUX_CONTAINER
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: /CodeBuild/AWS_DEFAULT_REGION_XXXX
Type: PARAMETER_STORE
- Name: IMAGE_REPO_NAME
Value: /CodeBuild/IMAGE_REPO_NAME_XXXX_BATCH
Type: PARAMETER_STORE
- Name: AWS_ACCOUNT_ID
Value: /CodeBuild/AWS_ACCOUNT_ID_XXXX
Type: PARAMETER_STORE
- Name: IMAGE_TAG
Value: /CodeBuild/IMAGE_TAG_XXXX
Type: PARAMETER_STORE
- Name: REPOSITORY_URI
Value: /CodeBuild/REPOSITORY_URI_XXXX_BATCH
Type: PARAMETER_STORE
- Name: ContainerName
Value: /CodeBuild/ContainerName_XXXX_BATCH
Type: PARAMETER_STORE
- Name: DOCKERHUB_USER
Value: /CodeBuild/DOCKERHUB_USER_XXXX
Type: PARAMETER_STORE
- Name: DOCKERHUB_PASS
Value: /CodeBuild/DOCKERHUB_PASS_XXXX
Type: PARAMETER_STORE
Name: !Sub ${Service}-${Env}-cicd-batch-project
# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt CodePipelineServiceRole.Arn
Name: !Sub ${Service}-cicd-batch-pipeline
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: '1'
Provider: CodeCommit
Configuration:
RepositoryName: !Sub xxx-${Env}-${Service}-codecommit-batch
PollForSourceChanges: false
BranchName: main
RunOrder: 1
OutputArtifacts:
- Name: App
- Name: Build
Actions:
- Name: Build
ActionTypeId:
Category: Build
Owner: AWS
Version: '1'
Provider: CodeBuild
Configuration:
ProjectName: !Ref CodeBuildProject
RunOrder: 1
InputArtifacts:
- Name: App
OutputArtifacts:
- Name: BuildOutput
CodeCommitリポジトリは下記図になる。
#5. まとめ
本文のCICD buildspec.ymlファイルからタスク定義ファイルを実行するの活用により、BatchジョッブのようなELBやTargetGroupが付いていないBatchジョッブもCodePipelineのCICDのBlue/Green(CodeDeployToECS)も実装できる。ご実務に参照になれば幸いである。