AWS CodePipeline、CodeBuild、ECR を連携させ、GitHub へのプッシュを契機にコンテナイメージのビルド・プッシュ・ECS デプロイまでを全自動化する CI/CD パイプラインです。
AWS CLIを使用してCloudFormationスタックをデプロイする場合は、以下のコマンドを実行します。
aws cloudformation deploy \ --template-file cicd-pipeline.yaml \ --stack-name cicd-pipeline-stack \ --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CI/CD Pipeline with CodePipeline, CodeBuild, ECR, and ECS - Best Practices'
Parameters:
Environment:
Type: String
Default: production
AllowedValues: [production, staging, development]
GitHubOwner:
Type: String
Description: GitHub repository owner
GitHubRepo:
Type: String
Description: GitHub repository name
GitHubBranch:
Type: String
Default: main
ECSClusterName:
Type: String
Description: Target ECS cluster name
ECSServiceName:
Type: String
Description: Target ECS service name
Resources:
# ECR Repository
ECRRepository:
Type: AWS::ECR::Repository
Properties:
ImageScanningConfiguration:
ScanOnPush: true
EncryptionConfiguration:
EncryptionType: KMS
LifecyclePolicy:
LifecyclePolicyText: |
{
"rules": [{
"rulePriority": 1,
"description": "Keep last 20 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 20
},
"action": { "type": "expire" }
}]
}
# S3 Artifact Bucket
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: DeleteOldArtifacts
Status: Enabled
ExpirationInDays: 30
ArtifactBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref ArtifactBucket
PolicyDocument:
Statement:
- Sid: DenyInsecureTransport
Effect: Deny
Principal: '*'
Action: s3:*
Resource:
- !GetAtt ArtifactBucket.Arn
- !Sub ${ArtifactBucket.Arn}/*
Condition:
Bool:
aws:SecureTransport: false
# CodeBuild Log Group
CodeBuildLogGroup:
Type: AWS::Logs::LogGroup
Properties:
RetentionInDays: 14
# IAM Role for CodeBuild
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CodeBuildPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !GetAtt CodeBuildLogGroup.Arn
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:GetObjectVersion
Resource: !Sub ${ArtifactBucket.Arn}/*
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: '*'
- Effect: Allow
Action:
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource: !GetAtt ECRRepository.Arn
# CodeBuild Project
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:7.0
PrivilegedMode: true
EnvironmentVariables:
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: AWS_REGION
Value: !Ref AWS::Region
- Name: ECR_REPO_URI
Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}
- Name: ENVIRONMENT
Value: !Ref Environment
Source:
Type: CODEPIPELINE
BuildSpec: |
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c1-7)
build:
commands:
- docker build -t $ECR_REPO_URI:$IMAGE_TAG -t $ECR_REPO_URI:latest .
post_build:
commands:
- docker push $ECR_REPO_URI:$IMAGE_TAG
- docker push $ECR_REPO_URI:latest
- printf '[{"name":"app-container","imageUri":"%s"}]' $ECR_REPO_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
LogsConfig:
CloudWatchLogs:
Status: ENABLED
GroupName: !Ref CodeBuildLogGroup
# IAM Role for CodePipeline
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CodePipelinePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:GetObjectVersion
- s3:GetBucketVersioning
Resource:
- !GetAtt ArtifactBucket.Arn
- !Sub ${ArtifactBucket.Arn}/*
- Effect: Allow
Action:
- codebuild:BatchGetBuilds
- codebuild:StartBuild
Resource: !GetAtt CodeBuildProject.Arn
- Effect: Allow
Action:
- ecs:DescribeServices
- ecs:DescribeTaskDefinition
- ecs:DescribeTasks
- ecs:ListTasks
- ecs:RegisterTaskDefinition
- ecs:UpdateService
Resource: '*'
- Effect: Allow
Action:
- iam:PassRole
Resource: '*'
Condition:
StringEqualsIfExists:
iam:PassedToService:
- ecs-tasks.amazonaws.com
- Effect: Allow
Action:
- codestar-connections:UseConnection
Resource: !Ref CodeStarConnection
# CodeStar Connection (GitHub)
CodeStarConnection:
Type: AWS::CodeStarConnections::Connection
Properties:
ConnectionName: !Sub ${AWS::StackName}-github
ProviderType: GitHub
# CodePipeline
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt CodePipelineRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection
Version: '1'
Configuration:
ConnectionArn: !Ref CodeStarConnection
FullRepositoryId: !Sub ${GitHubOwner}/${GitHubRepo}
BranchName: !Ref GitHubBranch
OutputArtifactFormat: CODE_ZIP
OutputArtifacts:
- Name: SourceArtifact
- Name: Build
Actions:
- Name: Build
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: '1'
Configuration:
ProjectName: !Ref CodeBuildProject
InputArtifacts:
- Name: SourceArtifact
OutputArtifacts:
- Name: BuildArtifact
- Name: Deploy
Actions:
- Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: ECS
Version: '1'
Configuration:
ClusterName: !Ref ECSClusterName
ServiceName: !Ref ECSServiceName
FileName: imagedefinitions.json
InputArtifacts:
- Name: BuildArtifact
# CloudWatch Alarm for Pipeline Failure
PipelineFailureAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Alert when pipeline execution fails
Namespace: AWS/CodePipeline
MetricName: FailedPipelineExecutions
Dimensions:
- Name: PipelineName
Value: !Ref Pipeline
Statistic: Sum
Period: 300
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: notBreaching
Outputs:
PipelineName:
Description: CodePipeline name
Value: !Ref Pipeline
ECRRepositoryURI:
Description: ECR repository URI
Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}
CodeStarConnectionArn:
Description: CodeStar Connection ARN (requires manual activation in console)
Value: !Ref CodeStarConnection