AWSのCodePipelineについて整理
前回は、AWSのECRとEC2の役割について整理しました。
今回はその関連サービスとして、CodePipelineについてまとめたいと思います。
今回のプロジェクトでも、S3やデプロイ処理とあわせて使われていたため、EC2やECRとつなげて理解すると分かりやすかったです。
整理シリーズ第2弾として、今回はCodePipelineの基本機能、連携しやすいサービス、そしてプロジェクトでの活用方法を振り返ります。
CodePipelineとは何か
CodePipelineは、AWSでデプロイの流れを自動化するためのサービスです。
ソースの取得、ビルド、テスト、デプロイといった処理を、1つの流れとしてまとめて管理できます。
手作業でデプロイする場合は、ファイルの取得、ビルド、反映を毎回人が行う必要があります。
一方、CodePipelineを使うと、あらかじめ決めた手順に沿って処理を進められます。
そのため、作業の抜け漏れを減らしやすくなります。
CodePipelineで何ができるのか
CodePipelineでは、処理の流れをステージとアクションに分けて構成します。
たとえば、Source、Build、Deployのように工程を並べ、その中に具体的な処理を置いていきます。
また、各工程ではアーティファクトを受け渡します。
これは、ソースコードやビルド成果物など、次の工程へ渡すファイルのことです。
Buildで作った成果物をDeployで使う、という流れはこの仕組みで成り立っています。
CodePipelineと連携しやすいサービス
CodePipelineは単体で完結するというより、周辺サービスと組み合わせて使うことが多いです。
そのため、連携先の役割もあわせて理解しておくと、全体像が見やすいです。
S3
S3は、ソースファイルやZIPファイルの置き場として使えます。
今回のプロジェクトでも、ZIP化したファイルをS3へ置き、それをきっかけに処理が始まっていました。
そのため、S3はCodePipelineの入口として使われていたと考えられます。
CodeBuild
CodeBuildは、ビルドやテストを実行するサービスです。
CodePipelineの中では、BuildステージやTestステージとして使われることが多いです。
リポジトリのルート直下にある buildspec.yml に従って処理を実行できるため、ビルド内容をソースコードと一緒に管理しやすい点が特徴です。
CodeDeploy
CodeDeployは、EC2やオンプレミス環境へ成果物を配布するサービスです。
EC2へアプリを配る構成では、CodePipelineのDeployステージからCodeDeployにつなぐ形がよくあります。
EC2と組み合わせる場合に理解しておきたいサービスです。
ECR
ECRは、コンテナイメージの保存先です。
CodeBuildでDockerイメージを作成し、それをECRへpushしておき、その後ECSなどが取得して実行する流れがよく使われます。
前回整理したECRは、CodePipelineの後半で登場しやすいサービスです。
ECS
ECSは、コンテナを実行するサービスです。
CodePipelineからECS Deployアクションを使うことで、ECRに保存したイメージを反映できます。
コンテナ構成のAWSでは、CodePipeline・CodeBuild・ECR・ECSの組み合わせがよく使われます。
CodeConnections
GitHubやGitLab、Bitbucketなどの外部リポジトリと連携するときは、CodeConnectionsが使われます。
これにより、Gitのpushをきっかけにパイプラインを開始できます。
とりあえず外部GitサービスとAWSをつなぐ入口、と捉えておくと分かりやすいです。
よくあるユースケース
CodePipelineは、構成によって使い方が少し変わります。
ただ、実務ではよく似た形にまとまることが多いです。
1. S3に置いたZIPをEC2へ反映する
ZIPファイルをS3へ置き、CodePipelineがそれを受け取り、CodeBuildやCodeDeployを通じてEC2へ反映します。
今回のプロジェクトは、この流れに近かったです。
2. GitHubのコードをビルドしてECSへデプロイする
GitHubでコードを管理し、CodeConnectionsで取得し、CodeBuildでDockerイメージを作成します。
その後、ECRへpushし、ECSへ反映する流れです。
3. CloudFormationの変更を安全に適用する
インフラ構成そのものを変更したい場合は、CloudFormationと組み合わせることがあります。
変更内容を確認し、承認を入れてから反映する流れにすると、安全性を高めやすくなります。
4. 本番反映前に手動承認を入れる
すべてを完全自動にせず、最後だけ承認を入れる構成もあります。
たとえば、開発環境までは自動、本番だけは人が確認してから反映する形です。
このようにすると、本番環境への変更を慎重に進めやすくなります。
プロジェクトでの活用方法
S3・CodePipelineを使ったデプロイの流れ
今回のプロジェクトでは、プロジェクトファイルをZIPにまとめてS3へ格納していました。
その後、CodePipelineを使ってデプロイ処理を進める構成になっていました。
私は構成の細部までは把握していませんでしたが、少なくともS3への配置をきっかけに処理が進んでいたように見えました。
この流れを簡単に書くと、次のようになります。
- 修正したプロジェクトファイルをZIP化する
- ZIPファイルをS3へアップロードする
- CodePipelineが処理を開始する
- ビルドを行う
- 成果物をデプロイ先へ反映する
この構成では、S3がファイル置き場になります。
CodePipelineは、その後の処理を自動で進める役割でした。
実際に便利だと感じた点
この仕組みが入る前は、AWS CLIを使ったローカルビルドが中心でした。
そのため、デプロイ時には手作業で進める部分も多かったです。
それに比べると、S3への配置をきっかけにCodePipelineが動く形になってからは、デプロイ手順がかなり分かりやすくなったと感じました。
私自身、構成の細かい部分まで把握できていたわけではありません。
それでも、処理の流れが整理されて自動で進むだけで、運用の負担がかなり減ることは実感できました。
こうした仕組みが、CI/CDに近い考え方なのだと思います。
buildspec.yml でどこまで制御できるのか
CodePipelineを理解するうえで、CodeBuildのbuildspec.ymlも重要です。
buildspec.yml は、CodeBuildが何を実行するかを定義するYAMLファイルです。
リポジトリのルートに置いて、ビルド手順をソースコードと一緒に管理する形が基本です。
ここで大事なのは、パイプライン全体の骨格はAWS側で定義し、Buildの中身はbuildspec.ymlで定義するという役割分担です。
Sourceアクションで取り込まれたコードの中に buildspec.yml を含めることで、BuildとDeployの詳細な挙動をソースコードと一緒に管理できます。
buildspec.ymlでできること
buildspec.yml では、たとえば次のような内容を制御できます。
- どのコマンドでビルドするか
- どのDockerfileを使うか
- ECRへどのタグでpushするか
- Deploy用の成果物を何として出力するか
buildspec.ymlでできないこと
一方で、buildspec.yml では扱わない範囲もあります。
- どのリポジトリをSourceにするか
- どのブランチを監視するか
- Buildの後にどのDeployアクションを置くか
- 承認フローを入れるかどうか
このあたりは、パイプライン定義側で管理する内容です。
つまり、構造はAWS側、実行内容はbuildspec.yml側と考えると整理しやすいです。
buildspec.yml のコード例
たとえば、CodeBuildでDockerイメージを作成し、ECRへpushし、ECS Deploy用の imagedefinitions.json を出力する例は次のようになります。
pre_build でECRログイン、build でDocker build、post_build でpushと imagedefinitions の生成、artifacts で次のステージへ渡す、という流れです。
なお、以下の例は実際の設定値をそのまま載せたものではなく、構成が分かるように一般化したサンプルです。
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
- AWS_REGION=your-region
- ECR_REPOSITORY=your-ecr-repository
- IMAGE_TAG=your-image-tag
- REPOSITORY_URI=$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $REPOSITORY_URI
build:
commands:
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:$IMAGE_TAG -f Dockerfile .
post_build:
commands:
- echo Pushing the Docker image...
- docker push $REPOSITORY_URI:$IMAGE_TAG
- printf '[{"name":"app","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
このコードの見方
コードは、次の流れで読むと理解しやすいです。
入力
ソースコード、Dockerfile、AWSの認証情報、ECRリポジトリ情報
処理
ECRへログインする
→ Dockerイメージをビルドする
→ ECRへpushする
→ ECS Deploy用の imagedefinitions.json を作る
出力
ECRに保存されたコンテナイメージ
imagedefinitions.json
目的
Build結果を次のDeployステージへ正しく渡すこと
imagedefinitions.json は何をしているのか
imagedefinitions.json は、ECS Deployアクションに対して、どのコンテナ名に、どのイメージURIを適用するかを伝えるためのファイルです。
ECSのDeployアクションでは、このファイルを入力として受け取り、対象コンテナの image を更新します。
つまり、ECS DeployはECRの latest を自動で見に行くのではなく、CodeBuildが出力した imagedefinitions.json を入力として使います。
この点を押さえると、CodePipeline・CodeBuild・ECR・ECSのつながりが理解しやすくなります。
まとめ
今回のプロジェクトを通じて、CodePipelineはデプロイ作業の流れを自動化するためのサービスだと整理できました。
単に処理を実行するだけでなく、ソース取得からビルド、デプロイまでを流れとして管理できる点が大きな特徴です。
また、S3と組み合わせることで、ファイルの配置をきっかけに処理を進める構成にできることも分かりました。
さらに、CodePipelineは単体で使うよりも、S3、CodeBuild、CodeDeploy、ECR、ECS、CodeConnectionsなどと組み合わせることで、役割がより見えてきます。
よくあるユースケースを知っておくと、「このサービスはどこで使うのか」がつかみやすくなりました。
そして、buildspec.yml の視点を入れると、パイプラインの骨格はAWS側で管理しつつ、BuildやDeployの具体的な挙動はソースコード側から管理できることも分かります。
この考え方は、今回かなり重要な学びだったと感じます。
今後は、CodeBuildやCodeDeploy、ECSまわりも含めて理解を深めていきたいと思います。
参考