こんにちは!
技術課の山本です
今年は雪が多いですね
晴れた日に雪山に登って 冠雪している遠くの山々を眺めるのが最高です
最近は八ヶ岳(北横岳と蓼科山)に行きました
CodeCommit のリポジトリに Dockerfile をpush すると CodeBuild が docker build して コンテナイメージを作成し ECR のリポジトリに push する仕組みの構築方法 (docker build の自動化)
さて長いタイトルですね。。。
コンテナイメージのビルド環境 を CodeCommit , CodePipeline , CodeBuild を使って構成する機会がありました
実際の環境構築の際にハマったポイント等も含めてブログに書き残すことにしました
こんな構成です
- CodeCommit にあるリポジトリの 開発(develop)/本番環境(production) ブランチ に Dockerfile をpush
- CodePipeline が CodeBuild に連携
- CodeBuild が docker build しイメージ作成
- CodeBuild が 開発(develop)/本番環境(production)のAWSアカウントにある ECR に push
+ Dockerhub へのログイン情報を Secret Manager に保管します
前提
Docker Hub (https://hub.docker.com/) にアカウントを作成してあること
- Docker Hub のアカウントを利用して pull を行うため
- Docker Hub にログインをしていないユーザーが Docker Hub のイメージを Pull する際には IP アドレス毎に回数制限が掛かります (CodeBuild を使って pull すると パブリッククラウドの性質上 他のユーザーの pull 回数の影響を受けることがあります)
- 詳細は:ダウンロード率制限 | Docker ドキュメント
- Docker Hub のアカウントを利用して pull を行うため
各AWSアカウントにリソースを構築するための適切な権限を持つ IAM ユーザーを持っていること
Secret Manager に Docker Hub のアカウント情報を保管する
Docker Hub のアカウント名とパスワードをSecret Manager に保管します
先ずはDocker Hub のアカウント名 (DOCKERHUB_USER) を保管します
※ 値 (xxxx) には Docker Hub のアカウント名を入れてください
上の設定で作成します
同様に パスワードを DOCKERHUB_PASS という名前で保管してください (画像は割愛)
また後ほど使用するため各シークレットのARNをメモしておいてください
CodeCommit にリポジトリを作成
CodeCommit に作成したリポジトリから clone して push するまでの流れは以下のドキュメントを参照ください
フォルダ構成
- nginx
- nginxコンテナイメージ作成用のフォルダ)
- 仮に nginx コンテナとしています (変更可)
- ここにDockerfileを配置
- nginxコンテナイメージ作成用のフォルダ)
- buildspec.yml
- CodeBuild が docker build して コンテナイメージを作成し ECR のリポジトリに push する際に使用する仕様書
- トップディレクトリに buildspec.yml という名前で配置しておくと CodeBuild が読みに行きます
- CodeBuild が docker build して コンテナイメージを作成し ECR のリポジトリに push する際に使用する仕様書
各ファイルの中身
Dockerfile
FROM nginx
buildspec.yml (変更する箇所を下に記載します)
version: 0.2 env: variables: version: "v0.95" secrets-manager: DOCKERHUB_USER: arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:DOCKERHUB_USER-xxxxxx:DOCKERHUB_USER DOCKERHUB_PASS: arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:DOCKERHUB_PASS-xxxxxx:DOCKERHUB_PASS phases: pre_build: commands: - echo Logging in to Amazon ECR - 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 - echo Logging in to Docker Hub - echo $DOCKERHUB_PASS | docker login -u $DOCKERHUB_USER --password-stdin build: commands: - echo Build started on `date` - echo Build and Run the Docker image - docker build -f nginx/Dockerfile -t $IMAGE_REPO_NAME1:$version nginx/ - echo docker tag $IMAGE_REPO_NAME1:$version $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version - docker tag $IMAGE_REPO_NAME1:$version $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version - printf '[{"name":"<container-definition>","imageUri":"%s"}]' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version > artifacts.json artifacts: files: artifacts.json
- secrets-manager セクション
- 以下の部分にメモしておいたシークレットのARN を記載してください ※末尾の「:DOCKERHUB_USER」や「:DOCKERHUB_PASS」を消さないように注意です
- arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:DOCKERHUB_USER-xxxxxx
- arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:DOCKERHUB_PASS-xxxxxx
- 以下の部分にメモしておいたシークレットのARN を記載してください ※末尾の「:DOCKERHUB_USER」や「:DOCKERHUB_PASS」を消さないように注意です
- pre_build セクション
- 変更不要
- ECR と Docker Hub にログインしています
- 変更不要
- build セクション
- CodeCommit に作成したリポジトリのフォルダ名を「nginx」から変えている場合には置き換えてください
- docker build してタグ付与をしています
- CodeCommit に作成したリポジトリのフォルダ名を「nginx」から変えている場合には置き換えてください
- post_build
- 変更不要
- ECR に push しています
- 変更不要
またversion変数は イメージタグになります
push する際に更新すると新しいイメージタグを付与します
env: variables: version: "v0.95" #イメージタグの名前
ブランチ
develop/production ブランチを作成します
CodeBuild作成
ビルドプロジェクトを作成していきます
本番/開発環境用にそれぞれ作る必要があります
ここでは 開発環境用のビルドプロジェクトを作成します
本番環境用のビルドプロジェクトを作る際には「[ソース]セクション」を production にして作成ください
[プロジェクトの設定]セクション
[ソース]セクション
develop ブランチを対象にします
[環境]セクション
イメージの AMD or ARM は コンテナの実行環境に合わせてください
[環境(追加設定)]セクション
以下を環境変数に設定します
- AWS_ACCOUNT_ID
- ECRリポジトリを作成する AWSアカウントのアカウント番号
- AWS_DEFAULT_REGION
- ECRリポジトリを作成するリージョン
- IMAGE_REPO_NAME1
- 作成するECRリポジトリの名前
他のセクションは特に設定しないで「ビルドプロジェクトを作成する」を押します ※必要に応じて変更ください
CodeBuild の 利用する IAM Role
CodeBuild の利用する IAM Role には以下のポリシーを追加付与します
Secret Manager の読み取り権限
- 詳細はドキュメントを参照ください
ECR への push 権限
- 詳細はドキュメントを参照ください
CodeBuild の利用する IAM Role は以下から参照可能です
ECR にリポジトリを作成
本番/開発環境用にそれぞれ作る必要があります
ここでは 開発環境用のECR リポジトリを作成します
ECRの作成は開発環境のAWSアカウントにログインして作業ください
本番環境用のECR リポジトリを作る際には 本番環境のAWSアカウントにログインして作業 してください
プライベートなリポジトリを作成します
CodeBuild を作成するときに 環境変数 IMAGE_REPO_NAME1 に指定した名前で作成します
リポジトリポリシー
CodeBuild に作成したビルドプロジェクトからの push を許可します
そのため Principal にはビルドプロジェクトに設定した IAM Role の ARN を指定します
リポジトリポリシー
{ "Version": "2008-10-17", "Statement": [ { "Sid": "new statement", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::0123456789012:role/service-role/codebuild-yamamoto-test-docker-build-service-role" }, "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:InitiateLayerUpload", "ecr:PutImage", "ecr:UploadLayerPart" ] } ] }
※ここまで作成すると CodeBuild のビルドプロジェクトを正常に実行できます
( 実行すると develop ブランチ の Dockerfile からイメージを作成し ECR に push します)
CodePipeline の作成
本番/開発環境用にそれぞれ作る必要があります
ここでは 開発環境用のビルドプロジェクトを作成します
本番環境用のビルドプロジェクトを作る際には「[ソース]セクション」の「ブランチ名」を production にして作成ください
以下の設定で作成します
(確認画面は割愛します)
パイプラインを 作ったときに 1回パイプラインが実行されてしまう点にご注意ください (仕様なのか確認中)
これにて完成です😺
動かしてみる
buildspec.yml 内のイメージタグ変数を 「v1」に変更して CodeCommit の develop ブランチに push します
env: variables: version: "v1"
CodePipeline が動きました
CodeBuild による docker build が成功しています
「v1」タグのイメージがECR に push されていました 🎉
(イメージ自体に変更を加えていないためタグが追加された)
複数のコンテナイメージをビルドするとき
複数のコンテナイメージをビルドするときは以下のようにします
- 新しいコンテナ用にECR リポジトリを作成
- CodeBuild のビルドプロジェクトに環境変数($IMAGE_REPO_NAMEx)を追加
- CodeCommit の対象ブランチに 新しいコンテナイメージ 用のディレクトリと Dockerファイルを追加
- buildspec.yml のbuild セクションと post_buildセクションを修正し push
nginx2 コンテナを追加する例
1.nginx2 コンテナ用にECR リポジトリ作成
またリポジトリポリシーを設定します (割愛)
2.CodeBuild のビルドプロジェクトに環境変数($IMAGE_REPO_NAMEx)を追加
作成したECRリポジトリの情報を追加します
3.CodeCommit の対象ブランチに 新しいコンテナイメージ 用のディレクトリと Dockerファイルを追加
developブランチに nginx2 用のディレクトリと Dockerファイルを追加します
4.buildspec.yml のbuild セクションと post_buildセクションを修正し push
buildspec.yml ※ 「#追加」と書いてある箇所(全部で5行)になります
version: 0.2 env: variables: version: "v2.0" secrets-manager: DOCKERHUB_USER: arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:DOCKERHUB_USER-xxxxxx:DOCKERHUB_USER DOCKERHUB_PASS: arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:DOCKERHUB_PASS-xxxxxx:DOCKERHUB_PASS phases: pre_build: commands: - echo Logging in to Amazon ECR - 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 - echo Logging in to Docker Hub - echo $DOCKERHUB_PASS | docker login -u $DOCKERHUB_USER --password-stdin build: commands: - echo Build started on `date` - echo Build and Run the Docker image - docker build -f nginx/Dockerfile -t $IMAGE_REPO_NAME1:$version nginx/ - echo docker tag $IMAGE_REPO_NAME1:$version $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version - docker tag $IMAGE_REPO_NAME1:$version $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version # 追加 - docker build -f nginx2/Dockerfile -t $IMAGE_REPO_NAME2:$version nginx/ - echo docker tag $IMAGE_REPO_NAME2:$version $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME2:$version - docker tag $IMAGE_REPO_NAME2:$version $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME2:$version post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version - printf '[{"name":"<container-definition>","imageUri":"%s"}]' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME1:$version > artifacts.json # 追加 - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME2:$version - printf '[{"name":"<container-definition>","imageUri":"%s"}]' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME2:$version >> artifacts.json artifacts: files: artifacts.json
結果: 同じタグでpushされていました🎉
おわりに
docker build を各人の環境で行って ECR に push する部分が自動化できました
3ヶ月ほど運用して特に何事もなく運用できています
なにかあればまた追記しますね
山本 哲也 (記事一覧)
カスタマーサクセス部のエンジニア。2024 Japan AWS Top Engineers に選んでもらいました。
今年の目標は Advanced Networking – Specialty と Machine Learning - Specialty を取得することです。
山を走るのが趣味です。今年の目標は 100 km と 100 mile を完走することです。 100 km は Gran Trail みなかみで完走しました。残すは OSJ koumi 100 で 100 mile 走ります。実際には 175 km らしいです。「草 100 km / mile」 もたまに企画します。
基本的にのんびりした性格です。座右の銘は「いつか着く」