構成
- AWSに構築したCI環境
- CI環境を構築するCloudformationテンプレートと構築手順
- 本手順の注意点
- まとめ
AWSに構築したCI環境
AWS構成図
以下のような環境を構築しました。
コードリポジトリにはGithubを使用し、ここで Dockerfile と AWS CodeBuild に渡す buildspec.yaml をコード管理しています。
CIの流れ
CIの流れは以下のようになります。
- 開発者がGithubにpush
- 1.で変更がある場合はCodePipelineが起動 (trigger or pollilng)
- CodePipelineがGithubリポジトリからコードをpull
- CodeBuildにコードが渡り、Dockerイメージビルド&ECRへのpushを実行 (buildspec.yamlの内容)
CodePipelineとCodeBuildは、それぞれAWS内部リソースへアクセスするため、サービスロールという形で権限付与する必要があります。
また、CodePipelineはパイプラインの各ステージ間で、ソースコードやビルド後の成果物を共有する際に(後段のステージに渡す際に)S3バケットを必要とします。
CI環境を構築するCloudformationテンプレートと構築手順
Cloudformationテンプレート
上記の構成を実現するCloudformationテンプレートを作成しました。
cloudformationテンプレート
このテンプレート使用したCI環境構築は後ほど説明します。
buildspec.yaml
buildspecには以下のコマンドが記述されています。
- Dockerイメージビルドのコマンド
- ECRへイメージpushするコマンド
<ECRレジストリURI>、<リポジトリ名>は、ECR作成後に取得して、追記が必要です。 後ほど説明します。
buildspec.yml
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
- REPOSITORY_URI=<ECRレジストリURI>
- 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:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker images...
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- echo Writing image definitions file...
- printf '[{"name":"<リポジトリ名>","imageUri":"%s"}]'
$REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
構築手順
上記テンプレートを使用して、実際に構築する手順を紹介します。
- DockerイメージのGithubリポジトリを用意する
- GithubアカウントのOAuthTokenを取得する
- Cloudformationsスタックの作成
- Githubリポジトリへpush
1. DockerイメージのGithubリポジトリを用意する
Dockerfileとbuildspec.yamlが含まれたGithubリポジトリを用意します。
ファイル構成は以下です。
既存のGithubリポジトリに適用する場合も、所定のDockerfileと、上記で説明したbuildspec.yamlを追加するだけです。
.
├── Dockerfile
└── buildspec.yaml
2. GithubアカウントのOAuthTokenを取得する
Githubへログインし、[Settings]→[Developer settings]→[Personal access tokens]へ移動し、「Generate new token」をクリック
Token description と Select scopesの「repo」を全選択し、「Generate Token」をクリック
生成完了後、トークンが表示されるのはこの画面だけです。
画面を移動する前にトークンをコピーしておきましょう。
余談ですが、このトークンはセキュリティ上重要なもので、漏洩すると不正攻撃に利用されてしまいます。
LastPassやOnePasswordなど、セキュア情報管理サービスを利用するなどして安全に保存するのがおすすめです。
Githubリポジトリ内のコードへのハードコーディングは絶対にやめましょう。
3. Cloudformationsスタックの作成
先ほど紹介したテンプレートを使用して、CI環境のAWSスタックを構築します。
Cloudformationのコンソールを開き「スタックの作成」をクリック
「テンプレートを Amazon S3 にアップロード」を選択し、テンプレートファイルを指定する
テンプレートファイルは、以下リポジトリよりダウンロードできます。
cloudformationテンプレート
本テンプレートは以下のパラメータを指定できます。(必須)
- Env - 環境名
- CodeRepository - Githubリポジトリ名
- Branch - Githubリポジトリのデプロイ対象とするブランチ名
- ImageRegistry - 作成するECRレジストリ名
- Owner - Githubアカウント名
- OAuthToken - 「2. GithubアカウントのOAuthTokenを取得する」で取得したOAuth Token
例として、以下のように設定します。
オプション設定はそのままで次へ
「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にチェックを入れて、「作成」をクリック
Cloudformationに作成したスタックが表示されているはずです。 作成には少し時間がかかります。 以下のように「CREATE_COMPLETE」となれば完了です。
4. Githubリポジトリへpush
CodePipelineに作成したスタック名と同じpipelineが作成されています。
まずは、SourceステージのGithubリポジトリからのPullが成功しているかを確認します。
失敗している場合、エラー内容をみて、リポジトリ名やOAuth Tokenに誤りがないかなど確認しましょう。
このままでもpipelineは起動しますが、buildspec.yamlにECRリポジトリURLを記述していないため、ビルドに失敗します。
「1. DockerイメージのGithubリポジトリを用意する」で用意したGithubリポジトリのbuildspwc.yamlの<リポジトリURL>と<リポジトリ名>を埋めて、pushしましょう。
これらはECRの画面から確認できます。
リポジトリ名とURI欄をコピーして、それぞれbuildspec.yamlにペーストします。
追記したbuildspec.yamlをpushすると、ビルドも成功するはずです。
ECRにも、Dockerイメージがpushされています。
本手順の注意点
Githubリポジトリの変更検出方式がポーリングである
本記事で紹介している方式では、「CodePipelineはGithubの変更によってトリガーされて起動する」ような書き方をしています。
これはイメージ的にわかり易くするためですが、実際にはトリガーされるのではなく、「CodePipelineがGithubリポジトリをポーリング」しています。
以前はこの方式しかなかったのですが、2018年5月にGithubのWebhookイベントに対応し、本当にGithubからCodePipelineをトリガーすることができるようになりました。
https://aws.amazon.com/jp/about-aws/whats-new/2018/05/aws-codepipeline-supports-push-events-from-github-via-webhooks/
Webhookに対応するやり方は、改めてまとめたいと思います。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/pipelines-webhooks-migration.html
CI環境を構築する度にS3バケットと各種サービスロールが作成される
これには2つ問題があります。
- 作成されるリソースが多くなるため管理が煩雑化
- アカウント毎に作成可能なリソース上限に引っかかる
一つ目は特に説明は不要かと思いますし、それほど大きな問題でもないですが、二つ目に関しては結構重大な問題になる場合があります。
S3バケットはAWSアカウント毎に 100個までしか作成できません。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/BucketRestrictions.html
CodePipelineには、アーティファクトバケット(Githubからpullしたコードなどを各ステージへ受け渡すための保存用のバケット)が必要です。
本記事で紹介したテンプレートを使用してスタックを構築する度に、S3バケットが増えていくことになります。
もし、他の要因で大量のバケットが存在している場合や、100を超えるCI環境を構築したい場合には、アーティファクトバケットをパラメータで指定するなどの工夫が必要です。
また、IAM Roleについても同じことが言えます。
こちらはAWSアカウント毎に1000オブジェクトまで作成可能なので、S3バケットほどすぐには問題になりませんが、管理煩雑化を低減するためにも、S3と同様の対策をしておいたほうが良さそうです。
https://docs.aws.amazon.com/jajp/IAM/latest/UserGuide/referenceiam-limits.html
本問題について、対策したものは改めてまとめたいと思います。
まとめ
「DockerイメージをECRへデプロイするCI環境」を構築するCloudformationテンプレートと構築手順を紹介しました。
Dockerは、今後使われていく機会が増えていくと思いますので、単にECRへのデプロイを自動化するCI環境も、サクッと構築したい場合に参考になるかと思います。
ぜひご活用ください。
CodePipelineは、ECSへのデプロイにも対応しており、ECRへのDockerイメージpushと同時にECSへのデプロイも自動化したCICD環境を作ることも可能です。
この環境についても、改めてまとめたいと思います。