■ECR+ECS+Codeシリーズで自動デプロイ: CodeBuildでデプロイ用イメージを自動ビルド
上にある「ECR+ECS+Codeシリーズで自動デプロイ: ベースイメージを登録」からの続き
ローカルにDockerをビルドする環境が無くても、リポジトリの内容をもとにAWS上でビルド&ECRへのイメージプッシュを行える
主に以下の書籍を参考に検証中
CodeシリーズはP.335から
AWSコンテナ設計・構築[本格]入門 | SBクリエイティブ
https://www.sbcr.jp/product/4815607654/
GitHub - uma-arai/sbcntr-resources: 書籍用の各種リソースのダウンロードリポジトリ
https://github.com/uma-arai/sbcntr-resources
■前提
ベースイメージは、先の手順で作成した以下を使うものとする
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/base_php81_apache:1.0.0
そのイメージをもとに、先の手順でプログラムも含めたApache+PHPの環境を作成した
ecr_php81_apache
以下で起動&終了でき、
http://localhost/ にアクセスできることを確認した
$ cd docker/ecr_php81_apache
$ docker-compose build
$ docker-compose up -d
$ docker-compose down
■準備: Bitbucketアプリパスワードの作成
今回はBitbucketからデプロイするので、以下を参考にアプリパスワードを発行しておく
アプリ パスワード | Bitbucket Cloud | アトラシアン サポート
https://support.atlassian.com/ja/bitbucket-cloud/docs/app-passwords/
個人設定 → アクセス管理 → アプリパスワード
以下のページに遷移できる
https://bitbucket.org/account/settings/app-passwords/
Label: ECS
権限: リポジトリの「読み取り」のみ
「作成」をクリックするとアプリパスワードが表示されるので控えておく
■準備: IAMポリシーの作成
セキュリティ認証情報 → ポリシー → ポリシーの作成
「JSON」タブに以下を入力する
AWSアカウントID(123456789012)とイメージ名(base_php81_apache と ecr_php81_apache)は、環境に合わせて調整する
イメージ名は、
ベースイメージを参照するためのリポジトリ(base_php81_apache)と、
ビルド済みイメージを保存するためのリポジトリ(ecr_php81_apache)を、
それぞれ2箇所で指定する
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListImagesInRepository",
"Effect": "Allow",
"Action": [
"ecr:ListImages"
],
"Resource": [
"arn:aws:ecr:ap-northeast-1:123456789012:repository/base_php81_apache",
"arn:aws:ecr:ap-northeast-1:123456789012:repository/ecr_php81_apache"
]
},
{
"Sid": "GetAuthorizationToken",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Sid": "ManageRepositoryContents",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:GetRepositoryPolicy",
"ecr:DescribeRepositories",
"ecr:ListImages",
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": [
"arn:aws:ecr:ap-northeast-1:123456789012:repository/base_php81_apache",
"arn:aws:ecr:ap-northeast-1:123456789012:repository/ecr_php81_apache"
]
}
]
}
「次のステップ:タグ」をクリック
そのまま「次のステップ:確認」をクリック
ポリシーの作成画面になるので以下を入力
名前: Container-Test-AccessingECRRepositoryPolicy
説明: Policy to access ECR repo
「ポリシーの作成」をクリック
なお、上記のコードは書籍のサポートページ掲載されている
sbcntr-resources/cloud9_ecr_policy.json at main - uma-arai/sbcntr-resources
https://github.com/uma-arai/sbcntr-resources/blob/main/iam/cloud9_ecr_policy.json
AWS公式サイトにも掲載されている(「Accessing One Amazon ECR Repository」の部分)
Amazon Elastic Container Registry Identity-Based Policy Examples - Amazon ECR
https://docs.aws.amazon.com/AmazonECR/latest/userguide/security_iam_id-based-policy-examples.html
■CodeBuild用ファイル作成
アプリケーションリポジトリのルートディレクトリに、以下をもとにファイルを作成する
https://github.com/uma-arai/sbcntr-resources/blob/main/cicd/buildspec.yml
docker\ecr_php81_apache\buildspec.yml
version: 0.2
env:
variables:
AWS_REGION_NAME: ap-northeast-1
ECR_REPOSITORY_NAME: ecr_php81_apache
DOCKER_BUILDKIT: "1"
phases:
install:
runtime-versions:
docker: 20
pre_build:
commands:
- AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
- aws ecr get-login-password --region ${AWS_REGION_NAME} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION_NAME}.amazonaws.com
- REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION_NAME}.amazonaws.com/${ECR_REPOSITORY_NAME}
- IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
build:
commands:
- docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} -f docker/php/Dockerfile .
post_build:
commands:
- docker image push ${REPOSITORY_URI}:${IMAGE_TAG}
- printf '[{"name":"%s","imageUri":"%s"}]' $ECR_REPOSITORY_NAME $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
ファイルを作成したら、コミット&プッシュしておく
imagedefinitions.json というファイル名が2箇所あるが、これはビルド結果をCodeDeployやCodePipelineに渡すためのもの
ファイル名は
・Amazon ECS 標準デプロイには imagedefinitions.json がデフォルト
・Amazon ECS Blue/Green デプロイでは imageDetail.json がデフォルト
となっているので、原則これに合わせておくといい
また imageDetail.json の場合、printf 内の「[{"name":"%s","imageUri":"%s"}]」は「{"name":"%s","ImageURI":"%s"}」となるので注意
イメージ定義ファイルのリファレンス - AWS CodePipeline
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html
また、一例だが post_build 部分を以下のようにすると、イメージをプッシュする直前の諸々の情報が、CodeBuildのコンソールに出力されるようになる
デバッグの一手段として
post_build:
commands:
- echo Build completed on `date`
- echo ${AWS_REGION_NAME}
- echo ${ECR_REPOSITORY_NAME}
- echo ${DOCKER_BUILDKIT}
- echo ${AWS_ACCOUNT_ID}
- echo ${REPOSITORY_URI}
- echo ${IMAGE_TAG}
- echo '[{"name":"%s","imageUri":"%s"}]' $ECR_REPOSITORY_NAME $REPOSITORY_URI:$IMAGE_TAG
- docker image push ${REPOSITORY_URI}:${IMAGE_TAG}
- printf '[{"name":"%s","imageUri":"%s"}]' $ECR_REPOSITORY_NAME $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
■CodeBuild設定
※今回はCodeCommitではなく、Bitbucketからデプロイする
※「Container-Test」というパイプライン名は、実際は案件名と環境をもとに付けると良さそう
CodeBuild → ビルドプロジェクトを作成する
プロジェクトの設定
プロジェクト名: Container-Test
ビルドバッジ: (「ビルドバッジを有効にする」にチェックを入れる)
ソース
ソースプロバイダ: Bitbucket
リポジトリ: Bitbucketアプリパスワードで接続する
Bitbucketユーザー名: refirio
Bitbucketのアプリパスワード: 9jzJ4nHrLp395xTzjgdX ※入力したら「Bitbucket認証情報の保存」ボタンをクリックし、Bitbucketに接続できることを確認する
リポジトリのURL:
https://bitbucket.org/refirio/ecr_php81_apache.git(Bitbucketに接続すると入力欄が表示される)
ソースバージョン: master
環境
環境イメージ: マネージド型イメージ
オペレーティングシステム: Amazon Linux 2
ランタイム: Standard
イメージ: aws/codebuild/amazonlinux2-x86_64-standard:4.0
イメージのバージョン: このランタイムバージョンには常に最新のイメージを使用してください
特権付与: (「Docker イメージを構築するか、ビルドで昇格されたアクセス権限を取得するには、このフラグを有効にします」にチェックを入れる)
サービスロール: 新しいサービスロール(以前作成したものがあれば「既存のサービスロール」を選択する)
ロール名: Container-Test-CodeBuild-Role(以前作成したものがあれば選択する)
Buildspec
ビルド仕様: buildspecファイルを使用する
アーティファクト
タイプ: アーティファクトなし
キャッシュタイプ: ローカル
少なくとも 1 つのオプションを選択してください。:(「DockerLayerCache」のみにチェックを入れる)
ログ
CloudWatch:(「CloudWatch Logs」にチェックを入れる)
「ビルドプロジェクトを作成する」ボタンをクリック
■ロールにアクセス権限を付与
CodeBuild → ビルドプロジェクト
作成したプロジェクトをクリックし、「ビルド開始」をクリック
…としても、現時点では以下のエラーになる
CodeBuildを設定する際に Container-Test-CodeBuild-Role というロールを作ったが、このロールにアクセス権限を与えていないため
[Container] 2022/01/19 11:37:34 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin https://${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/ecr_php81_apache. Reason: exit status 1
「準備: IAMポリシーの作成」で作成したポリシーを紐づける
セキュリティ認証情報 → ロール
先の手順中で作成した Container-Test-CodeBuild-Role を選択
「アクセス許可を追加 → ポリシーをアタッチ」をクリック
上の手順中で作成した Container-Test-AccessingECRRepositoryPolicy にチェックを入れ、「ポリシーのアタッチ」ボタンをクリック
■CodeBuild実行
CodeBuild → ビルドプロジェクト → Container-Test
画面上部にある「ビルド開始」ボタンをクリック
しばらく待つと最後に以下のようなログが表示され、ビルドが完了した
[Container] 2023/05/12 03:50:00 Phase complete: POST_BUILD State: SUCCEEDED
[Container] 2023/05/12 03:50:00 Phase context status code: Message:
[Container] 2023/05/12 03:50:00 Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
[Container] 2023/05/12 03:50:00 Phase context status code: Message:
ビルドする内容によると思われるが、今回は4〜5分程度で完了した
ECRのリポジトリ ecr_php81_apache を確認すると、a322b3d というタグが指定されたイメージを確認できる
(buildspec.yml の内容に従って、Bitbucketのコミットしたときに発行されたハッシュの先頭7桁がタグ名に使われる)
作成された以下のイメージを使用して、新しいタスクとしてECSに反映できることを確認する
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/ecr_php81_apache:a322b3d
※タグの上書き禁止設定(タグのイミュータビリティ)を有効にしている場合、
後の手順でエラーにならないように、この時点でリポジトリに何かしらの変更を加えておくといい
※デプロイできるかの確認のために、この時点でトップページの表示に何かしらの変更を加えておくといい
■ビルドエラー
以下などが参考になりそう
CodeBuildでECRビルドエラーから得た4つの知見 - Qiita
https://qiita.com/icck/items/bcf118a38c2a691a837d
AWS CodeBuildを使ってDockerイメージをビルドし、Amazon EC2 Container Registry(ECR)へpushする | DevelopersIO
https://dev.classmethod.jp/articles/20170225-codebuild-docker/
AWS CodeBuildでDockerビルドしてECRへプッシュする - け日記
https://ohke.hateblo.jp/entry/2020/09/26/230000
■ビルドエラー: 具体例1
「ビルド開始」をクリックする
しばらく待つと、以下のエラーで止まった
[Container] 2022/01/20 06:31:27 Running command docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} .
failed to dial gRPC: cannot connect to the Docker daemon. Is 'docker daemon' running on this host?: dial unix /var/run/docker.sock: connect: no such file or directory
エラーメッセージで調べると、特権を有効にする必要があるらしい
CodeBuildでDocker in Dockerする - Qiita
https://qiita.com/reireias/items/c02e5e7b2f099f2dab9e
プロジェクトを選択し、「編集 → 環境 → イメージの上書き」として「特権付与」の項目を確認すると、チェックを入れ忘れていた
チェックを入れて「環境を更新」ボタンを押す
■ビルドエラー: 具体例2
「ビルド開始」をクリックする
しばらく待つと、以下のエラーで止まった
[Container] 2022/01/20 07:26:20 Entering phase BUILD
[Container] 2022/01/20 07:26:20 Running command docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} .
#1 [internal] load build definition from Dockerfile
#1 sha256:25a25a7aeafc92304eb63ea0db9f200c3253075e86ba57c7dac9663fd44d2194
#1 transferring dockerfile: 2B done
#1 DONE 0.1s
#2 [internal] load .dockerignore
#2 sha256:f9f7891f650ee769119b4b75aaaa3c7e5037b200eb42f86391868e95a0a17f8a
#2 transferring context: 2B done
#2 DONE 0.1s
failed to solve with frontend dockerfile.v0: failed to read dockerfile: open /var/lib/docker/tmp/buildkit-mount854683111/Dockerfile: no such file or directory
[Container] 2022/01/20 07:26:20 Command did not exit successfully docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} . exit status 1
[Container] 2022/01/20 07:26:20 Phase complete: BUILD State: FAILED
[Container] 2022/01/20 07:26:20 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} .. Reason: exit status 1
Dockerfileを読み込めないとなっている
以下のようにパスを調整
docker\ecr_php81_apache\docker\buildspec.yml
- docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} .
↓
- docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} -f docker/php/Dockerfile .
■ビルドエラー: 具体例3
「ビルド開始」をクリックする
しばらく待つと、以下のエラーで止まった
[Container] 2022/01/20 07:57:40 Entering phase POST_BUILD
[Container] 2022/01/20 07:57:40 Running command docker image push ${REPOSITORY_URI}:${IMAGE_TAG}
The push refers to repository [123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/ecr_php81_apache]
4906f6f3890c: Preparing
2fee96bd8066: Preparing
280952099e51: Preparing
〜中略〜
4906f6f3890c: Retrying in 1 second
7173e6f39ae9: Retrying in 1 second
280952099e51: Retrying in 1 second
2fee96bd8066: Retrying in 1 second
EOF
[Container] 2022/01/20 07:58:30 Command did not exit successfully docker image push ${REPOSITORY_URI}:${IMAGE_TAG} exit status 1
[Container] 2022/01/20 07:58:30 Phase complete: POST_BUILD State: FAILED
[Container] 2022/01/20 07:58:30 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker image push ${REPOSITORY_URI}:${IMAGE_TAG}. Reason: exit status 1
IAMポリシーで指定するリポジトリの名前が間違っていたので修正
■ビルドエラー: 具体例4
「ビルド開始」をクリックする
しばらく待つと、以下のエラーで止まった
tag invalid: The image tag '6abc7d3' already exists in the 'ecr_php81_apache' repository and cannot be overwritten because the repository is immutable.
[Container] 2022/02/08 02:32:30 Command did not exit successfully docker image push ${REPOSITORY_URI}:${IMAGE_TAG} exit status 1
[Container] 2022/02/08 02:32:30 Phase complete: POST_BUILD State: FAILED
[Container] 2022/02/08 02:32:30 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker image push ${REPOSITORY_URI}:${IMAGE_TAG}. Reason: exit status 1
[Container] 2022/02/08 02:32:30 Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
[Container] 2022/02/08 02:32:30 Phase context status code: Message:
タグの上書き禁止設定(タグのイミュータビリティ)を有効にしていて、すでにビルド済みのイメージが存在するためだった
リポジトリに変更を加えてから再度ビルドするか、ビルド済みのイメージを削除してから再度ビルドする
■ビルドエラー: 具体例5
「ビルド開始」をクリックする
しばらく待つと、以下のエラーで止まった
[Container] 2023/05/12 03:25:38 Waiting for agent ping
[Container] 2023/05/12 03:25:39 Waiting for DOWNLOAD_SOURCE
[Container] 2023/05/12 03:25:42 Phase is DOWNLOAD_SOURCE
[Container] 2023/05/12 03:25:42 CODEBUILD_SRC_DIR=/codebuild/output/src317942003/src/bitbucket.org/refirio/ecr_php81_apache
[Container] 2023/05/12 03:25:44 Phase complete: DOWNLOAD_SOURCE State: FAILED
[Container] 2023/05/12 03:25:44 Phase context status code: YAML_FILE_ERROR Message: YAML file does not exist
buildspec.ymlをコミット&プッシュしていなかったので対応
■ビルドエラー: 具体例6
「ビルド開始」をクリックする
しばらく待つと、以下のエラーで止まった
[Container] 2023/05/12 03:30:08 Waiting for agent ping
[Container] 2023/05/12 03:30:09 Waiting for DOWNLOAD_SOURCE
[Container] 2023/05/12 03:30:12 Phase is DOWNLOAD_SOURCE
[Container] 2023/05/12 03:30:12 CODEBUILD_SRC_DIR=/codebuild/output/src787575457/src/bitbucket.org/refirio/ecr_php81_apache
[Container] 2023/05/12 03:30:12 YAML location is /codebuild/output/src787575457/src/bitbucket.org/refirio/ecr_php81_apache/buildspec.yml
[Container] 2023/05/12 03:30:12 No commands found for phase name: install
[Container] 2023/05/12 03:30:12 Processing environment variables
[Container] 2023/05/12 03:30:12 Selecting 'docker' runtime version '19' based on manual selections...
[Container] 2023/05/12 03:30:14 Phase complete: DOWNLOAD_SOURCE State: FAILED
[Container] 2023/05/12 03:30:14 Phase context status code: YAML_FILE_ERROR Message: Unknown runtime version named '19' of docker. This build image has the following versions: 20
buildspec.ymlで「runtime-versions」を「docker: 19」から「docker: 20」に修正