#1. 概要
AWS CodePipelineのCodeCommitによるCICDのBlue/Greenを実装するには、Deployステージに渡されたアーティファクトのサイズ制限があり、前回の投稿ではS3のPipelineに変更と別のappspec.yml、taskdef.jsonだけを格納するCodeCommitリポジトリを作成の二つ対処方法を紹介した。
S3 Pipelineの対処方法には、作成されたCodeCommitタイプのPipelineのCloudFormationのソースファイルの変更作業が多いの短所があり、別のCodeCommitリポジトリの対処では改めてCodeCommitリポジトリの作成が必要もあるため、両対処とも少し手間が掛かる。
本文は、両対処方法の長所を連携し、S3バーケットを事前に作成し、Dockerファイルをこのバーケットにおいて置き、AWS::CodeBuild::Project段階まずCodeCommitリポジトリにある古いパッケージファイル(target/*.jarなど)cleanし、S3バーケットを利用してCodePipelineのSourceステージで作成されたアーティファクトのサイズの抑えることができ、別のCodeCommitリポジトリを作成しなくてもオリジナルのCodeCommitリポジトリのままでCICDを実現する方法を紹介する。
#2. 対処法
Step1:S3バーケットを作る。Dockerfileを格納する。
作成されたS3バーケットと格納されたDockerfile を下記の図をご参照ください。実際、このStepによりS3バーケットにDockerfileをアップロードしておけば、DockerfileをCodeCommitリポジトリに格納しなくてもCodeDeployを起動することが出来る。
FROM amazoncorretto:11-al2-jdk
EXPOSE 22
EXPOSE 80
EXPOSE 8080
RUN yum install -y iputils
RUN yum install -y net-tools
RUN yum install -y iputils
RUN yum -y install procps
RUN mkdir /usr/local/bin/api
RUN mkdir /usr/local/bin/batch
RUN mkdir /usr/local/bin/common
RUN mkdir /usr/local/bin/database
RUN mkdir /usr/local/bin/stubs
RUN mkdir /usr/local/bin/lib
ADD target/xxxxxxx_api-0.0.1-SNAPSHOT.jar /usr/local/bin/api/xxxxxxx_api-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java", "-jar", "/usr/local/bin/api/xxxxxxx_api-0.0.1-SNAPSHOT.jar"]
Step2:buildspec.ymlを編集する
Buildする際使用するDockerfileを、S3バーケットからコピーする、とDockerImageを作るにPathを“xxxxxxx-api/.”を指定することをご注意。
このbuildspec.ymlファイルをCodeBuildのProjectに埋め込み。
version: 0.2
env:
parameter-store:
DOCKER_USER: dockerhub-user
DOCKER_TOKEN: dockerhub-token
phases:
install:
runtime-versions:
java: corretto11
commands:
- mvn clean
- aws s3 cp s3://xxx-dev-xxxx-s3-01/Dockerfile-xxxx-api-s3 .
pre_build:
commands:
- echo Logging in to Amazon ECR...
- $(aws ecr get-login --no-include-email)
- REPOSITORY_URI=xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxx-dev-xxxx-app-ecr
- COMMIT_HASH=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HAST:=latest}
- echo Logging in to Docker Hub...
- echo ${DOCKER_TOKEN} | docker login -u ${DOCKER_USER} --password-stdin
build:
commands:
- echo Build started on `date`
- mvn package --also-make --projects xxxxxxx-api -Dmaven.test.skip=true
- echo Building the Docker image...
# - docker build -t ${REPOSITORY_URI}:latest .
- docker build -t $REPOSITORY_URI:latest xxxxxxx-api/. -f ./Dockerfile-xxxx-api-s3
- 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...
- printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
files:
- imageDetail.json
Step3:CodeCommitのリポジトリに応用開発のソースコード、及びappspec.yml、taskdef.jsonを格納する。下記図は例である。
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "<TASK_DEFINITION>"
LoadBalancerInfo:
ContainerName: "xxx-dev-xxxx-app-container"
ContainerPort: "80"
3. 完成したCloudFormationYamlファイル
参照するため、完成したCodePipelineを作成するCloudFormationのYamlファイルを添付する。
AWSTemplateFormatVersion: 2010-09-09
Description: CodePipeline For ECS Fargate Blue/Green Deploy with PlaceHolder
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
AwsXxxxxRegion:
Type: String
Default: xx-xxxxxxxxx-1
Service:
Type: String
Default: xxxx
Env:
Type: String
Default: dev
ECRName:
Type: String
Default: xxx-dev-xxxx-app-ecr
ContainerName:
Type: String
Default: xxx-dev-xxxx-app-container
CodeCommitRepositoryName:
Type: String
Default: xxx-dev-xxxx-codecommit
CodeDeployAppName:
Type: String
Default: xxx-dev-xxxx-deploy-app1
CodeDeployGrpName:
Type: String
Default: xxx-dev-xxxx-deploy-grp1
NameTagPrefix:
Type: String
Default: test
Description: Prefix of Name tags.
ServiceName:
Type: String
Default: xxxx
Description: Prefix of Service tags.
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
# IAM Roles
# ------------------------------------------------------------#
# CodeWatchEventを実行できるIAMRole
CloudwatchEventRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub cwe-tko-${Service}-${Env}-phld-role
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-tko-${Service}-${Env}-phld-service-role
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: '*'
# add policy to get parameter from SSM parameter store
- Effect: Allow
Action:
- ssm:DescribeParameters
- ssm:GetParameters
Resource: '*'
# CodePipelineに適用するIAMRole
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub codepipeline-tko-${Service}-${Env}-phld-role
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
# CodeDeployに適用するIAMRole
CodeDeployRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: 'Allow'
Principal:
Service:
- 'codedeploy.amazonaws.com'
Action:
- 'sts:AssumeRole'
Path: '/'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
RoleName: !Sub codedeploy-tko-${Service}-${Env}-phld-role
# S3Bucket
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
# 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'
- ':'
- !Ref CodeCommitRepositoryName
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- master
Targets:
- Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
RoleArn: !GetAtt CloudwatchEventRole.Arn
Id: codepipeline-AppPipeline
# CodeBuild
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
ServiceRole: !Ref CodeBuildServiceRole
Artifacts:
Type: CODEPIPELINE
Source:
Type: CODEPIPELINE
BuildSpec: |
version: 0.2
env:
parameter-store:
DOCKER_USER: dockerhub-user
DOCKER_TOKEN: dockerhub-token
phases:
install:
runtime-versions:
java: corretto11
commands:
- mvn clean
- aws s3 cp s3://xxx-dev-xxxx-s3-01/Dockerfile-xxxx-api-s3 .
pre_build:
commands:
- echo Logging in to Amazon ECR...
- $(aws ecr get-login --no-include-email)
- REPOSITORY_URI=xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxx-dev-xxxx-app-ecr
- COMMIT_HASH=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HAST:=latest}
- echo Logging in to Docker Hub...
- echo ${DOCKER_TOKEN} | docker login -u ${DOCKER_USER} --password-stdin
build:
commands:
- echo Build started on `date`
- mvn package --also-make --projects xxxxxxx-api -Dmaven.test.skip=true
- echo Building the Docker image...
# - docker build -t ${REPOSITORY_URI}:latest .
- docker build -t $REPOSITORY_URI:latest xxxxxxx-api/. -f ./Dockerfile-xxxx-api-s3
- 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:$IMAGE_TAG
- echo Writing imageDetail json...
- printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
files:
- imageDetail.json
Environment:
PrivilegedMode: true
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:4.0
Type: LINUX_CONTAINER
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
- Name: REPOSITORY_URI
Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRName}
- Name: ContainerName
Value: !Ref ContainerName
- Name: DOCKER_BUILDKIT
Value: '1'
Name: !Sub ${Service}-${Env}-phld-project1
# Name: !Ref AWS::StackName
# Following was original from pre_build
# - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
# Following 2 lines was the last part in build
# - docker build -t $REPOSITORY_URI:$IMAGE_TAG .
# - docker tag $REPOSITORY_URI:$IMAGE_TAG $REPOSITORY_URI:$IMAGE_TAG
# Following 1 line was the last part in post_build
# - docker push $REPOSITORY_URI:$IMAGE_TAG
# Create Code deploy application
# CodeDeployApplication:
# Type: AWS::CodeDeploy::Application
# Properties:
# ApplicationName: !Ref CodeDeployAppName
# ComputePlatform: ECS
# CodeDeployDeploymentGroup:
# Type: AWS::CodeDeploy::DeploymentGroup
# Properties:
# ApplicationName: !Ref CodeDeployAppName
# DeploymentGroupName: !Ref CodeDeployGrpName
# DeploymentConfigName: CodeDeployDefault.ECSAllAtOnce
# DeploymentStyle:
# DeploymentType: BLUE_GREEN
# DeploymentOption: WITH_TRAFFIC_CONTROL
# ServiceRoleArn: !GetAtt CodeDeployRole.Arn
# LoadBalancerInfo:
# ElbInfoList:
# - Name: !ImportValue DevXxxxAppNLB
# TargetGroupInfoList:
# - Name: !ImportValue DevXxxxAppTG1
# - Name: !ImportValue DevXxxxAppTG2
# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt CodePipelineServiceRole.Arn
Name: !Sub ${ServiceName}-phld-pipeline1
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: '1'
Provider: CodeCommit
Configuration:
RepositoryName: !Ref CodeCommitRepositoryName
PollForSourceChanges: false
BranchName: master
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
# - Name: Approval
# Actions:
# - Name: Manual_Approval
# ActionTypeId:
# Category: Approval
# Owner: AWS
# Version: '1'
# Provider: Manual
# Configuration:
# CustomData: !Sub '${ServiceName} will be updated. Do you want to deploy it?'
# NotificationArn: arn:aws:sns:ap-xx-xxxx-2:xxxxxxxx:hogehoge
- Name: Deploy
Actions:
- Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CodeDeployToECS
Configuration:
AppSpecTemplateArtifact: App
AppSpecTemplatePath: appspec.yml
TaskDefinitionTemplateArtifact: App
TaskDefinitionTemplatePath: taskdef.json
ApplicationName: !Ref CodeDeployAppName
DeploymentGroupName: !Ref CodeDeployGrpName
# StackName: xxxx-dev-ecs
# ClusterName: !ImportValue DevXxxxEcsAppCluster
# ServiceName: !ImportValue DevXxxxEcsAppService
Image1ArtifactName: BuildOutput
Image1ContainerName: IMAGE1_NAME
RunOrder: 1
InputArtifacts:
- Name: App
- Name: BuildOutput
Region: !Ref AWS::Region
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
PipelineDev:
Description: Dev Xxxx Pipeline
Value: !Ref Pipeline
Export:
Name: DevPhldAppPipeline
4. 作成されたアーティファクトのサイズ
CodePipelineのSourceステージで作成されたアーティファクト(本文はAPPの名前にした)のサイズは、CloudFormationにて作成されたRsourceのS3で調べられる。本文の場合は609KBになった。
5. まとめ
CodePipelineのCodeCommitによるCICDのBlue/Greenを実装するには、現状のDeployステージに渡されたアーティファクトの3MBサイズ制限に対し、本文のS3バーケットを事前に作成、Dockerファイルをこのバーケットにおいて置き、CodeBuildの作成された中間JarファイルもS3バーケットを利用し、CodePipelineのSourceステージで作成されたアーティファクトのサイズの抑えることができた。この方法では、オリジナルのCodeCommitリポジトリままでCICDを実現することができる。