「TerraformでECSローリング更新のCodePipelineを作りたい」
サンプルがあまりなかったので書いてみました。
サンプルコードは以下になります。
msato0731/terraform-sample/tree/main/ecs-rolling-update
やってみた
terraformで環境構築
いくつかコードを解説します。
まずは、CodePipelineを定義している部分です。
CodeCommitをソースに、CodeBuildでDockerイメージのBuild・Push、最後にECSへのローリング更新を行なっています。
(B/Gデプロイでは、CodeDeployが必要ですがローリング更新であれば無くてもできます)
少しはまった点としては、CloudWatch Eventの部分を記述忘れていてCodePipelineが発火しなかった点です。
codepipeline.tf
resource "aws_codepipeline" "sample_app" {
name = local.name_prefix
role_arn = aws_iam_role.codepipeline.arn
artifact_store {
location = aws_s3_bucket.codepipeline_artifact.bucket
type = "S3"
}
stage {
name = "Source"
action {
name = "Source"
category = "Source"
owner = "AWS"
provider = "CodeCommit"
version = 1
output_artifacts = ["source_output"]
configuration = {
RepositoryName = aws_codecommit_repository.sample_app.repository_name
BranchName = "main"
OutputArtifactFormat = "CODE_ZIP"
PollForSourceChanges = "false"
}
}
}
stage {
name = "Build"
action {
name = "Build"
category = "Build"
owner = "AWS"
provider = "CodeBuild"
version = 1
input_artifacts = ["source_output"]
output_artifacts = ["build_output"]
configuration = {
ProjectName = aws_codebuild_project.sample_app.id
}
}
}
stage {
name = "Deploy"
action {
name = "Deploy"
category = "Deploy"
owner = "AWS"
provider = "ECS"
version = 1
input_artifacts = ["build_output"]
configuration = {
ClusterName = aws_ecs_cluster.this.id
ServiceName = aws_ecs_service.sample_app.name
}
}
}
}
resource "aws_cloudwatch_event_rule" "codepipeline_sample_app" {
name = "${local.name_prefix}-codepipeline-sample-app"
event_pattern = templatefile("./file/codepipeline_event_pattern.json", {
codecommit_arn : aws_codecommit_repository.sample_app.arn
})
}
resource "aws_cloudwatch_event_target" "codepipeline_sample_app" {
rule = aws_cloudwatch_event_rule.codepipeline_sample_app.name
arn = aws_codepipeline.sample_app.arn
role_arn = aws_iam_role.event_bridge_codepipeline.arn
}
次にCodeBuildです。
buildspec.ymlはアプリケーションリポジトリに置くパターンも多いかと思いますが、今回はterraformに含めました。
buildspec.ymlで必要なAWSアカウントIDやpushする先のECRの情報は、CodeBuildの環境変数で渡す形にしました。
codebuild.tf
resource "aws_codebuild_project" "sample_app" {
name = local.name_prefix
service_role = aws_iam_role.codebuild.arn
source {
type = "CODEPIPELINE"
git_clone_depth = 0
insecure_ssl = false
report_build_status = false
buildspec = file("./file/buildspec.yml")
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/standard:4.0"
type = "LINUX_CONTAINER"
privileged_mode = true
environment_variable {
name = "AWS_ACCOUNT_ID"
value = data.aws_caller_identity.current.account_id
}
environment_variable {
name = "REPOSITORY_URI"
value = aws_ecr_repository.httpd.repository_url
}
}
artifacts {
type = "CODEPIPELINE"
encryption_disabled = false
name = local.name_prefix
packaging = "NONE"
}
}
file/buildspec.yml
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $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 image definitions file...
- printf '[{"name":"httpd","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
CodeCommitにDockerfileをアップロード
CodeCommitはTerraformを流した状態だとブランチが存在しないため、以下のエラーになります。
マネジメントコンソール上から、リポジトリにあるDockerfileをアップロードすることでCodePipelineが成功するようになります。
ALBのエンドポイントにアクセスすると、以下のようにDockerfile上で設定したテキストが返ってきます。
$ curl <ALBのエンドポイント>
test
CodePipelineの動作確認
CodeCommit変更時に、変更内容がデプロイされるか試してみます。
マネジメントコンソールでCodeCommitにアクセスし、Dockerfileを編集します。
本来ならアプリケーションのコードを変更して反映を確認する流れになりますが、簡略化のためにhttpdのDockerファイルのindex.htmlを書き換えています。
CodeCommit上でコミットすることで、ファイルの変更をCodePipelineで検出してパイプラインが動作します。 パイプラインの動作終了後に、再度ALBのエンドポイントにアクセスするレスポンスが変わっていることが確認できます。
$ curl <ALBのエンドポイント>
test2
おわりに
TerraformでCodePipelineを使用したECSローリング更新をやってみました。
Terraformの記述に改善の余地があると思いますが、参考になると嬉しいです。
以上、AWS事業本部の佐藤(@chari7311)でした。