[Terraform]Amazon ECRのコンテナイメージをAWS CodePipeline経由でAmazon ECSにデプロイする際のポイント
コンテナイメージをデプロイしたい
おのやんです。
みなさん、Amazon ECR(以下、ECR)のコンテナイメージをAmazon ECS(以下、ECS)にデプロイしたいとおもったことはありませんか?私はあります。
AWSでは、AWS CodePipeline(以下、CodePipeline)を設定することで、AWSサービス内でCI/CDのパイプラインを設定できます。CodePipelineは、マネジメントコンソール上で設定する分には、AWS側でいい感じにサジェストしてくれるので、比較的簡単に設定することができます。しかしTerraformで設定する際にはCodePipeline側の挙動をドキュメントと照らし合わせて、適切な設定を記述する必要があります。
今回、ECRにpushしたコンテナイメージをECRへデプロイするCodePipelineのパイプラインを、Terraformで記述する機会がありましたので、この際のポイントをまとめていきたいと思います。
今回の構成
今回目指していた構成は以下の画像のようになります。ECRには、nginxのコンテナイメージをpushしておきます。このイメージに対して、CodePipelineを設定してECRへデプロイします。また、デプロイ設定は、S3にアップロードしたimagedefinitions.json
の圧縮ファイルをCodePipelineに参照させています。
一般的な構成だと、GitHubなどのソースコードリポジトリへのpushをトリガーとして、AWS CodeBuildのビルドステージを経由してデプロイしたりします。しかし、今回は単にECRリポジトリ内のコンテナイメージをそのままECRへデプロイしますので、ここはご認識いただければと思います。
1: イメージビルド時のプラットフォームに注意する
今回は、検証用ということでこのようなDockerfile
を使用しました。
FROM nginx:latest
このDockerfile
を手元のPCでビルドして、ECRにpushすることになります。このビルドコマンドですが、通常のコマンドでビルドしたイメージをpushしたとします。
docker build -t aws-test-nginx-image .
今回の場合、このイメージをCodePipelineで参照させた時に、このようなデプロイログが繰り返し表示されて、デプロイが終了しない事象に遭遇しました。
デプロイが失敗するわけではないため、「デプロイ中...」の表示がずっと表示されたまま、一向にデプロイが完了しない状態になります。
このエラーでは、こちらのZenn記事を参考にしました。
今回は、コンテナイメージビルド時に--platform linux/amd64
のオプションをつけたことでデプロイが通るようになりました。手元のPCがMacだったために、AWS FargateのプラットフォームがMacと異なる状態になりエラーが発生したと思われます。
docker build --platform linux/amd64 -t aws-test-nginx-image .
コンテナイメージを手元でビルドしてからイメージリポジトリにpushする場合は、ビルド時のプラットフォームに気をつけましょう。
2: コンテナ定義ファイルを保存するS3バケット設定に注意する
ECRのコンテナイメージから直接CodePipeline経由でデプロイする際には、imagedefinitions.json
というファイルを作成する必要があります。
imagedefinitions.json
の内容は以下の通りです。name
にはコンテナ名、imageUri
にはコンテナイメージのURIを記載しておきます。
[
{
"name": "nginx",
"imageUri": "123456789.dkr.ecr.us-east-1.amazonaws.com/aws-test-ecr:latest"
}
]
ECRから直接デプロイする際には、このimagedefinitions.json
をzipで圧縮してS3バケットに保存する必要があります。このS3バケットですが、バージョニングを有効にしておく必要があります。バージョニングが有効になっていないS3バケットにimagedefinitions.json.zip
を保存すると、デプロイ時にエラーになります。
ですので、imagedefinitions.json
を保存するS3バケットはしっかりバージョニングを有効にしておきましょう。例として、Terraformで設定する場合は以下のようなコードになります。
#----------------------------------------------------------------#
# CodePipeline Artifacts Bucket
#----------------------------------------------------------------#
resource "aws_s3_bucket" "codepipeline_artifact" {
bucket = local.codepipeline_artifact_bucket_name
}
resource "aws_s3_bucket_versioning" "codepipeline_artifact" {
bucket = aws_s3_bucket.codepipeline_artifact.id
versioning_configuration {
status = "Enabled"
}
}
3: ZIP圧縮時のパスに注意する
imagedefinitions.json
ファイルをS3にアップロードする際は、zipで圧縮する必要があります。
注記
ソースリポジトリが Amazon S3 バケットの場合は、JSON ファイルを圧縮してください。
このzip圧縮の際、以下のようなコマンドを使って、ディレクトリ構造ごとファイルを圧縮していました。
zip imagedefinitions.json.zip /Users/onoyan/works/aws-test-project/terraform/modules/pipeline/file/imagedefinitions.json
しかしこれだとimagedefinitions.json.zip
を解凍すると、imagedefinitions.json
ではなく/Users/onoyan/works/aws-test-project/terraform/modules/pipeline/file/imagedefinitions.json
が展開されます。不要なディレクトリ構造まで圧縮されてしまいます。
私の場合は、これが原因でCodePipelineの参照エラーになりました。
最新のアクション実行メッセージ
Did not find the image definition file imagedefinitions.json in the input artifacts ZIP file. Verify the file is stored in your pipeline's Amazon S3 artifact bucket: aws-test-s3-bucket key: aws-test-pipeline/source_out/XXXXXX.zip
そのため、S3にアップロードする際にはimagedefinitions.json
があるディレクトリ直下で実行するなどして、ディレクトリ構造まで圧縮しないようにしてください。
zip imagedefinitions.json.zip imagedefinitions.json
参考:CodePipelineのTerraformコード
以上のポイントに気をつけて、適切に設定できれば、以下のようなTerraformコードでコンテナイメージをデプロイできるようになります。
#----------------------------------------------------------------#
# CodePipeline
#----------------------------------------------------------------#
resource "aws_codepipeline" "aws_test_pipeline" {
name = "${var.system_name}-${var.environment}-pipeline"
role_arn = aws_iam_role.codepipeline.arn
artifact_store {
type = "S3"
location = var.codepipeline_artifact_bucket
}
stage {
name = "S3Setting"
action {
name = "S3Setting"
category = "Source"
owner = "AWS"
provider = "S3"
version = "1"
output_artifacts = ["source_output_s3"]
configuration = {
S3Bucket = var.codepipeline_artifact_bucket
S3ObjectKey = "imagedefinitions.json.zip"
}
}
}
stage {
name = "Deploy"
action {
name = "Deploy"
category = "Deploy"
owner = "AWS"
provider = "ECS"
input_artifacts = ["source_output_s3"]
version = "1"
configuration = {
ClusterName = var.cluster_name
ServiceName = var.service_name_web
}
}
}
}
CodePipelineに関するさまざまな設定に注意しよう
CodePipelineは、それ単体で動くことはほとんどないサービスです。今回では、S3やECR、ECSなどと連動していました。
そのため、CodePipelineだけでなく他のAWSサービスも含めて、注意深く設定するようにしましょう。では!