はじめに
こんばんは、mirukyです。
AWSでCI/CDを実現しようとすると、CodeCommit、CodeBuild、CodeDeploy、CodePipelineといった名前が次々に出てきます。いわゆるCodeシリーズです。自分の場合、サービスを1つずつ単体で覚えて、いざ自分で組み始めた時につまりまくった苦い経験があります。
理由は単純で、CI/CDは複数のサービスが連携して初めて動くものだからです。ソースコードをどこに置くのか、ビルドはどこで動かすのか、できあがった成果物を次の工程へどう渡すのか、EC2やECS、Lambdaへどうやって配るのか、本番に出す前に誰がOKを出すのか、もし失敗したらどうやって元に戻すのか。これらが1本のレールとしてつながったとき、ようやくCI/CDは役に立ちます。
この記事は、そのレールを端から端まで追えるように書きました。CI/CDの基礎から始めて、各サービスの役割、設定ファイルの書き方、実際に手を動かすハンズオン、本番で運用するときの注意点まで、順番に進めていきます。途中で出てくる専門用語にはそのつど説明を足しているので、AWSのCI/CDをこれから触る人でも、上から読み進めれば全体像がつかめるはずです。
AWS Codeシリーズ周辺は、近年サービスの提供状況に変更が続いています。
例えば2024年7月に新規利用が止まっていたAWS CodeCommitは、2025年11月25日に新規顧客も利用できる状態に戻りました。一方、Amazon CodeCatalystとAmazon CodeGuru Reviewerは、現時点(2026年6月1日)で新規利用に制限があります。この記事では、2026年6月1日時点の公式情報に合わせて、CodeCommit継続利用とGitHub連携の両方を選べる形で書いています。
この記事で扱う範囲
目次
- CI/CDの基本
- AWS Codeシリーズの全体像
- ソースコード管理とCodeCommitのいま
- GitHubとCodeConnectionsの準備
- CodeBuildの基礎
- buildspec.ymlの実践
- CodeDeployの基礎
- EC2へのデプロイ
- デプロイ戦略とロールバック
- ECSとLambdaへのカナリアデプロイ
- CodePipelineの基礎
- CodePipeline V2の実践機能
- 高度なパイプライン設計
- 【ハンズオン】ゼロから作るCI/CDパイプライン
- セキュリティとIAM設計
- コスト、監視、ログ運用
- トラブルシューティング
- 設計パターンとチーム運用
1. CI/CDの基本
CI/CDという言葉を初めて聞くと難しそうに感じますが、やっていることは「コードを書いてから本番で動くまでの作業を、できるだけ自動化する」ことです。

図で書くとこのようになります。後半のハンズオンセクションで、実際にこの構成を作成しますので、お楽しみに!
この、コードを書いてから本番環境で動かす作業を手でやると、テストを流し忘れたり、サーバーへのファイルのコピーを間違えたり、リリースのたびに誰かが手順書を確認しながら深夜作業をしたり、といったことが起きます。こうした一連の流れを機械に任せて、毎回まったく同じ手順で、速く、安全に回せるようにするのがCI/CDです。CIとCDという2つの言葉に分かれているので、まずそれぞれが何を指すのかを確認していきます。
1-1. CI
CIはContinuous Integrationの略で、日本語では継続的インテグレーションと呼ばれます。「インテグレーション(統合)」とは、自分のパソコンで書いたコードを、チームで共有しているリポジトリへ合流させることだと考えてください。CIは、この合流をこまめに行い、そのたびにビルドやテスト、静的解析などを自動で走らせる進め方です。
| 項目 | 内容 |
|---|---|
| 目的 | 統合時のバグを早く見つける |
| トリガー | push、pull request、手動実行 |
| 自動化する処理 | コンパイル、ユニットテスト、Lint、フォーマット確認 |
| AWSでの中心サービス | CodeBuild |
イメージとしては、コードを共有リポジトリにpushした瞬間に自動でテストが走り、どこか壊れていればすぐ赤信号で知らせてくれる、という感じです。pushのみでなく、例えばPR(プルリクエスト)を出した時に自動テストを走らせたりすることもできます。CIがないと、テストの流し忘れ、自分の環境では動くのに他の人の環境では動かない問題などが起きます。小さな変更をこまめに、しかも自動で検証していく。これがCIの一番大事なところです。
1-2. CD
CDには、よく似ているが実は意味の違う2つの言葉があります。
| 用語 | 意味 | 本番反映 |
|---|---|---|
| Continuous Delivery | いつでも本番へ出せる状態を保つ | 手動承認を挟む運用も多い |
| Continuous Deployment | テスト通過後に自動で本番へ出す | 完全自動 |
2つの違いは、本番へ反映する最後の一歩を人が押すか、機械が自動で押すかにあります。Continuous Deliveryは、変更をいつでも安全に本番へリリースできる状態に保つ考え方です。本番反映を人が判断する運用では、最後に手動承認を挟みます。責任者の承認やリリースしてよい時間帯(変更窓)が必要な組織では、この形が採用されやすいです。後ほど登場するCodePipelineの「手動承認ステージ」は、まさにこの「最後だけは人が判断する」やり方にぴったりはまります。どちらの意味であろうと、ちゃんとCDです。
1-3. CI/CDの全体フロー
この図は上から下へ読みます。コードが少しずつ本番環境に近づいていく流れだと思ってください。途中に出てくる「アーティファクト」はビルドでできあがった成果物(あとで配るためのファイル一式)、「ステージング」は本番そっくりに用意した確認用の環境のことです。今はすべての用語を覚えなくて大丈夫で、それぞれ該当する章でくわしく説明します。
AWSでこの流れを組むときは、ソースコード管理にCodeCommitやGitHub、ビルドにCodeBuild、デプロイにCodeDeploy、そして全体をつなぐ司令塔の役割にCodePipelineを使います。次の章から、それぞれが何をしてくれるサービスなのかを順番に確認していきます。
2. AWS Codeシリーズの全体像
全体の流れがつかめたところで、その流れを支えるAWSのサービスを上から眺めてみます。名前が似ていて混乱しやすいので、まずは「どれが何の担当か」をある程度頭に入れるのが目的です。
2-1. 4つの主要サービス
| サービス | 役割 | 主な設定ファイル |
|---|---|---|
| AWS CodeCommit | マネージドGitリポジトリ | なし |
| AWS CodeBuild | ビルドとテスト | buildspec.yml |
| AWS CodeDeploy | EC2、ECS、Lambdaへのデプロイ | appspec.yml |
| AWS CodePipeline | CI/CD全体のオーケストレーション | パイプラインJSON、コンソール設定 |
ここで間違えやすいのが、CodePipelineは自分でビルドやデプロイをするわけではない、という点です。実際にビルドするのはCodeBuild、デプロイするのはCodeDeployで、CodePipelineは「次はビルド、終わったらデプロイ」と順番に呼び出してまとめる司令塔の役回りです。オーケストラの指揮者が自分では楽器を弾かないのと同じで、各アクションをどの順番で動かすか、成果物を次へどう渡すか、途中で失敗したらどこで止めるか、を取り仕切ります。
2-2. AWSサービスとの対応
CI/CDの工程ごとのAWSサービス対応表は下記になります。
| CI/CDの工程 | AWSサービス | 補足 |
|---|---|---|
| ソース管理 | CodeCommit | CodeCommitはAWS内で直接連携。AWSサービスではないGitHub、GitLab、Bitbucket CloudなどはCodeConnections経由で接続 |
| ビルド | CodeBuild | EC2コンピュートとLambdaコンピュートを選択 |
| 成果物保存 | S3、ECR | ZIP、静的ファイル、Dockerイメージなど |
| デプロイ | CodeDeploy、CloudFormation、ECS、S3 | 対象により最適なアクションが変わる |
| 承認 | CodePipeline Manual approval | 本番反映前のチェックポイント |
| 通知 | SNS、EventBridge、Amazon Q Developer in chat applications(旧AWS Chatbot) | 失敗通知や承認依頼に使う |
| 監視 | CloudWatch、CloudTrail | ログ、メトリクス、監査証跡 |
2-3. 2026年時点のサービス選定
| やりたいこと | 第一候補 | 補足 |
|---|---|---|
| AWS内でGitを完結したい | CodeCommit + 各Codeシリーズ | 2025年11月に新規利用が再開 |
| GitHub中心で開発したい | GitHub + CodeConnections +各Codeシリーズ(CodeCommit以外) | 一般的な選択肢の一つ |
| ビルドだけAWSで実行したい | CodeBuild | GitHub Actionsとの併用も可能 |
| EC2へ安全に反映したい | CodeDeploy | Agentとappspec.ymlが必要 |
| ECSやLambdaへ段階反映したい | CodeDeploy | Canary、Linear、AllAtOnceを選択 |
| 複数工程をつなぎたい | CodePipeline V2 | 新規構成ではV2を基本に考える |
2-4. Codeシリーズ以外の選択肢
AWSでCI/CDを組む方法はCodeシリーズだけではありません。GitHub Actions、GitLab CI/CD、Bitbucket Pipelines、Terraform Cloud、CircleCIなども候補になります。
ここで大事なのは、どれか1つだけを選ぶというより、どこを主役にして、どこをAWSに任せるかを決めることです。たとえば、GitHub Actionsを主役にしつつ、デプロイ先だけCodeDeployにする構成もあります。逆に、CodePipelineを主役にして、ソースだけGitHubから受け取る構成もあります。要は、組み合わせが大事だということです。
| 主役にする構成 | 基本の組み合わせ | 追加で使うことが多いもの | 向いている場面 |
|---|---|---|---|
| GitHub中心 | GitHub + GitHub Actions | OIDC、CodeDeploy、ECS、Lambda | 開発体験をGitHubに寄せたい |
| GitLab中心 | GitLab + GitLab CI/CD | OIDC、ECR、ECS、CodeDeploy | GitLabでソース管理とCIを統合したい |
| Bitbucket中心 | Bitbucket + Bitbucket Pipelines | OIDC、ECR、CodeDeploy | Atlassian製品と合わせたい |
| AWS実行基盤中心 | GitHub + CodeConnections + CodePipeline + CodeBuild | CodeDeploy、CloudFormation、ECS、SNS | ソースはGitHub、実行と権限管理はAWSに寄せたい |
| AWS内完結 | CodeCommit + CodePipeline + CodeBuild | CodeDeploy、EventBridge、CloudWatch | IAM統制、監査、AWS内完結を重視したい |
本文では、AWS Codeシリーズの記事なので、CodePipeline、CodeBuild、CodeDeployを中心に進めます。ただし、GitHub ActionsやGitLab CI/CDを使っている現場でも、CodeBuildだけ使う、CodeDeployだけ使う、という切り出し方は十分にあります。
Amazon CodeCatalystは、ソースコード管理、課題管理、CI/CDをひとまとめにした統合開発サービスとして使われてきました。ただし、2025年11月7日以降は新規顧客が利用できなくなっているので、これから新しく組む構成の中心には据えず、すでに使っている人向けの選択肢として頭の片隅に置いておくくらいでよいでしょう。
このように、結構CodeシリーズはGAされたり提供が終わったりするので、状況を適宜確認する必要があります(ただし、元々使っていた場合は継続利用できる場合も多いです)。
3. ソースコード管理とCodeCommitのいま
3-1. CodeCommitの概要
AWS CodeCommitは、AWSが提供するマネージド(サーバーの面倒をAWSが見てくれる)なプライベートGitリポジトリです。IAMでアクセスを制御し、KMSで保存データを暗号化し、TLSで通信を暗号化し、CloudTrailで操作履歴を残す、というように、アクセス管理から暗号化、監査までをAWSの仕組みの中でまとめて面倒を見られるのが強みです。
| 項目 | 内容 |
|---|---|
| プロトコル | HTTPS、SSH |
| 認証 | IAM、Git認証情報、SSH公開鍵 |
| 暗号化 | 保存時と通信時に対応 |
| 通知 | SNS、Lambda、EventBridge |
| 統合 | CodeBuild、CodePipeline、CloudTrail |
| 向いている組織 | AWS内で開発基盤を閉じたいチーム、規制産業、IAM統制を重視するチーム |
3-2. CodeCommitの経緯
| 日付 | 出来事 |
|---|---|
| 2024年7月25日 | CodeCommitが新規顧客向けに利用停止 |
| 2025年11月25日 | CodeCommitが新規顧客にも再び利用可能になる |
| 2026年6月1日時点 | 新規利用を含め、CodeCommitを選択肢に戻してよい |
すでにGitHubやGitLabへ移ったチームは、そのまま使い続けて問題ありません。一方で、AWSの中だけで完結させたい、IAMで権限をきっちり締めたい、VPCエンドポイントやCloudTrail監査を効かせたい、といった事情があるなら、CodeCommitをもう一度候補に入れてよくなった、ということです。
3-3. GitHub、GitLab、Bitbucketとの比較
| 観点 | CodeCommit | GitHub | GitLab | Bitbucket |
|---|---|---|---|---|
| AWS統合 | 強い | CodeConnections経由 | CodeConnections経由 | CodeConnections経由 |
| IAM統制 | 強い | OIDCやGitHub権限管理 | OIDCやGitLab権限管理 | Atlassian権限管理 |
| コラボレーション | 基本機能中心 | Issues、Projects、PRが強い | Issue、Board、CIが強い | Jira連携が強い |
| CI/CD | CodeBuildと連携 | GitHub Actionsも選択可能 | GitLab CI/CDも選択可能 | Bitbucket Pipelinesも選択可能 |
| AWS内完結 | しやすい | しにくい | しにくい | しにくい |
この記事のハンズオンはGitHubを前提にします。CodeCommitを使う場合も、SourceステージをCodeCommitに置き換えれば、CodeBuild、CodeDeploy、CodePipelineの考え方はほぼ同じです。
3-4. CodeCommitから他のGitサービスへ移す手順
移行する場合は、履歴、ブランチ、タグをまとめて移せるミラークローンが基本です。
# CodeCommitリポジトリをミラークローン
git clone --mirror https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/my-repo temp-repo
# GitHubなどの空リポジトリへ向ける
cd temp-repo
git remote set-url origin git@github.com:my-org/my-repo.git
# 全ブランチとタグを移行
git push --mirror
# 作業ディレクトリを削除
cd ..
rm -rf temp-repo
移行後は、保護ブランチ、CODEOWNERS、CI/CD設定、Webhook、Secrets、パイプラインのSource設定を必ず見直します。
4. GitHubとCodeConnectionsの準備
4-1. GitとSSH鍵の準備
GitHubをソースに使う場合は、ローカルGit設定とSSH鍵を準備します。
# Gitのユーザー名とメールアドレスを設定
git config --global user.name "your-name"
git config --global user.email "your-email@example.com"
# デフォルトブランチ名をmainにする
git config --global init.defaultBranch main
# SSH鍵を作成
ssh-keygen -t ed25519 -C "your-email@example.com"
# 接続確認
ssh -T git@github.com
GitHub側でリポジトリを作成し、main ブランチに初期ファイルをpushしておきます。
4-2. GitHubリポジトリの作成
検証用リポジトリは、READMEだけを置いた空に近い状態で作ります。このあとのCodeBuild、CodeDeploy、CodePipelineで同じリポジトリを使い回すことで、1つのコード変更がビルドからデプロイまでどう流れていくかを、ひとつながりで観察できます。
# リポジトリをクローン
git clone git@github.com:your-name/aws-code-series-demo.git
cd aws-code-series-demo
# 最初のファイルを追加
cat > README.md << 'EOF'
# aws-code-series-demo
AWS CodeシリーズのCI/CD検証用リポジトリです。
EOF
# コミットしてpush
git add README.md
git commit -m "Add initial README"
git push origin main
mainブランチの保護や必須レビュー、必須ステータスチェックは、後からでも追加できます。最初から欲張らず、まずはシンプルに作って、パイプラインがひととおり動いてから保護ルールを足していくと、余計なところでつまずかずに済みます。
4-3. CodeConnectionsの役割
GitHubはAWSの外にある別のサービスです。そのため、AWSが何もせずに勝手にあなたのリポジトリを読んだり、pushを検知したりすることはできません。そこで、AWS側に「このGitHubリポジトリにアクセスしてもいいですよ」と許可を渡しておく仲介役が必要になります。これがAWS CodeConnectionsです。一度つないでおけば、CodePipelineはこの接続を通してリポジトリの変更を受け取れるようになります。
AWS CodeConnectionsは、AWSとGitHub、GitLab、Bitbucket Cloudなどをつなぐための接続機能で、2024年3月29日にAWS CodeStar Connectionsから名称が変わりました。
CodePipelineでGitHubを使う場合、Sourceアクションは通常 CodeStarSourceConnection プロバイダを使います。名前にCodeStarが残っていますが、接続管理のサービス名はAWS CodeConnectionsです。
| 項目 | 内容 |
|---|---|
| 接続対象 | GitHub、GitHub Enterprise Server、GitLab、Bitbucket Cloudなど |
| 役割 | AWS側からリポジトリ変更を検知し、ソースを取得する |
| 必要権限 | codeconnections:UseConnection |
| 出力形式 | ZIP形式、またはCodeBuild向けのFull clone参照 |
| 注意 | 一部リージョンでは接続アクションに制限がある |
4-4. 接続作成と確認コマンド
コンソールで接続を作る場合は、「デベロッパー用ツール」の「接続」からGitHubを選びます。接続名を入力したあと、「アプリインストール - オプショナル」で「新しいアプリをインストールする」を押し、GitHub側でAWS Connector for GitHub by AWS CodeConnectionsをインストールします(ここでやらなくても、ハンズオンセクションでスクショを入れつつ、もう一度詳しく解説しますのでご安心ください)。
この流れで進めると、AWS側のアプリインストール欄にインストールIDが自動で入り、そのまま接続を作成できます。接続ステータスが 「利用可能」になれば、CodePipelineのSourceアクションで利用できます。
# 接続一覧を確認
aws codeconnections list-connections \
--provider-type-filter GitHub \
--query 'Connections[*].{Name:ConnectionName,Status:ConnectionStatus,Arn:ConnectionArn}' \
--output table
古いCLIや既存リソースでは codestar-connections という古い名前空間が残っていることがあります。これから作るものは codeconnections を使う、と決めておくと、名前が混在しても混乱せずに済みます。
5. CodeBuildの基礎
CodeBuildの説明に入る前に、「ビルド」という言葉そのものをはっきりさせておきます。CI/CDの話が途中でわからなくなるのは、たいていここを素通りしてしまうのが原因だからです。
そもそも論、「ビルド」とは何でしょうか。 ひとことで言うと、「人間が書いたソースコードを、サーバー上で実際に動かせる形へ整える作業」のことです。
私たちが書いたソースコードは、そのままではコンピュータがすぐに動かせないことがほとんどです。
- JavaやGo、TypeScriptのような言語は、人間が読める文字のままでは実行できず、機械が動かせる形へ「変換(コンパイル)」する必要がある
- PythonやRubyのように変換がいらない言語でも、動かすには外部ライブラリ(
requirements.txtなどに書いた依存関係)を先にインストールしておく必要がある - どの言語であっても、本番に出す前に「テストがすべて通るか」は確認しておきたい
この「変換」「依存ライブラリのインストール」「テスト」「最終的に配布する形へのまとめ(パッケージング)」を、ひとまとめにやってしまうのがビルドです。たまに聞く例として料理にたとえるなら、買ってきた材料(ソースコード)を、下ごしらえして(依存解決)、火を通して(コンパイル)、味見して(テスト)、お皿に盛り付ける(パッケージング)まで、という一連の流れだと思うとイメージしやすいはずです。
そして、このビルド作業をAWS上で肩代わりしてくれるのがCodeBuildです。自分のPCではなく、AWSが用意したまっさらなサーバー(コンテナ)の上で、毎回まったく同じ手順でビルドを実行してくれます。「自分のPCでは動いたのに本番では動かない」という事故が起きにくいのは、誰か個人の環境ではなく、こうした共通のきれいな環境で毎回ビルドし直すからです。
5-1. CodeBuildとは
AWS CodeBuildは、ビルドサーバーを自前で管理しなくても、ビルド、テスト、パッケージングを実行できるフルマネージドサービスです。
| 特徴 | 内容 |
|---|---|
| サーバー管理 | 不要 |
| スケール | ビルド要求に応じて自動スケール |
| 実行環境 | 隔離されたビルド環境で実行 |
| 設定 |
buildspec.yml で定義 |
| 成果物 | S3、ECR、CodePipelineへ出力 |
| ログ | CloudWatch Logs |
基本的な流れは、ソースを取得して、実行環境(コンテナ)を起動し、install → pre_build → build → post_build の順に処理を進め、できあがった成果物(アーティファクト)を出力する、というものです。install から post_build までの各段階に何を書くのかは、次の章のbuildspec.ymlでくわしく説明します。個人PCではなく、CodeBuild側の隔離された環境で毎回同じ手順を実行できるのがポイントです。
5-2. コンピューティングタイプ
| 種類 | 特徴 | 向いている用途 |
|---|---|---|
| EC2オンデマンド | 分単位課金、Dockerビルドや長時間ビルドに対応 | 一般的なビルド |
| Lambdaオンデマンド | 秒単位課金、最大実行時間が短い | 軽量ビルド、短時間テスト |
| EC2リザーブドキャパシティ | 専用フリートを確保 | 大量ビルド、安定した待ち時間が必要な環境 |
Dockerイメージをビルドする場合は、基本的にEC2コンピューティングを選びます。特権モードが必要かどうかは構成次第で、VPC内のビルドでDockerコンテナを使う場合や、Dockerレイヤーキャッシュを使う場合は有効化が必要になります。Lambdaコンピューティングは軽量ですが、特権モードやDocker in Dockerのような用途には向きません。
5-3. 料金の考え方
2026年6月1日時点の公式料金では、CodeBuildは従量課金です。EC2オンデマンドは分単位、Lambdaオンデマンドは秒単位で計算されます。
| 項目 | 無料枠 |
|---|---|
| EC2オンデマンド |
general1.small または arm1.small で月100分 |
| Lambdaオンデマンド |
lambda.arm.1GB または lambda.x86-64.1GB で月6,000秒 |
ハンズオンで general1.small を使う分には、数回の検証くらいなら無料枠の中に収まることが多いです。ただし、CloudWatch Logs、S3、KMS、ECRといった周辺サービスには別途料金がかかることがあるので、そこは頭に入れておきましょう。
5-4. はじめてのCodeBuildプロジェクト
最小構成では、Pythonアプリ、テスト、buildspec.yml を用意します。
ただし、buildspec.yml は次の第6章でまとめて説明するため、ここでは先に「ビルド対象になるアプリ」と「実行したいテスト」だけを用意します。CodeBuildは、このようなファイル群をリポジトリから取得し、後で作る buildspec.yml に従ってコマンドを実行します。
def hello():
return "Hello, AWS CodeBuild!"
def add(a, b):
return a + b
if __name__ == "__main__":
print(hello())
print(f"1 + 2 = {add(1, 2)}")
from app import add, hello
def test_hello():
assert hello() == "Hello, AWS CodeBuild!"
def test_add():
assert add(1, 2) == 3
assert add(-1, 1) == 0
pytest
CodeBuildプロジェクトでは、ソースプロバイダ、GitHub接続、ブランチ、実行環境、サービスロール、Buildspec、アーティファクト、ログ出力を設定します。
5-5. 初回ビルドとログ確認
プロジェクト作成後は、まず手動でビルドを開始します。最初の目的は、アプリケーションが正しく動くことよりも、CodeBuildがソースを取得できるか、buildspec.yml を読めるか、ログをCloudWatch Logsへ出せるかを確認することです。
まだ buildspec.yml を置いていない場合は、第6章のサンプルを追加してから実行してください。CodeBuildは、ソースだけあっても「何のコマンドを実行するか」が分からないため、Buildspecの指定が必要になります。
# ビルドを開始
aws codebuild start-build \
--project-name code-series-demo-build
# 最新ビルドIDを取得
BUILD_ID=$(aws codebuild list-builds-for-project \
--project-name code-series-demo-build \
--query 'ids[0]' \
--output text)
# ビルド状態を確認
aws codebuild batch-get-builds \
--ids "$BUILD_ID" \
--query 'builds[0].{Status:buildStatus,Start:startTime,End:endTime}'
失敗したときは、CodeBuildコンソールのビルドログと、CloudWatch Logsを確認します。エラーの種類ごとに確認先はだいたい決まっていて、DOWNLOAD_SOURCE_FAILED なら接続やリポジトリの権限、CLIENT_ERROR ならbuildspecの書き方(構文)、UPLOAD_ARTIFACTS_FAILED ならS3の権限、というふうに当たりをつけていきます。
6. buildspec.ymlの実践
6-1. 基本構造
buildspec.yml は、CodeBuildが実行する処理を定義するファイルです。リポジトリのルートに置くのが基本です。
まずは、第5章で作ったPythonアプリをpytestでテストする最小構成から見ます。Pythonはコンパイルが不要なので、この例では「依存関係を入れる」「pytestを実行する」「成果物としてファイルを渡す」という流れになります。
version: 0.2
env:
variables:
ENVIRONMENT: "dev"
phases:
install:
runtime-versions:
python: 3.12
commands:
- echo "依存関係をインストールします"
- pip install -r requirements.txt
pre_build:
commands:
- echo "テストを実行します"
- pytest -v
finally:
- echo "pre_buildの終了処理です"
build:
commands:
- echo "Pythonアプリではコンパイルせず、ここでは構文確認だけ行います"
- python -m py_compile app.py
- python app.py
post_build:
commands:
- echo "ビルドが完了しました"
artifacts:
files:
- '**/*'
Pythonの場合、build フェーズで必ず何かをコンパイルしなければいけないわけではありません。テストだけで十分なケースもあります。ただ、CI/CDの流れを見せるために、ここでは py_compile で簡単な構文確認を入れています。
コンパイルや成果物生成がある言語だと、build フェーズの役割がもう少し分かりやすくなります。たとえばTypeScriptなら、依存関係を入れて、テストして、npm run build で dist/ を作り、それを成果物として次の工程へ渡します。
version: 0.2
phases:
install:
runtime-versions:
nodejs: 22
commands:
- echo "依存関係をインストールします"
- npm ci
pre_build:
commands:
- echo "テストを実行します"
- npm test
build:
commands:
- echo "TypeScriptをビルドします"
- npm run build
post_build:
commands:
- echo "ビルド成果物を確認します"
- ls -la dist
artifacts:
base-directory: dist
files:
- '**/*'
このように、Pythonでは「テスト中心」、TypeScriptやJava、Goでは「コンパイルやパッケージング中心」になることが多いです。言語によって中身は変わりますが、CodeBuild側では、どちらも buildspec.yml に書かれたコマンドを順番に実行しているだけです。
このファイルは上から順に読まれます。初見では記号が多くて身構えてしまうかもしれませんが、大きく4つのブロックに分かれているだけだと思えば怖くありません。
version
buildspecの書式バージョンです。今は 0.2 を指定します。おまじないのようなものなので、まずはこのまま書いておけば大丈夫です。
env
ビルド中に使う環境変数を定義する場所です。上の例では ENVIRONMENT に dev という値を入れています。APIキーやパスワードのような秘密の値は、ここに直接書かず、後述の安全な方法で渡します。
phases(ここが本体)
実際に動かすコマンドを、4つの段階(フェーズ)に分けて書く場所です。上から順番に実行され、途中のコマンドが1つでも失敗すると、その時点でビルドは止まります。「いつ動くか」と「どこに何を書くか」の目安は次のとおりです。
| フェーズ | いつ実行されるか | よく書く内容 |
|---|---|---|
install |
最初。実行環境を整える段階 | 言語ランタイムの指定、ツールや依存ライブラリのインストール |
pre_build |
ビルドに入る直前の準備 | テストの実行、ECRへのログイン、変数の組み立て |
build |
メインの処理 | コンパイル、Dockerイメージのビルドなど中心的な作業 |
post_build |
ビルドが終わったあとの仕上げ | 成果物のアップロード、イメージのpush、完了ログ出力 |
この4分割は「絶対にこう書かなければいけない」という厳密な決まりではなく、読みやすさのための目安です。極端に言えば、全部 build に書いても動くには動きます。それでも、install で準備、pre_build でテスト、build で本処理、post_build で後片付け、と役割ごとに分けておくと、失敗したときにどの段階でコケたのかがログから一目でわかります。なお、install の中の runtime-versions に python: 3.12 のように書くと、指定したバージョンの言語をCodeBuild側が用意してくれます。
artifacts
ビルドが終わったあと、どのファイルを成果物(アーティファクト)として次の工程(S3やCodePipeline)へ渡すかを指定します。files: の '**/*' は「すべてのファイルを渡す」という意味です。ここで渡したものが、あとでCodeDeployがデプロイする中身になります。
最後の finally は、同じフェーズの commands が途中で失敗しても必ず実行されるブロックです。通常コマンドが失敗するとそのフェーズは失敗扱いになりますが、finally は実行されます。ログの収集や一時ファイルの片付けなど、「成功しても失敗してもやっておきたいこと」を書くのに向いています。
6-2. 環境変数とシークレット
buildspec.yml では、ビルド中に使う値を環境変数として渡せます。ただし、何でも env.variables に書けばよいわけではありません。
たとえば、ENVIRONMENT=dev や API_BASE_URL=https://example.com のように、見えても困らない値は平文の環境変数で構いません。一方、DBパスワード、外部APIキー、トークンのような値は、buildspecやGitリポジトリに直接書かず、Systems Manager Parameter StoreやSecrets Managerから取得します。ちなみに、Parameter Storeなどの機能があるSystems ManagerはよくSSMと呼ばれます。これはかつてSystems ManagerがSimple Systems Managerという名前だった時の略称に由来するそうです。
| 種類 | 書き方 | 用途 |
|---|---|---|
| 平文環境変数 | env.variables |
環境名、非機密設定 |
| Parameter Store | env.parameter-store |
設定値、比較的単純な秘密情報 |
| Secrets Manager | env.secrets-manager |
DBパスワード、外部サービス認証情報 |
| exported variables | env.exported-variables |
CodePipeline後続アクションへ渡す値 |
env:
variables:
ENVIRONMENT: "production"
parameter-store:
API_ENDPOINT: "/app/prod/api-endpoint"
secrets-manager:
DB_PASSWORD: "prod/db:password"
GITHUB_TOKEN: "prod/github-token:token"
exported-variables:
- IMAGE_TAG
- BUILD_VERSION
Secrets Managerの指定は、ざっくり言うと シークレット名:JSONキー です。上の例なら、Secrets Managerに prod/db というシークレットがあり、その中の password というキーを DB_PASSWORD としてCodeBuild内で使えるようにしています。
ビルド中は、普通の環境変数と同じように参照できます。
phases:
pre_build:
commands:
- echo "DB接続確認を行います"
- python scripts/check_db.py
ここで大事なのは、echo $DB_PASSWORD のように秘密情報をログへ出さないことです。CodeBuildのログはCloudWatch Logsに残るため、秘密情報を標準出力へ出してしまうと、ログ側の閲覧権限を持つ人にも見えてしまいます。
また、CodeBuildサービスロールには、値を読むための権限が必要です。Parameter Storeなら ssm:GetParameters、Secrets Managerなら secretsmanager:GetSecretValue、カスタマー管理KMSキーで暗号化している場合は kms:Decrypt も必要になります。
6-3. 組み込み環境変数
CodeBuildは、ビルドごとに便利な環境変数を自動で用意してくれます。これを使うと、手でバージョン番号を入れなくても、「どのコミットから作られた成果物か」を追跡できます。
| 変数 | 内容 |
|---|---|
CODEBUILD_BUILD_ID |
ビルドの一意ID |
CODEBUILD_BUILD_NUMBER |
ビルド番号 |
CODEBUILD_SOURCE_VERSION |
指定されたソースバージョン |
CODEBUILD_RESOLVED_SOURCE_VERSION |
解決後のコミットハッシュ |
CODEBUILD_SRC_DIR |
ソースコードのディレクトリ |
CODEBUILD_WEBHOOK_HEAD_REF |
Webhookのブランチ参照 |
よく使うのは CODEBUILD_RESOLVED_SOURCE_VERSION です。GitHubやCodeCommitから渡されたコミットIDが入るため、DockerイメージのタグやZIPファイル名に入れておくと、障害調査のときに「この本番環境はどのコミットから作られたのか」をたどりやすくなります。
phases:
pre_build:
commands:
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG="${ENVIRONMENT}-${COMMIT_HASH}"
- echo "IMAGE_TAG=$IMAGE_TAG"
build:
commands:
- echo "このビルドのタグは $IMAGE_TAG です"
CodePipelineの後続アクションへ値を渡したい場合は、6-2で触れた exported-variables に変数名を入れます。ECSへ渡すイメージタグ、CloudFormationへ渡すテンプレートのパラメータ、通知に載せるバージョンなどに使えます。
6-4. キャッシュ
毎回ゼロから依存関係をダウンロードすると、ビルド時間が伸びます。npm、pip、Maven、Gradleのようなパッケージ管理ツールを使う場合は、キャッシュを使うと体感がかなり変わります。
| 種類 | 特徴 | 注意点 |
|---|---|---|
| S3キャッシュ | プロジェクト間で使いやすい | S3アクセスの分だけ遅延がある |
| ローカルキャッシュ | 同一ホストなら高速 | ホストが変わると効かない |
ローカルキャッシュには、ソースキャッシュ、Dockerレイヤーキャッシュ、カスタムキャッシュがあります。Dockerレイヤーキャッシュを使う場合はLinux環境と特権モードが必要です。
buildspec側では、キャッシュしたいディレクトリを cache.paths に書きます。
cache:
paths:
- '/root/.cache/pip/**/*'
- 'node_modules/**/*'
- '/root/.m2/**/*'
キャッシュは早くするためのものであって、正しさを保証するものではありません。依存関係のバージョンを変えたのに古いキャッシュが使われると、原因が見えにくい不具合になります。package-lock.json、requirements.txt、pom.xml などのロックファイルをきちんと管理し、必要に応じてキャッシュを削除して切り分ける運用も用意しておくと安心です。
初心者の方向けですが、まずはキャッシュなしで動かし、ビルドが安定してからキャッシュを入れるのが安全です。最初から高速化を狙うより、まずは「毎回同じ結果になる」状態を作るほうが大事です。
6-5. DockerイメージのビルドとECRプッシュ
ECSやEKSへデプロイする場合は、CodeBuildでDockerイメージを作り、ECRへpushする構成がよく使われます。非VPCビルドではDockerデーモンが有効なマネージドイメージを使えますが、VPC内のビルドでDockerコンテナを使う場合やDockerレイヤーキャッシュを使う場合は、特権モードを有効にします。特権モードは強い権限なので、必要なプロジェクトだけで使うのが安全です。
version: 0.2
env:
variables:
AWS_ACCOUNT_ID: "123456789012"
ECR_REPOSITORY_NAME: "my-app"
phases:
pre_build:
commands:
- echo "ECRへログインします"
- ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
- ECR_REPOSITORY_URI="${ECR_REGISTRY}/${ECR_REPOSITORY_NAME}"
- aws ecr get-login-password --region "$AWS_DEFAULT_REGION" | docker login --username AWS --password-stdin "$ECR_REGISTRY"
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
build:
commands:
- echo "Dockerイメージをビルドします"
- docker build -t "$ECR_REPOSITORY_URI:$IMAGE_TAG" .
- docker tag "$ECR_REPOSITORY_URI:$IMAGE_TAG" "$ECR_REPOSITORY_URI:latest"
post_build:
commands:
- echo "Dockerイメージをプッシュします"
- docker push "$ECR_REPOSITORY_URI:$IMAGE_TAG"
- docker push "$ECR_REPOSITORY_URI:latest"
- printf '[{"name":"app","imageUri":"%s"}]' "$ECR_REPOSITORY_URI:$IMAGE_TAG" > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
docker login の接続先はリポジトリ名を含まないレジストリホスト(ECR_REGISTRY)で、イメージのタグやpush先はリポジトリ名まで含むURI(ECR_REPOSITORY_URI)です。両者を同じ変数にすると、ログインやpushに失敗します。
ここで作っている imagedefinitions.json は、CodePipelineのECS標準デプロイアクションで使うファイルです。コンテナ名とイメージURIをJSONで渡すことで、後続のデプロイアクションが「どのイメージへ更新すればよいか」を判断できます。
一方、CodeDeployを使うECS Blue/Greenデプロイでは、imagedefinitions.json ではなく imageDetail.json を使います。さらに、タスク定義テンプレートとECS用のAppSpecファイルを組み合わせるため、標準デプロイとは入力ファイルが変わります。
ECRへpushするには、CodeBuildサービスロールにECR権限が必要です。認証トークン取得は Resource: "*" が必要ですが、イメージレイヤーのアップロードやイメージ登録は対象リポジトリARNへ絞れます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRAuthorizationToken",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "ECRPushToRepository",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "arn:aws:ecr:ap-northeast-1:123456789012:repository/my-app"
}
]
}
本番運用では、latest だけに頼らず、コミットハッシュやビルド番号をタグに含めると良いです。latest は人間には分かりやすいですが、あとから「このlatestはどのコードだったのか」を追いにくくなるためです。
6-6. バッチビルドとレポート
バッチビルドは、複数のビルドをまとめて実行する機能です。たとえば、Node.js 20、22など複数バージョンで同じテストを走らせたり、先に共通ビルドを実行してから、単体テストと結合テストを並列に流したりできます。
| 機能 | 例 |
|---|---|
| ビルドマトリクス | Node.js 20、22を並列テスト |
| ビルドグラフ | build後にunit testとintegration testを並列実行 |
| レポート | JUnit XML、Cucumber JSON、TRX、TestNG、NUnit |
ただし、初心者が最初からバッチビルドを使う必要はありません。通常のビルドが安定し、テスト時間が長くなってから検討すれば十分です。
一方、レポートは早めに入れておく価値があります。CodeBuildはJUnit XMLなどのテスト結果を読み取り、コンソール上で成功数、失敗数、スキップ数を見られるようにできます。
pytestなら、次のようにJUnit XMLを出力します。
phases:
pre_build:
commands:
- mkdir -p reports
- pytest -v --junitxml=reports/test-results.xml
reports:
pytest-reports:
files:
- 'test-results.xml'
base-directory: 'reports'
file-format: JUNITXML
テストが失敗したとき、ログを上から全部読むのは大変です。レポートを出しておけば、「どのテストが、何件失敗したか」を先に見られます。チームで運用するなら、ログだけよりもずっと調査しやすくなります。
6-7. Webhookによる自動ビルド
CodeBuild単体でもWebhookで自動ビルドできます。イベントはpush、pull request作成、pull request更新などです。ブランチやファイルパスでフィルタすると、不要なビルドを減らせます。
| フィルタ | CodeBuild Webhookの例(正規表現) |
|---|---|
| ブランチ | ^refs/heads/main$ |
| Pull Request |
PULL_REQUEST_CREATED、PULL_REQUEST_UPDATED
|
| ファイルパス |
^src/.*(docs/ 配下を除外するなら ^docs/.* を除外指定) |
CodeBuild Webhookのフィルターパターンは、glob(ワイルドカード)ではなく正規表現として評価されます。CodePipeline V2のファイルパストリガーで使う src/** のような記法とは別物なので、^src/.* のように正規表現で書きます。
CodeBuild単体のWebhookは、「pushされたらテストだけ回したい」「Pull Request時に静的解析だけしたい」という用途に向いています。
一方、Source → Build → Deployまでつなげるなら、CodePipeline側のトリガーに寄せたほうが流れが分かりやすくなります。CodeBuildはビルドとテストに集中させ、起動条件や後続ステージの制御はCodePipelineに任せる、という分担ですね。
| 使い方 | 向いている用途 |
|---|---|
| CodeBuild単体Webhook | PRのテスト、軽い静的解析、単発のビルド |
| CodePipelineトリガー | ビルド後に承認、デプロイ、通知までつなげる |
この記事の後半では、CodePipelineを中心に扱います。CodeBuildのWebhookは便利ですが、本番反映まで考えるなら、パイプライン全体でどのブランチ・どのパス変更を起点にするかを設計したほうが安全です。
7. CodeDeployの基礎
ビルドで成果物ができあがったら、次はそれを実際のサーバーへ配って動かす番です。この「できあがったアプリを、本番やステージングのサーバーに配置して、動く状態にする作業」をデプロイと呼びます。手作業でやろうとすると、サーバーにログインして、古いファイルを消して、新しいファイルをコピーして、サービスを再起動する、といった手順が多く、どこかで間違えやすい作業です。これを毎回同じ手順で自動的にこなしてくれるのがCodeDeployです。
7-1. CodeDeployとは
AWS CodeDeployは、アプリケーションをEC2、オンプレミス、ECS、Lambdaへ自動でデプロイしてくれるサービスです。
| デプロイ先 | Agent | 主な戦略 |
|---|---|---|
| EC2、オンプレミス | 必要 | In-Place、Blue/Green |
| ECS | 不要 | Blue/Green。切替方式としてCanary、Linear、AllAtOnceを選べる |
| Lambda | 不要 | Canary、Linear、AllAtOnce |
EC2やオンプレミスへデプロイする場合は、各サーバーに「CodeDeploy Agent」という小さな常駐プログラムを入れておきます。このAgentがサーバー側で待ち構えていて、デプロイの指示が来ると appspec.yml(次に説明します)の内容に従って、ファイルの配置やスクリプトの実行を代わりにこなしてくれます。一方、ECSやLambdaへのデプロイではこのAgentは不要で、AWSがマネージドな仕組みで切り替えを担当します。
7-2. 主要コンポーネント
| コンポーネント | 説明 |
|---|---|
| アプリケーション | デプロイ対象の管理単位 |
| デプロイグループ | 対象インスタンスやECSサービスなどの集合 |
| デプロイ設定 | 一度に何台、何割へ反映するかを決める |
| リビジョン | デプロイするアプリケーションの成果物 |
| appspec.yml | ファイル配置、Hook、対象リソースを定義 |
| CodeDeploy Agent | EC2、オンプレミスで実行されるエージェント |
7-3. EC2用appspec.yml
version: 0.0
os: linux
files:
- source: /web
destination: /var/www/html
permissions:
- object: /var/www/html
owner: apache
group: apache
mode: 755
type:
- directory
hooks:
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 120
runas: root
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 120
runas: root
AfterInstall:
- location: scripts/after_install.sh
timeout: 120
runas: root
ApplicationStart:
- location: scripts/start_server.sh
timeout: 120
runas: root
ValidateService:
- location: scripts/validate_service.sh
timeout: 120
runas: root
appspec.ymlは、いわばbuildspec.ymlの「デプロイ版」です。「どのファイルをどこへ置くか」「いつ、どのスクリプトを動かすか」をCodeDeployに伝えます。上の例は、大きく3つのブロックでできています。
-
filesは、成果物のどのファイルをサーバーのどこへコピーするかを指定します。ここでは公開用ファイルだけをまとめたwebディレクトリを/var/www/htmlへ配置し、appspec.ymlやスクリプトのような非公開ファイルを公開領域へ出さないようにしています(source: /にすると、リビジョン内の全ファイルが公開ディレクトリへコピーされてしまいます)。 -
permissionsは、コピーしたファイルの所有者やアクセス権限を指定します。 -
hooksは、デプロイの各タイミングで実行するスクリプトを割り当てます。
ここで出てくる「Hook(フック)」がポイントです。Hookとは、ざっくり言えば「デプロイのこの瞬間に、このスクリプトを差し込んで実行してね」という予約のようなものです。たとえば ApplicationStop のHookには「古いサーバーを止めるスクリプト」、ApplicationStart のHookには「新しいサーバーを起動するスクリプト」、ValidateService のHookには「ちゃんと起動できたかを確認するスクリプト」を割り当てます。CodeDeployが決まった順番でこれらのHookを呼び出してくれるので、私たちは「このタイミングで何をしたいか」をスクリプトとして書いて、置き場所を指定しておくだけで済みます。
たとえばApacheへ静的サイトを配置するだけなら、Hookスクリプトは次のような最小構成で動かせます。
#!/bin/bash
# ApplicationStop: 2回目以降のデプロイで、前回リビジョン側のスクリプトとして実行される
if systemctl is-active --quiet httpd; then
systemctl stop httpd
fi
exit 0
#!/bin/bash
# BeforeInstall: Apacheが入っていなければインストールする
if ! rpm -q httpd >/dev/null 2>&1; then
dnf install -y httpd
fi
#!/bin/bash
# AfterInstall: 配置後の権限を整える
chown -R apache:apache /var/www/html
chmod -R 755 /var/www/html
#!/bin/bash
# ApplicationStart: Apacheを起動する
systemctl start httpd
systemctl enable httpd
#!/bin/bash
# ValidateService: ローカルでHTTP 200が返るか確認する
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/)
if [ "$HTTP_CODE" = "200" ]; then
echo "Validation passed"
exit 0
fi
echo "Validation failed: HTTP $HTTP_CODE"
exit 1
この例では静的サイトなのでシンプルですが、実際のアプリでは BeforeInstall でバックアップ、AfterInstall で設定ファイルの差し替え、ApplicationStart でサービス起動、ValidateService でヘルスチェック、という形にすると運用しやすくなります。
7-4. ライフサイクルイベント
In-Placeデプロイでは、次の順番で処理されます。
ApplicationStop
DownloadBundle
BeforeInstall
Install
AfterInstall
ApplicationStart
ValidateService
この一連の決まった流れを「ライフサイクルイベント」と呼びます。上から順に、次のように進みます。
-
ApplicationStopで、今動いているアプリを止める -
DownloadBundleで、新しい成果物をサーバーへ取り込む -
BeforeInstallで、ファイルを置く前の準備(バックアップなど)を行う -
Installで、成果物のファイルを所定の場所へ配置する -
AfterInstallで、置いたあとの調整(権限変更や設定ファイルの書き換えなど)を行う -
ApplicationStartで、新しいアプリを起動する -
ValidateServiceで、きちんと動いているかを最終確認する
このうち DownloadBundle と Install の2つはCodeDeployが自動でやってくれるので、私たちが中身を書く必要はありません。それ以外のイベントに対して、appspec.ymlのHookでスクリプトを割り当てていくことになります。
なお、ApplicationStop は少し特殊です。初回デプロイでは、まだ前回の正常リビジョンがないため実行されません。2回目以降は、今回のリビジョンではなく、前回正常にデプロイされたリビジョンのAppSpecとスクリプトが使われます。
ここで、第6章のbuildspec.ymlと混同しないようにまとめておきます。buildspecの install や pre_build は ビルドの段階 (CodeBuildが、まだサーバーへ配る前のソースをビルドするとき)に動くものです。一方、appspecの BeforeInstall や AfterInstall は デプロイの段階 (CodeDeployが、できあがった成果物をサーバーへ配置するとき)に動くものです。名前は似ていますが、動く場所もタイミングもまったくの別物です。「これはビルド側の話か、デプロイ側の話か」を意識して読むと、混乱せずに済みます。
8. EC2へのデプロイ
ここからは、CodeDeployを使ってEC2へ実際に反映する流れをもう少し具体的に見ます。EC2デプロイで大事なのは、「CodeDeployがどのEC2を対象にするか」と「EC2が成果物を取りに行けるか」です。
CodeDeployは、EC2の中へ魔法のように直接ファイルを置くわけではありません。CodeDeploy AgentがEC2上で動き、S3などから成果物を取得し、appspec.yml に従って配置やスクリプト実行を行います。つまり、EC2側にも準備が必要です。
8-1. EC2側の準備
EC2にCodeDeployするには、インスタンスロール、CodeDeployサービスロール、タグ、CodeDeploy Agentが必要です。
| 項目 | 内容 |
|---|---|
| EC2インスタンスロール | S3からリビジョンを取得する権限、SSM権限 |
| CodeDeployサービスロール | EC2タグ、Auto Scaling、ELBなどを参照する権限 |
| タグ | デプロイ対象を絞り込む。環境ごとに分けると運用しやすい |
| Agent | EC2上でデプロイ処理を実行 |
ここで「ロール」が2種類出てくるのが、最初に戸惑いやすいところです。違いは単純で、「誰に与える権限か」が異なるだけです。関係無いですが、AWSのSCS試験ではこの辺りの切り分けが肝です。どのサービスがどのサービスにアクセスするのか、という関係図をイメージしましょう。
- EC2インスタンスロール は、サーバー(EC2)そのものに与える権限です。EC2がS3から成果物を取ってくるときに使います。
- CodeDeployサービスロール は、CodeDeployというサービスに与える権限です。CodeDeployがEC2のタグを調べたり、ロードバランサーやAuto Scalingを操作したりするときに使います。
「サーバー側の権限」と「デプロイ役の権限」を別々に用意するんだな、と押さえておけば混乱しません。
もう1つ大事なのがタグです。CodeDeployのデプロイグループでは、タグを使って「どのEC2へ配るか」を指定できます。検証環境と本番環境を同じアカウントに置くなら、次のように環境タグを分けておくと、誤配布を防ぎやすくなります。以下は、4環境で開発を行っている場合の例です。devは普段使いの開発環境、itstは結合テスト(IT)、システムテスト(ST)を行う環境、stgは本番環境デプロイに限りなく近いリハーサルを行う環境、prdは本番環境、という想定です。
| 環境 | EC2タグ例 | デプロイグループ例 |
|---|---|---|
| dev |
App=sample-web、Env=dev
|
sample-web-dev-dg |
| itst |
App=sample-web、Env=itst
|
sample-web-itst-dg |
| stg |
App=sample-web、Env=stg
|
sample-web-stg-dg |
| prd |
App=sample-web、Env=prd
|
sample-web-prd-dg |
タグの値が1文字でも違うと、CodeDeployは対象インスタンスを見つけられません。最初は Name タグだけで進めてもよいですが、実務では App と Env を分けるほうが、環境を増やしたときに管理しやすくなります。
なお、Amazon Linux 2023を使うなら、AMI IDを直接書かずにSSM Parameter Storeから最新版を取り出して起動すると、古いAMIに固定されてしまう心配がありません。
AMI_ID=$(aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query 'Parameters[0].Value' \
--output text)
8-2. CodeDeploy Agent
EC2へデプロイする場合、CodeDeploy Agentは必須です。Agentが入っていない、止まっている、S3を読めない、という状態だと、CodeDeploy側ではデプロイを開始しているように見えても、EC2側で処理が進みません。
# インストーラーを取得
cd /tmp
wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install
# インストール
chmod +x ./install
sudo ./install auto
# 状態確認
sudo systemctl status codedeploy-agent
sudo codedeploy-agent --version
上のURLは東京リージョン(ap-northeast-1)用です。別リージョンで作る場合は、リージョン名を合わせます。リージョン違いのインストーラーを使うと、思ったように動かない場合があります。
Agentが停止していると、デプロイは進みません。失敗時は /var/log/aws/codedeploy-agent/ と /opt/codedeploy-agent/deployment-root/ を確認します。前者にはAgent自体のログ、後者にはデプロイごとの展開ディレクトリやHook実行ログが残ります。
8-3. デプロイ用ファイル構成
aws-code-series-demo/
├── appspec.yml
├── index.html
└── scripts/
├── stop_server.sh
├── install_dependencies.sh
├── after_install.sh
├── start_server.sh
└── validate_service.sh
#!/bin/bash
echo "ValidateServiceを実行します"
sleep 5
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/)
if [ "$HTTP_CODE" = "200" ]; then
echo "Validation passed"
exit 0
else
echo "Validation failed"
exit 1
fi
ValidateService のスクリプトは、少し面倒でも必ず用意しておくことをおすすめします。ここでアプリの異常をしっかり検知できれば、「デプロイ自体は終わったのに、実はアプリが壊れていた」という最悪の事態を防げます。検証で失敗を返せば、CodeDeployがそのデプロイを失敗とみなし、設定しておいた自動ロールバックへつなげてくれるからです。
8-4. デプロイグループ
EC2デプロイグループでは、対象インスタンスをタグやAuto Scaling Groupで指定します。
| 設定 | 例 |
|---|---|
| アプリケーション名 | code-series-demo-app |
| デプロイグループ名 | code-series-demo-dg |
| デプロイタイプ | In-Place |
| 対象 | EC2タグ Name=CodeDeploy-Demo
|
| デプロイ設定 | CodeDeployDefault.AllAtOnce |
| ロールバック | デプロイ失敗時に有効 |
本番環境では、全台を一度に入れ替えるAllAtOnceは避けて、1台ずつ入れ替えるOneAtATimeや、後ほど説明するBlue/Greenを検討します。全台を同時に更新してしまうと、もし新しいバージョンが壊れていたとき、すべてのユーザーに一斉に影響が出てしまうからです。
複数環境を持つ場合は、環境ごとにデプロイグループを分けます。dev、itst、stg、prdの4環境なら、同じアプリケーション名の下に4つのデプロイグループを作るイメージです。
CodeDeploy Application: sample-web
├── sample-web-dev-dg -> Env=dev のEC2
├── sample-web-itst-dg -> Env=itst のEC2
├── sample-web-stg-dg -> Env=stg のEC2
└── sample-web-prd-dg -> Env=prd のEC2
こうしておくと、CodePipeline側で「developブランチはdevへ」「releaseブランチはitstやstgへ」「mainブランチはprdへ」のように、後続のDeployステージを分けやすくなります。ブランチと環境の対応は、11章と12章で改めてまとめます。
8-5. デプロイの実行
CodeDeployアプリケーションとデプロイグループを作ったら、リビジョンを指定してデプロイを開始します。GitHubを直接リビジョンにする場合は、リポジトリ名とコミットIDを指定します。CodePipeline経由の場合は、CodePipelineがS3アーティファクトをCodeDeployへ渡します。
# 最新コミットIDを確認
git rev-parse HEAD
デプロイ中は、ApplicationStop、DownloadBundle、BeforeInstall、Install、AfterInstall、ApplicationStart、ValidateServiceの進行状況を確認します。ValidateServiceまで成功すれば、ブラウザでEC2のパブリックIPやALBのDNS名へアクセスして動作確認します。
http://<EC2のパブリックIP>/
更新デプロイを試す場合は、index.html のバージョン表記を変えてpushし、再度デプロイします。失敗時の挙動を確認するなら、validate_service.sh を一時的に失敗させ、ロールバック設定が期待どおり動くか確認します。
本番運用では、手動で create-deployment を実行するより、CodePipelineからCodeDeployを呼ぶ形に寄せます。手動実行だと「誰が、どの成果物を、どの環境へ出したか」が分かりにくくなりますが、CodePipeline経由ならSource、Build、Deployの履歴がひとつながりで残ります。
9. デプロイ戦略とロールバック
9-1. In-PlaceとBlue/Green
デプロイのやり方には、大きく分けて2つの考え方があります。お店の改装にたとえると、イメージがつかめると思います。
- In-Place(インプレース) は、今営業しているお店を、開けたまま中身だけ入れ替えるやり方です。同じサーバーの上で、アプリを新しいバージョンへ置き換えます。手軽でコストも低い反面、入れ替えの最中は一時的にサービスが止まったり不安定になったりすることがあります。
- Blue/Green(ブルーグリーン) は、今のお店(Blue)はそのまま営業させておき、すぐ隣にまったく新しいお店(Green)を建てて準備するやり方です。新しいお店の準備が整い、動作確認も済んだら、お客さんの入り口(トラフィック)を一気に新しいお店へ切り替えます。サーバーを一時的に2セット用意するためコストはかかりますが、切り替えはなめらかで、もし新しいお店に問題が見つかっても、入り口を元のお店へ戻すだけですぐにやり直せます。
この違いを表にまとめると、次のようになります。
| 観点 | In-Place | Blue/Green |
|---|---|---|
| 方式 | 既存環境を直接更新 | 新環境を作ってトラフィック切替 |
| ダウンタイム | 発生しやすい | ほぼ避けられる |
| コスト | 低い | 一時的に高い |
| ロールバック | 再デプロイが必要 | トラフィック切り戻しが可能 |
| 切替前検証 | 難しい | 置換環境を検証してから切替可能 |
| 向いている用途 | 開発環境、小規模 | 本番、影響を抑えたい環境 |
EC2でBlue/Greenを実現するには、入り口を切り替える「ロードバランサー」と、新旧のEC2をどう用意するかがカギになります。置換環境は、既存のAuto Scaling GroupをCodeDeployにコピーさせる方法もあれば、自分で作ったEC2を手動で指定する方法もあります。次の項でもう少しくわしく見ていきます。
9-2. ロードバランサー連携
CodeDeployのBlue/Greenデプロイでは、ロードバランサーが旧環境と新環境のトラフィック切替を担当します。EC2/オンプレミスのBlue/Greenでは、Classic Load Balancer、Application Load Balancerのターゲットグループ、Network Load Balancerのターゲットグループを指定できます。
| 要素 | 役割 |
|---|---|
| ロードバランサー | ユーザーからのトラフィックを受け、旧環境から新環境へ向き先を変える |
| 本番リスナー | 通常アクセスを受ける |
| ターゲットグループ | Blue環境、Green環境の登録先 |
| Auto Scaling Group | 自動コピー方式でGreen環境を作る場合に使う |
| 手動指定インスタンス | 自分で用意したGreen環境を指定する場合に使う |
EC2 Blue/Greenの流れは、Green環境作成、Green環境へのデプロイと検証、ロードバランサーの向き先切替、Blue環境を一定時間残す、という順番です。
Blue環境
現行バージョンが本番トラフィックを受ける
Green環境
新バージョンを配置し、切替前に検証する
切替
ロードバランサーの向き先をGreenへ変える
保持
Blue環境を一定時間残して、必要なら戻せるようにする
9-3. EC2のデプロイ設定
| 設定 | 動作 |
|---|---|
CodeDeployDefault.AllAtOnce |
全台同時に更新 |
CodeDeployDefault.HalfAtATime |
半数ずつ更新 |
CodeDeployDefault.OneAtATime |
1台ずつ更新 |
| カスタム設定 | 最小正常ホスト数を割合や台数で指定 |
本番では、インスタンス数、ロードバランサー、Auto Scaling、ヘルスチェックをセットで考えます。
9-4. 自動ロールバック
CodeDeployは、デプロイ失敗時やCloudWatchアラーム発火時に自動ロールバックできます。
次の例は、ALBを関連付けた本番構成向けです。第14章のハンズオンはALBを使わないため、そのまま適用するものではありません。
# ALBターゲットの5xxアラームを作成する
aws cloudwatch put-metric-alarm \
--alarm-name codedeploy-5xx-alarm \
--namespace AWS/ApplicationELB \
--metric-name HTTPCode_Target_5XX_Count \
--dimensions \
Name=LoadBalancer,Value=app/my-alb/0123456789abcdef \
Name=TargetGroup,Value=targetgroup/my-target-group/0123456789abcdef \
--statistic Sum \
--period 60 \
--threshold 10 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 2 \
--treat-missing-data notBreaching
# CodeDeployデプロイグループにアラームと自動ロールバックを関連付ける
aws deploy update-deployment-group \
--application-name my-application \
--current-deployment-group-name my-deployment-group \
--alarm-configuration \
'enabled=true,ignorePollAlarmFailure=false,alarms=[{name=codedeploy-5xx-alarm}]' \
--auto-rollback-configuration \
'enabled=true,events=DEPLOYMENT_FAILURE,DEPLOYMENT_STOP_ON_ALARM'
ポイントは、CloudWatchアラームを作るだけで終わらせないことです。ALBメトリクスでは LoadBalancer と TargetGroup のディメンションで監視対象を絞り、そのアラームをCodeDeployデプロイグループへ関連付け、さらに DEPLOYMENT_STOP_ON_ALARM を自動ロールバック対象に入れます。
Blue/Greenの場合は、切り替えが終わっても旧環境(Blue)をすぐには消さず、しばらく残しておきましょう。そうしておけば、切り替えたあとで問題が見つかっても、入り口を旧環境へ戻すだけですぐに元の状態へやり直せます。
10. ECSとLambdaへのカナリアデプロイ
この章のタイトルにある「カナリア(Canary)」という言葉を、先に説明しておきます。由来はこの可愛らしい小鳥です。
昔、炭鉱で働く人たちは、有毒ガスをいち早く察知するためにカナリアを連れて坑道に入りました。カナリアは人間より先にガスに反応するので、「カナリアが弱ったら危ない」という早期警報の役割を果たしていたのです。デプロイの「カナリアリリース」も発想は同じで、新しいバージョンをいきなり全ユーザーに出すのではなく、まずは一部のユーザー(たとえば10%)にだけ流してみて、エラーが増えていないか様子をうかがいます。問題がなさそうなら、残りのユーザーにも広げていく、という進め方です。
似たものに「Linear(リニア)」があります。こちらは「10%ずつ、1分おきに増やしていく」のように、一定の割合で少しずつ均等に切り替えていく方式です。由来は英語のLinear(リニア)の意味である「線形の、直線的な」そのままです。カナリアもリニアも、一度に全部を切り替えるAllAtOnceと違って、問題に早く気づき、被害を小さく抑えることを狙っています。
ECSとLambdaは、こうした段階的な切り替えがやりやすいデプロイ先です。順番に見ていきましょう。
EC2デプロイとの大きな違いは、CodeDeploy Agentが出てこないことです。ECSではタスクセットとロードバランサー、Lambdaでは関数バージョンとエイリアスをCodeDeployが操作します。つまり、サーバー内でファイルをコピーするというより、「新しい実行先へトラフィックをどの割合で流すか」を制御するイメージです。
10-1. ECS Blue/Green
ECSでCodeDeployを使う場合は、基本的にBlue/Greenデプロイです。CodeDeploy Agentは不要で、ALBまたはNLBのリスナーとターゲットグループを使ってトラフィックを切り替えます。
流れをもう少し分解すると、次のようになります。
① 現行タスクセットが本番リスナーでトラフィックを受ける
② 新しいタスク定義から、新タスクセットを作成する
③ テストリスナーを設定している場合は、新タスクセットを事前に確認する
④ 問題なければ本番リスナーを新タスクセットへ切り替える
⑤ 旧タスクセットを一定時間残し、問題がなければ削除する
ECSの appspec.yml では、どのタスク定義へ切り替えるか、どのコンテナ名とポートをロードバランサーへ登録するかを指定します。
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/my-task:1"
LoadBalancerInfo:
ContainerName: "app"
ContainerPort: 80
Hooks:
- AfterAllowTestTraffic: "arn:aws:lambda:ap-northeast-1:123456789012:function:ecs-test-hook"
テストリスナーを使う構成では、新しいタスクセットへ本番切替前の確認トラフィックを流せます。ここでHookを使うと、テストリスナー経由で /health へアクセスしたり、簡単なAPI疎通を確認したりできます。
10-2. ECSのデプロイ設定
| 設定 | 動作 |
|---|---|
CodeDeployDefault.ECSAllAtOnce |
一度に全トラフィックを切替 |
CodeDeployDefault.ECSCanary10Percent5Minutes |
10%を流し、5分後に残りを切替 |
CodeDeployDefault.ECSCanary10Percent15Minutes |
10%を流し、15分後に残りを切替 |
CodeDeployDefault.ECSLinear10PercentEvery1Minutes |
1分ごとに10%ずつ切替 |
CodeDeployDefault.ECSLinear10PercentEvery3Minutes |
3分ごとに10%ずつ切替 |
NLBを使う場合は、事前定義のうち CodeDeployDefault.ECSAllAtOnce のみがサポート対象です。
どれを選ぶかは、影響範囲と確認時間で決めます。検証環境ならAllAtOnceでも十分ですが、本番で不具合の影響を抑えたいならCanaryやLinearを選びます。Canaryは「最初に少しだけ流して様子を確認する」、Linearは「一定間隔で少しずつ増やす」と考えると分かりやすいです。
| 環境 | 選びやすい設定 | 考え方 |
|---|---|---|
| dev | AllAtOnce | 速さ優先。すぐ壊してすぐ直す |
| itst | AllAtOnceまたはCanary | 結合確認を早く回す |
| stg | CanaryまたはLinear | 本番に近い形で確認する |
| prd | CanaryまたはLinear | 影響範囲を抑えて段階反映する |
10-3. Lambdaデプロイ
Lambdaでは、関数バージョンとエイリアスを使ってトラフィックを段階的に移します。
Lambdaのカナリアデプロイを理解するには、$LATEST、バージョン、エイリアスの違いを押さえると楽です。
| 要素 | 役割 |
|---|---|
$LATEST |
編集中の最新コード |
| バージョン | ある時点の関数コードを固定したもの |
| エイリアス |
live や prd のような名前で特定バージョンを指す入口 |
本番では、利用者が直接バージョン番号を呼ぶのではなく、live のようなエイリアスを呼ぶ構成にします。CodeDeployは、そのエイリアスが向いている先をバージョン1からバージョン2へ少しずつ動かします。
version: 0.0
Resources:
- myFunction:
Type: AWS::Lambda::Function
Properties:
Name: "my-function"
Alias: "live"
CurrentVersion: "1"
TargetVersion: "2"
Hooks:
- BeforeAllowTraffic: "before-allow-traffic-hook"
- AfterAllowTraffic: "after-allow-traffic-hook"
Lambdaでは、AllAtOnce、Canary10Percent、Linear10PercentEveryなどの設定を選べます。本番環境で、まずは少しだけ流してエラー率やレイテンシといった数値(メトリクス)を確認したい、という場面では、CanaryかLinearが向いています。
たとえば CodeDeployDefault.LambdaCanary10Percent5Minutes を選ぶと、最初に10%だけ新バージョンへ流し、5分後に残りを切り替えます。その間にCloudWatchアラームでエラー率の上昇を検知すれば、ロールバックにつなげられます。
10-4. ECSとLambdaのHook
ECSとLambdaでは、HookとしてLambda関数を呼び出し、トラフィック切替前後の検証を入れられます。
| 対象 | 主なHook | 使い道 |
|---|---|---|
| ECS | AfterAllowTestTraffic |
テストリスナー経由で新タスクセットを検証 |
| ECS | BeforeAllowTraffic |
本番切替直前の確認 |
| ECS | AfterAllowTraffic |
本番切替後の確認 |
| Lambda | BeforeAllowTraffic |
新バージョンへ流す前の確認 |
| Lambda | AfterAllowTraffic |
切替後のメトリクス確認 |
Hook関数は、検証が終わったらCodeDeployへ成功または失敗を通知します。ECSやLambdaのHookでは、Lambda関数が単にHTTP 200を返すだけでは完了扱いになりません。PutLifecycleEventHookExecutionStatus を呼び出し、Succeeded または Failed をCodeDeployへ返します。
Hookでやることは、最初から大きくしすぎないほうが扱いやすいです。最初は /health の疎通、依存先への接続、最低限のレスポンス確認くらいにしておき、重い結合テストはCodeBuildや別の検証ステージへ逃がします。Hookが長すぎると、デプロイの切り替えそのものが遅くなり、障害時の切り戻し判断も遅れます。
import boto3
import urllib.request
codedeploy = boto3.client("codedeploy")
def lambda_handler(event, context):
# 例: テストリスナーや新バージョンのヘルスチェックURLを確認する
deployment_id = event["DeploymentId"]
hook_execution_id = event["LifecycleEventHookExecutionId"]
url = "https://example.com/health"
status = "Failed"
try:
with urllib.request.urlopen(url, timeout=5) as response:
if response.status == 200:
status = "Succeeded"
finally:
codedeploy.put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id,
lifecycleEventHookExecutionId=hook_execution_id,
status=status
)
return {
"status": status
}
Hook用Lambdaの実行ロールには、検証結果をCodeDeployへ返すための codedeploy:PutLifecycleEventHookExecutionStatus を許可します。加えて、CodeDeployサービスロールには、Hook Lambdaを呼び出すための lambda:InvokeFunction が必要です。ECS向けのAWS管理ポリシーを使う場合も、対象関数へ必要な権限が含まれているか確認します。ここでは考え方を示すため、あえて最小のヘルスチェック例にしています。
11. CodePipelineの基礎
11-1. CodePipelineとは
AWS CodePipelineは、ソース取得、ビルド、テスト、承認、デプロイなどをステージとしてつなぐCI/CDオーケストレーションサービスです。
| コンポーネント | 説明 |
|---|---|
| パイプライン | ワークフロー全体 |
| ステージ | Source、Build、Deployなどのまとまり |
| アクション | ステージ内の個別処理 |
| アーティファクト | ステージ間で受け渡す成果物 |
| トランジション | ステージ間の遷移 |
言葉が多くて戸惑うかもしれませんが、工場のベルトコンベアを思い浮かべると理解しやすくなります。
- パイプライン は、ライン全体です。
- ステージ は、ラインの途中にある各作業所です。ソース取得所、ビルド所、デプロイ所のようなまとまりです。
- アクション は、それぞれの作業所で実際に行う作業です。
- アーティファクト は、ベルトの上を流れていく品物そのものです。ビルド成果物を指します。
- トランジション は、作業所と作業所をつなぐベルト部分です。
コードという材料が、ステージを1つずつ通過しながら加工されていき、最後に本番へ届く。この流れ全体を取り仕切るのがCodePipelineです。
CodePipelineを理解するときは、アーティファクトの流れも重要です。SourceステージがソースコードをArtifactとして出力し、Buildステージがそれを受け取ってビルド成果物を作り、Deployステージがその成果物を使って反映します。
Sourceステージ
GitHubやCodeCommitからソースを取得する
出力: SourceArtifact
Buildステージ
SourceArtifactを入力としてCodeBuildを実行する
出力: BuildArtifact
Deployステージ
BuildArtifactを入力としてCodeDeployやECSへ渡す
この「入力Artifact」と「出力Artifact」の名前がずれていると、後続ステージが成果物を受け取れません。CodePipelineの設定画面で SourceOutput、BuildOutput のような名前が出てきたら、「次のステージへ渡す荷物の名前」だと考えると分かりやすいです。
11-2. ソース、ビルド、デプロイプロバイダ
| 区分 | 代表例 |
|---|---|
| Source | CodeCommit、GitHub、GitLab、Bitbucket Cloud、S3、ECR |
| Build | CodeBuild、Jenkins |
| Test | CodeBuild、Jenkins |
| Deploy | CodeDeploy、CloudFormation、ECS、S3、Elastic Beanstalk、AppConfig |
| Approval | Manual approval |
| Invoke | Lambda |
GitHub、GitLab、Bitbucket Cloudを使う場合は、SourceにCodeConnectionsの接続を指定します。CodeCommitを使う場合は、AWS内のSourceプロバイダとして直接指定できます。S3をSourceにする構成もあり、これは「外部で作ったZIPを置いたらパイプラインを動かす」ような用途で使います。
初心者のうちは、Source、Build、Deployの3ステージだけで十分です。そこに慣れてから、Test、Approval、Invoke、CloudFormationなどを足していくと、構成が追いやすくなります。
11-3. V1とV2
| 観点 | V1 | V2 |
|---|---|---|
| 料金 | アクティブパイプライン単位 | アクション実行分単位 |
| トリガーフィルタ | 非対応 | 対応 |
| パイプラインレベル変数 | 非対応 | 対応 |
| Gitタグ起動 | 非対応 | 対応 |
| PR、ブランチ、パスフィルタ | 非対応 | 対応 |
| ステージ条件 | 非対応 | 対応 |
| ステージロールバック | 非対応 | 対応 |
これから新しく作るなら、基本はV2を第一候補にしてよいです。ブランチ、タグ、パス、Pull Requestを条件にしたトリガー、パイプライン変数、ステージ条件、ステージロールバックなど、実務で使いたい機能がV2側に寄っています。
一方で、既存のV1パイプラインが安定して動いているなら、慌てて作り替える必要はありません。新規構成や大きな見直しのタイミングでV2へ寄せる、くらいの考え方で十分です。
11-4. 料金の考え方
CodePipeline V1は、アクティブパイプラインごとの月額です。V2は、手動承認とカスタムアクションを除くアクション実行分に対して、分単位で課金されます。
| タイプ | 無料枠 |
|---|---|
| V1 | 月1本のアクティブパイプライン |
| V2 | 月100分のアクション実行時間 |
たとえば、V2パイプラインでSource、Build、Deployがそれぞれ2分動く場合、1回の実行はおおよそ合計6アクション実行時間(分)として考えます。実際にはアクションごとに計算されるため、料金ページで最新条件を確認します。
料金を考えるときは、パイプライン本数だけでなく、起動回数も確認します。feature/* ブランチの全pushで本番用パイプラインが動く設計にすると、不要な実行が増えます。12章で扱うトリガーフィルタは、コスト面でもかなり大事です。
12. CodePipeline V2の実践機能
12-1. トリガーフィルタ
V2では、GitHubなどの接続ソースに対して、ブランチ、タグ、ファイルパス、プルリクエスト条件で起動を制御できます。
| フィルタ | 例 |
|---|---|
| ブランチ |
main、release/*
|
| タグ |
v*、*-beta を除外 |
| ファイルパス |
src/** の変更のみ |
| プルリクエスト | PR作成、更新、クローズ |
ドキュメント変更だけで本番パイプラインが走るのを避けるには、docs/** や *.md を除外します。逆に、アプリケーションのコードが backend/** にまとまっているなら、backend/** の変更だけで起動するようにできます。
複数ブランチ運用では、このフィルタがかなり重要です。feature/* のpushはテストだけ、develop はdev環境へ自動反映、release/* はitstやstgへ反映、main はprdへ反映、というように、起動条件と反映先を分けられます。
feature/* -> テストだけ
develop -> dev環境へ自動デプロイ
release/* -> itst、stg環境へデプロイ
main -> prd環境へデプロイ
すべてのブランチ変更で同じパイプラインを動かすと、意図しない環境に反映される危険があります。ブランチと環境の対応は、最初に決めておくべき設計項目です。
12-2. パイプライン変数
| 種類 | 例 |
|---|---|
| パイプラインレベル変数 | DEPLOY_ENV=staging |
| アクション出力変数 | CodeBuildの IMAGE_TAG
|
| 名前空間変数 | #{SourceVariables.CommitId} |
CodeBuildから後続へ値を渡すには、exported-variables を使います。
env:
exported-variables:
- IMAGE_TAG
phases:
build:
commands:
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- echo "IMAGE_TAG=$IMAGE_TAG"
変数は、環境ごとに同じパイプライン定義を使い回したいときに便利です。たとえば、DEPLOY_ENV=dev のときはdev用のS3バケットやCodeDeployデプロイグループを使い、DEPLOY_ENV=prd のときはprd用を使う、といった形です。
ただし、パイプライン変数に秘密情報を入れるのは避けます。環境名、リリース番号、イメージタグのように、見えても困らない値に使うのが基本です。秘密情報はCodeBuild側でSecrets ManagerやParameter Storeから読むほうが安全です。
12-3. 手動承認
本番環境へデプロイする手前には、Manual approval(手動承認)アクションを1つ置いておくと安心です。これは、パイプラインをそこでいったん止めて、人が承認ボタンを押すまで先へ進ませない仕組みです。「最後は人の目で確認してから本番へ」というブレーキの役割を果たします。
| 設定 | 内容 |
|---|---|
| SNS通知 | 承認依頼をメールやチャットへ送る |
| レビューURL | ステージング環境や変更セットURLを添付 |
| コメント | 承認、却下時に理由を残す |
| 待機時間 | デフォルト7日。アクション単位で5分〜最大60日まで設定可 |
aws codepipeline put-approval-result \
--pipeline-name my-pipeline \
--stage-name Approval \
--action-name ManualApproval \
--token <approval-token> \
--result summary="LGTM",status=Approved
手動承認は、何でもかんでも置けばよいものではありません。dev環境への反映まで毎回承認にすると、開発速度が落ちます。置きやすいのは、stgからprdへ進む直前です。
develop -> dev 自動
release -> itst 自動または軽い承認
release -> stg 自動または軽い承認
main -> prd 手動承認あり
承認者が確認すべき情報は、承認画面や通知に寄せます。変更内容、コミットID、差分URL、ステージングURL、テスト結果、影響範囲が分かると、承認が単なるボタン押しになりにくくなります。
12-4. 通知とEventBridge
CodePipelineの状態変化はEventBridgeで拾えます。失敗時にLambdaを起動したり、SNSやAmazon Q Developer in chat applications(旧AWS Chatbot)で通知できます。
aws events put-rule \
--name codepipeline-failed-rule \
--event-pattern '{
"source": ["aws.codepipeline"],
"detail-type": ["CodePipeline Pipeline Execution State Change"],
"detail": {
"state": ["FAILED"]
}
}'
手動承認の通知はSNS、パイプライン全体の失敗通知はEventBridgeとAmazon Q Developer in chat applications、というふうに役割を分けておくと、あとで確認したときも扱いやすくなります。
SNS通知は、承認待ちの担当者へメールを送る用途に向いています。SlackやMicrosoft Teamsへ流したい場合は、Amazon Q Developer in chat applicationsとSNSトピックを組み合わせます。
# SNSトピックを作成
aws sns create-topic \
--name codepipeline-approval-topic
# メール通知を追加
aws sns subscribe \
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:codepipeline-approval-topic \
--protocol email \
--notification-endpoint reviewer@example.com
12-5. JSON定義とIaC管理
コンソールで作ったパイプラインも、JSONとして取得して管理できます。
# パイプライン定義をエクスポートし、更新に不要なmetadataを除く
aws codepipeline get-pipeline \
--name my-pipeline \
| jq 'del(.metadata)' > pipeline.json
# pipeline.jsonを編集してから既存パイプラインを更新
aws codepipeline update-pipeline \
--cli-input-json file://pipeline.json
get-pipeline の出力全体には、更新対象の pipeline 以外に metadata も含まれます。そのまま update-pipeline へ渡すと失敗するため、上の例では jq で metadata を除いています。update-pipeline --cli-input-json は {"pipeline": {...}} という形を受け取るので、pipeline の中身だけを単体で抜き出さないようにします。
本番運用では、CodePipelineそのものも、CloudFormationやCDK、Terraformといったコードで定義しておくのが理想です。インフラの構成をコードとして管理するこの考え方を、IaC(Infrastructure as Code)と呼びます。手作業でコンソールをいじっていると、誰がいつ何を変えたのかが追いにくくなるためです。
特に複数環境を扱う場合は、コンソール手作業だと設定差分が生まれやすくなります。devだけCodeBuildの環境変数が違う、stgだけ承認ステージが抜けている、prdだけArtifact名が違う、という小さな差分が、後で調査しにくい障害につながります。
12-6. ステージングから本番への流れ
本番を含むパイプラインでは、ステージング環境でのデプロイと確認を先に行い、その結果を見てから本番へ進めます。
Source
Build
DeployToStaging
SmokeTest
ManualApproval
DeployToProduction
PostDeployCheck
手動承認のステップには、ステージングのURL、変更内容、コミットID、CodeBuildのテスト結果、CloudFormationの変更セットなど、レビューに必要な材料を集めておきます。承認する人が「自分は今、何にOKを出そうとしているのか」をきちんと判断できる状態にしておく。これが、手動承認をただのボタン押しで終わらせないためのコツです。
dev、itst、stg、prdの4環境なら、次のような流れが分かりやすいです。
Source
Build
UnitTest
DeployToDev
IntegrationTest
DeployToItst
DeployToStg
ManualApproval
DeployToPrd
PostDeployCheck
ただし、すべてを1本の巨大なパイプラインに詰め込むと、失敗時の再実行や権限分離が難しくなることがあります。小さく始めるなら、dev用、stg用、prd用のようにパイプラインを分け、ブランチやタグで起動条件を分ける構成も扱いやすいです。
12-7. 複数ブランチと複数環境の対応
実務では、1つのブランチだけで開発から本番まで進めることは多くありません。feature、develop、release、mainのようにブランチを分け、それぞれの役割に応じて反映先を変えます。
4環境の例として、dev、itst、stg、prdを考えます。
| ブランチ | 主な用途 | 反映先 | 承認 |
|---|---|---|---|
feature/* |
個別機能の開発 | 反映しない、または一時環境 | なし |
develop |
開発ブランチの統合 | dev | なし |
release/* |
結合試験、受入試験、リリース候補 | itst、stg | 必要に応じて |
main |
本番リリース済みコード | prd | あり |
この構成では、feature/* ではCodeBuildのテストだけを実行し、develop へマージされたらdev環境へ自動デプロイします。リリース候補を release/2026-06-01 のようなブランチに切ったらitst、stgへ進め、最後にmainへマージしてprdへ出す、という流れです。
feature/* のpush
-> CodeBuildでテスト
develop へのpush
-> devへ自動デプロイ
release/* へのpush
-> itstへデプロイ
-> stgへデプロイ
main へのpush
-> 手動承認
-> prdへデプロイ
CodeDeploy側では、8章で触れたように環境ごとのデプロイグループを作ります。CodePipeline側では、Sourceトリガーでブランチを分け、Deployアクションで対応するデプロイグループを指定します。
| 環境 | CodeDeployデプロイグループ | 起動ブランチ例 |
|---|---|---|
| dev | sample-web-dev-dg |
develop |
| itst | sample-web-itst-dg |
release/* |
| stg | sample-web-stg-dg |
release/* |
| prd | sample-web-prd-dg |
main |
パイプラインを1本にまとめるか、環境ごとに分けるかは、チームの運用で決めます。
| 構成 | 向いている場面 | 注意点 |
|---|---|---|
| 1本のマルチステージパイプライン | リリースの流れを1本で追いたい | 失敗時の再実行範囲、権限分離を設計する |
| 環境ごとにパイプラインを分ける | dev、stg、prdで権限や起動条件を分けたい | パイプライン定義の重複をIaCで管理する |
迷う場合は、最初は環境ごとに分けるほうが理解しやすいです。devパイプラインはdevelopだけ、prdパイプラインはmainだけ、という形にすると、どのブランチがどの環境に反映されるかが見えやすくなります。
13. 高度なパイプライン設計
ここからは少し発展的な内容に入ります。最初のうちは「こんなこともできるんだな」と眺めるくらいで十分なので、必要になったときに読み返してもらえれば大丈夫です。
13-1. マルチステージ構成
実務では、Buildの次にすぐ本番ではなく、複数ステージを挟みます。
Source
Build
UnitTest
IntegrationTest
DeployToStaging
Approval
DeployToProduction
各ステージの責務を分けると、どこで失敗したのかが見えやすくなります。
13-2. 並列アクション
同じステージ内で runOrder が同じアクションは並列実行されます。
runOrder 1: UnitTest, Lint, SecurityScan
runOrder 2: SmokeTest
テストを並列化すると時間短縮できますが、成果物の依存関係がある場合は runOrder を分けます。
13-3. 複数ソースと複数アーティファクト
フロントエンドとバックエンド、アプリケーションとインフラ定義を別リポジトリに分ける場合、複数ソースアクションを使います。
version: 0.2
phases:
build:
commands:
- echo "メインソースは $CODEBUILD_SRC_DIR にあります"
- echo "インフラソースは $CODEBUILD_SRC_DIR_infra にあります"
artifacts:
files:
- README.md
secondary-artifacts:
app-artifact:
files:
- '**/*'
base-directory: dist
cfn-artifact:
files:
- '**/*'
base-directory: infra
secondary artifactsを使う場合でも、トップレベルの artifacts.files(主成果物)は省略できません。また、app-artifact や cfn-artifact という識別子は、CodeBuildプロジェクト側のsecondary artifact設定と一致させる必要があります。これらの名前は後続アクションからも参照するため、わかりやすく付けます。
13-4. CloudFormation連携
CodePipelineはCloudFormationデプロイアクションを使えます。
| アクションモード | 用途 |
|---|---|
CREATE_UPDATE |
スタックを直接作成、更新 |
DELETE_ONLY |
スタック削除 |
REPLACE_ON_FAILURE |
失敗スタックの置換 |
CHANGE_SET_REPLACE |
変更セットがなければ作成し、既存なら削除して作り直す |
CHANGE_SET_EXECUTE |
変更セット実行 |
安全な本番反映では、変更セット作成、手動承認、変更セット実行の順にします。
AWSTemplateFormatVersion: "2010-09-09"
Description: Simple S3 bucket for CodePipeline demo
Resources:
DemoBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
ここで出てくる「変更セット(Change Set)」は、CloudFormationが「この操作で何がどう変わるのか」を実行前に見せてくれるプレビューのようなものです。本番では、いきなりCloudFormationを実行するのではなく、まず CHANGE_SET_REPLACE で変更内容のプレビュー(変更セット)を作り、Manual approvalで人が中身を確かめてから、CHANGE_SET_EXECUTE で実際に反映する。この順番にしておけば、思わぬ変更をうっかり本番へ流してしまう事故を防げます。
13-5. クロスアカウントデプロイ
開発アカウントのCodePipelineから本番アカウントへデプロイする場合、S3、KMS、AssumeRole、デプロイ先サービスの権限をまたいで設計します。
| 要素 | 設定 |
|---|---|
| 開発アカウントS3 | 本番ロールからの取得を許可 |
| 開発アカウントKMS | 本番ロールへの復号権限を付与 |
| 本番アカウントIAM | CodePipelineからAssumeRoleできるロール |
| 本番アカウントデプロイ先 | CodeDeploy、CloudFormation、ECSなど |
本番アカウントのDeployアクションには、roleArn でクロスアカウントロールを指定します。
13-6. クロスリージョンとLambda Invoke
クロスリージョンデプロイでは、対象アクションに region を指定します。リージョンごとにアーティファクトストアが必要になります。
Lambda Invokeアクションは、Slack通知、DBマイグレーション、外部API呼び出し、事前検証に使えます。
import boto3
codepipeline = boto3.client("codepipeline")
def lambda_handler(event, context):
job_id = event["CodePipeline.job"]["id"]
try:
# ここに独自処理を書く
codepipeline.put_job_success_result(jobId=job_id)
except Exception as exc:
codepipeline.put_job_failure_result(
jobId=job_id,
failureDetails={
"type": "JobFailed",
"message": str(exc)
}
)
必ず成功、失敗のどちらかをCodePipelineへ返します。返さないとアクションがタイムアウトまで待ち続けます。
14. 【ハンズオン】ゼロから作るCI/CDパイプライン
ここまで、各サービスを1つずつ見てきました。この章では、それらをいよいよ1本のパイプラインにつなげて、GitHubへのpushをきっかけにEC2まで自動で届く流れを、ゼロから組み立てます。これまでの章の総復習のつもりで読んでみてください。なお、この章でエラーが出るとしたら、その大半はIAMロールの権限まわりが原因です。何か失敗したら、まず「どのロールに、どの権限が足りていないか」を疑うと、解決が早くなります。
14-1. 全体像とゴール
この章のゴールは、「GitHubのmainブランチにpushしたら、自動でテスト → ビルド → EC2へのデプロイまで走り、ブラウザで開いたWebページの中身が新しくなる」という状態を、自分の手で一から作ることです。
作る構成はGitHub、AWS CodeConnections、CodePipeline V2、CodeBuild、S3 Artifact Bucket、CodeDeploy、EC2の組み合わせです。完成形の流れは次のとおりです。
開発者がGitHubへpushすると、AWS CodeConnectionsが変更検知とソース取得の橋渡しを行い、CodePipelineのSourceステージへソースを渡します。CodePipelineはBuildステージでCodeBuildを呼び出し、ビルド成果物をS3 Artifact Bucketへ保存します。その成果物をDeployステージでCodeDeployが利用し、EC2上のApacheへ反映する、という流れです。
ざっくり言うと、GitHubが入口、CodeConnectionsがAWSとの接続役、CodePipelineが全体をまとめる司令塔、CodeBuildがテスト係、CodeDeployが配達係、EC2が実際にWebページを表示するApacheサーバー、という分担です。初めてなら所要時間は1〜2時間ほど。料金はEC2を立てている時間に対してかかるので、終わったら必ず最後(14-17)のクリーンアップまでやりきりましょう。
14-2. 前提条件
始める前に、次のものをそろえておきます。
| 必要なもの | 補足 |
|---|---|
| AWSアカウント | この章ではIAMロールやEC2を作るので、管理者に近い権限があるとスムーズです |
| 作業リージョン | この記事は東京リージョン(ap-northeast-1)に統一します。コンソール右上のリージョン表示も東京に合わせてください |
| GitHubアカウント | ソースの置き場所に使います(第4章で作ったリポジトリを使い回してもOK) |
| ローカル環境 |
git、できれば AWS CLI(aws configure 済み)があると便利です |
操作はマネジメントコンソール(ブラウザ)を中心に説明し、要所でCLIも併記します。慣れないうちは、コンソールのほうが「今どこを触っているか」を目で追いながら進められるためです。
EC2は起動している時間に対して課金されます。t2.micro / t3.micro のような小さいインスタンスでも、消し忘れると地味に積み上がりますので、検証が終わったら14-17のクリーンアップを必ず実行してください。
14-3. 作成するリソース一覧
| 分類 | リソース |
|---|---|
| ソース | GitHubリポジトリ、CodeConnections |
| ビルド | CodeBuildプロジェクト、CloudWatch Logs |
| 成果物 | S3アーティファクトバケット |
| デプロイ | CodeDeployアプリケーション、デプロイグループ |
| 実行環境 | EC2、セキュリティグループ、IAMインスタンスプロファイル |
| オーケストレーション | CodePipeline V2 |
| 権限 | EC2、CodeBuild、CodeDeploy、CodePipelineの各ロール |
14-4. リポジトリとファイルを準備する
まず、デプロイするWebアプリと、CI/CDの設定ファイルをそろえます。GitHubに aws-code-series-handson というリポジトリを作り(第4章で作ったものを使い回しても構いません)、次の構成でファイルを置いていきます。
aws-code-series-handson/
├── buildspec.yml
├── appspec.yml
├── index.html
├── css/
│ └── style.css
├── js/
│ └── app.js
├── tests/
│ └── test_app.sh
└── scripts/
├── install_dependencies.sh
├── stop_server.sh
├── start_server.sh
└── validate_service.sh
ここから、各ファイルの中身を順に作っていきます。まずは表示するWebページ本体です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>AWS Code シリーズ ハンズオン</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1>AWS CI/CD ハンズオン</h1>
<p>バージョン: v1</p>
<script src="js/app.js"></script>
</body>
</html>
body {
font-family: sans-serif;
text-align: center;
margin-top: 80px;
background-color: #f4f6f8;
}
console.log("AWS Code series handson loaded");
次に、CodeBuildで走らせるテストです。ここでは「index.html にバージョン表記が入っているか」を確認するだけの、ごく簡単なシェルスクリプトにしておきます(本来はアプリのユニットテストなどを置く場所です)。
#!/bin/bash
set -e
echo "テストを開始します"
if grep -q "バージョン" index.html; then
echo "OK: index.html にバージョン表記があります"
else
echo "NG: index.html にバージョン表記がありません"
exit 1
fi
echo "テストが完了しました"
続いて、CodeDeployがEC2上で実行する4つのスクリプトです。appspec.yml のHookから、デプロイの各タイミングで呼ばれます(割り当ては14-6で説明します)。
#!/bin/bash
# BeforeInstall: Apacheが入っていなければインストールする
if ! systemctl list-unit-files | grep -q httpd; then
dnf install -y httpd
fi
#!/bin/bash
# ApplicationStop: 2回目以降のデプロイでApacheを止める
if systemctl is-active --quiet httpd; then
systemctl stop httpd
fi
#!/bin/bash
# ApplicationStart: Apacheを起動し、再起動後も自動で立ち上がるようにする
systemctl start httpd
systemctl enable httpd
#!/bin/bash
# ValidateService: ローカルにHTTPアクセスして、200が返るか確認する
echo "ValidateServiceを実行します"
sleep 5
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/)
if [ "$HTTP_CODE" = "200" ]; then
echo "Validation passed"
exit 0
else
echo "Validation failed (HTTP $HTTP_CODE)"
exit 1
fi
残る buildspec.yml と appspec.yml は重要なので、次の2項に分けて説明します。ここまでのファイルがそろったら、いったんGitHubへpushしておきましょう。
GitHubで空のリポジトリを作成したあと、ローカルの aws-code-series-handson ディレクトリで次のコマンドを実行します。your-name の部分は自分のGitHubユーザー名、またはOrganization名に置き換えてください。
# Gitリポジトリとして初期化
git init
# ブランチ名をmainにする
git branch -M main
# 作成したファイルをコミット対象にする
git add .
# 最初のコミットを作る
git commit -m "Add handson application and CI/CD config"
# GitHubリポジトリをリモートとして登録
git remote add origin git@github.com:your-name/aws-code-series-handson.git
# GitHubへpush
git push origin main
14-5. buildspec.yml(テストと成果物の受け渡し)
CodeBuildにやらせる内容を書いたファイルで、リポジトリのルートに置きます。今回は「スクリプトに実行権限を付ける → テストを走らせる → 公開ファイルとCodeDeployに必要な制御ファイルだけを成果物として次へ渡す」という構成です。
version: 0.2
phases:
install:
commands:
- echo "依存関係を確認します"
- chmod +x tests/test_app.sh
- chmod +x scripts/*.sh
pre_build:
commands:
- echo "テストを実行します"
- ./tests/test_app.sh
build:
commands:
- echo "成果物を作成します"
- mkdir -p artifact/web artifact/scripts
- cp index.html artifact/web/
- cp -r css js artifact/web/
- cp appspec.yml artifact/
- cp scripts/*.sh artifact/scripts/
post_build:
commands:
- echo "ビルドが完了しました"
artifacts:
base-directory: artifact
files:
- '**/*'
pre_build のテストが失敗すると、ここでビルドが止まり、Deployまで進みません。「壊れたものは本番に出さない」を、この1行が担保してくれます。
artifact/web に公開したいファイルだけをまとめ、artifact/scripts にCodeDeployのHookスクリプトを入れています。これにより、tests/ や buildspec.yml をApacheの公開ディレクトリへ置かずに済みます。
14-6. appspec.yml(EC2への配置とスクリプト実行)
CodeDeployがEC2上でやることを書いたファイルで、こちらもリポジトリのルートに置きます。files で「成果物内の web ディレクトリだけを /var/www/html に置く」ことを指示し、hooks で14-4の4つのスクリプトを、デプロイの各タイミングへ割り当てています。
version: 0.0
os: linux
files:
- source: /web
destination: /var/www/html
hooks:
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 300
runas: root
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 300
runas: root
ApplicationStart:
- location: scripts/start_server.sh
timeout: 300
runas: root
ValidateService:
- location: scripts/validate_service.sh
timeout: 300
runas: root
第7章で説明したライフサイクルの順番(ApplicationStop → BeforeInstall → Install → ApplicationStart → ValidateService)に沿って、これらのスクリプトが自動で呼び出されます。初回デプロイでは、前回の正常リビジョンがまだないため ApplicationStop はスキップされます。
ファイルの準備はここまでです。次からは、AWS側のリソースをコンソールで作っていきます。
14-7. IAMロールを作る
このIAMロールが、このハンズオン最大の関門です。CI/CDは複数のサービスが連携するので、「どのサービスに、何をしてよいか」を表すIAMロールを先に用意しておきます。作るのは次の4つですが、CodeBuildとCodePipeline用の2つは、後の作成ウィザードで自動生成させるのが簡単です。ここでは手作業で、EC2用とCodeDeploy用の2つを作ります。
| ロール | 誰のための権限か | 付けるポリシー |
|---|---|---|
| EC2インスタンスロール | EC2自身 |
AmazonEC2RoleforAWSCodeDeploy、AmazonSSMManagedInstanceCore
|
| CodeDeployサービスロール | CodeDeploy | AWSCodeDeployRole |
| CodeBuildサービスロール | CodeBuild | 14-11のBuildステージで自動生成 |
| CodePipelineサービスロール | CodePipeline | 14-11のウィザードで自動生成 |
- コンソールで「IAM」→「ロール」→「ロールを作成」を開く
- 信頼されたエンティティタイプで「AWS のサービス」、ユースケースで「EC2」を選んで次へ
- 許可ポリシーで
AmazonEC2RoleforAWSCodeDeploy(成果物をS3から取得するため)とAmazonSSMManagedInstanceCore(管理・調査用)の2つにチェック - ロール名を
code-series-ec2-roleとして作成
- 同じく「ロールを作成」を開く
- ユースケースで「CodeDeploy」を選ぶ(これを選ぶと、CodeDeployを信頼する設定が自動で入ります)
- 許可ポリシーは
AWSCodeDeployRoleが自動で選ばれるので、そのまま次へ - ロール名を
code-series-codedeploy-roleとして作成
この2つができていれば、後のステップで権限エラーに悩まされる確率がぐっと下がります。
14-8. EC2を起動する
デプロイ先となるWebサーバー(EC2)を1台立てます。起動と同時に、Apacheと「CodeDeploy Agent」(第7章で出てきた、各サーバーに常駐してデプロイを実行してくれるプログラム)を自動でインストールしてしまいます。
1.「EC2」→「インスタンスを起動」を開く
2.名前に CodeDeploy-Demo と入力する(このNameタグを、あとでデプロイ対象の目印に使います)
3.AMIは「Amazon Linux 2023」、インスタンスタイプは t2.micro または t3.micro

4.キーペアは「キーペアなしで続行」で進める

5.ネットワーク設定で、パブリックIPの自動割り当てが有効になっていることを確認する

6.セキュリティグループは「セキュリティグループを作成」を選び、次の3つのチェックは入れない

7.「高度な詳細」を開き、「IAMインスタンスプロフィール」で `code-series-ec2-role` を選ぶ
8.同じく「高度な詳細」の「ユーザーデータ」に、次のスクリプトを貼り付ける
#!/bin/bash
set -euxo pipefail
dnf update -y
dnf install -y httpd ruby wget
systemctl start httpd
systemctl enable httpd
# CodeDeploy Agent のインストール
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install
chmod +x ./install
./install auto
systemctl start codedeploy-agent
systemctl enable codedeploy-agent
rm -f ./install
ユーザーデータを貼り付けたら、「インスタンスを起動」をクリックします。
起動後、作成されたセキュリティグループのインバウンドルールを編集し、ブラウザ確認用にHTTPだけを追加します。
リソースタイプでマイIPをクリックすれば、自動的に自分のグローバルIPが設定されます。
| 種類 | プロトコル | ポート | ソース |
|---|---|---|---|
| HTTP | TCP | 80 | 自分のグローバルIP /32
|
SSH(ポート22)は開けません。14-7で AmazonSSMManagedInstanceCore を付けているので、サーバーへ入る必要がある場合はEC2の「接続」からSession Managerを使います。どうしてもSSHを使う場合だけ、自分のグローバルIP /32 に限定して一時的に許可し、作業後に削除してください。
添付画像の「インターネットからのHTTPトラフィックを許可」をそのまま使うと、HTTPが広い範囲へ公開される構成になりやすいです。この記事では検証用のため、ブラウザ確認に必要なHTTPだけを自分のIPへ絞ります。本番では、EC2を直接インターネットへ出すのではなく、ALB、HTTPS、ACM、WAF、プライベートサブネットなどを組み合わせる構成を検討してください。
数分待ってから、ブラウザで http://<EC2のパブリックIP>/ を開き、下記のようなApacheのテストページが表示されれば下準備は成功です(この時点では、まだ自分の index.html は出ません)。AWSコンソール上のオープンアドレスで飛ぶとhttpsになるので、httpに直すと下記のような画面になるはずです(Apacheのテストページ)。
14-9. GitHubと接続する(CodeConnections)
CodePipelineがGitHubのリポジトリを読めるように、接続を作ります(第4章の復習です)。すでに第4章で接続を作っていれば、それをそのまま使えるので、この項は飛ばして構いません。
- 「デベロッパー用ツール」と検索→「設定」→「接続」→「接続を作成」を開く
- プロバイダーで「GitHub」を選び、接続名を入力する。例として
github-connectionのような名前にします - 「アプリインストール - オプショナル」で「新しいアプリをインストールする」をクリック
- GitHub側の画面で、AWS Connector for GitHub by AWS CodeConnectionsをインストールし、対象リポジトリへのアクセスを許可
- AWSマネコンに戻ると、アプリインストール欄にインストールIDが自動で入るので、その状態で接続を作成
- 接続のステータスが「利用可能(Available)」になればOK
この手順で作ると、CodePipeline作成画面で接続とリポジトリを選びやすくなります。接続は選べるのにリポジトリが出ない場合は、アプリインストール時に対象リポジトリを許可したか、main ブランチと初回コミットが存在するかを確認してください。
14-10. CodeDeployアプリとデプロイグループを作る
EC2へ配るためのCodeDeploy側を用意します。CodeDeployは「アプリケーション」と「デプロイグループ」の2階建てです。
アプリケーションの作成
- 「CodeDeploy」→「アプリケーション」→「アプリケーションの作成」を開く
- アプリケーション名を
code-series-demo-app、コンピューティングプラットフォームを「EC2/オンプレミス」にして作成
デプロイグループの作成
- 作ったアプリケーションを開き、「デプロイグループの作成」を押す
- デプロイグループ名を
code-series-demo-dgにする - サービスロールに、14-7で作った
code-series-codedeploy-roleを指定 - デプロイタイプは「インプレース」
- 環境設定で「Amazon EC2 インスタンス」を選び、タグで キー
Name= 値CodeDeploy-Demoを指定(14-8で付けたタグです) - デプロイ設定は
CodeDeployDefault.AllAtOnce(インスタンスが1台なので、これでOK) - 「ロードバランシングを有効にする」はオフにしておく
- 「デプロイグループの作成」をクリック
ここで指定するタグが、EC2のNameタグと1文字でも違うと「デプロイ対象が見つからない」となります。CodeDeploy-Demo を正確に合わせてください。
14-11. CodePipelineで全体をつなぐ
いよいよ、ここまで作ったものを1本のパイプラインにつなぎます。
CodeBuildプロジェクトは、このCodePipeline作成画面のBuildステージで作ります。CodeBuild単体の作成画面からGitHubリポジトリを選ぶ必要はありません。今回の構成では、GitHubを読むのはCodePipelineのSourceステージで、CodeBuildはSourceステージから渡されたArtifactを受け取って buildspec.yml を実行します。
- 「CodePipeline」→「パイプライン」→「パイプラインを作成する」を開く
- 作成オプションで「カスタムパイプラインを構築する」を選ぶ
- パイプライン名を
code-series-demo-pipelineにする - 実行モードは「キュー」を選ぶ
- サービスロールは「新しいサービスロール」を選ぶ(CodePipeline用ロールが自動生成されます)
- ソースステージで、プロバイダーに「GitHub」を選び、14-9の接続・リポジトリ・ブランチ
mainを指定 - ビルドステージで、プロバイダーに「AWS CodeBuild」を選び、「プロジェクトを作成する」から新しいCodeBuildプロジェクトを作る
- プロジェクト名は
code-series-demo-build - 環境は「マネージド型イメージ」
- オペレーティングシステムはAmazon Linux、ランタイムはStandard、イメージは最新版
- コンピューティングは
general1.small - サービスロールは「新しいサービスロール」
- Buildspecは「buildspec ファイルを使用する」
- プロジェクト名は
- テストステージが表示された場合はスキップする
- デプロイステージで、プロバイダーに「AWS CodeDeploy」を選び、アプリケーション
code-series-demo-app、デプロイグループcode-series-demo-dgを指定 - 内容を確認して「パイプラインを作成する」
作成オプションの画面では、ECRにプッシュ や ECS Fargateにデプロイ などのテンプレートも表示されます。今回はGitHub → CodeBuild → CodeDeploy → EC2の流れを自分で組むので、テンプレートではなく「カスタムパイプラインを構築する」を選びます。
現在のコンソールでは、ここで「パイプラインタイプ V2」を直接選ぶ欄が表示されない場合があります。その場合は、実行モードで「キュー」を選んで進めます。キューや並行の実行モードはV2側の機能なので、V2という名前の選択肢がなくても問題ありません。
途中でテストステージが表示されることがありますが、今回は buildspec.yml の pre_build で tests/test_app.sh を実行しています。そのため、CodePipeline上で別のTestステージを作らず、スキップして進めます。結合テストやブラウザテストを別工程に分けたい場合は、後からTestステージを追加する形で十分です。
作成すると、パイプラインは自動で1回目の実行を始めます。Source → Build → Deploy の順に緑のチェックが付いていけば成功です。途中で失敗したら、まずそのステージのログ(CodeBuildならビルドログ、CodeDeployならデプロイの詳細画面)を開いて、エラーメッセージを読むのが解決の近道です。
14-12. 動作確認
パイプラインが最後まで緑になったら、ブラウザで http://<EC2のパブリックIP>/ を開きます。自分の index.html(「バージョン: v1」と書いたページ)が表示されれば、CI/CDがひととおり通った証拠です。
次に、コードの変更が自動で反映されることを確かめます。index.html の v1 を v2 に書き換えます。
変更したら、GitHubへpushしてみてください。
git add index.html
git commit -m "Bump version to v2"
git push origin main
しばらくすると、pushをきっかけにパイプラインがひとりでに動き出します。完了後にページを再読み込みして、表示が v2 に変わっていれば、自動デプロイまで成功です。
慣れてきたら、次のようなシナリオも試すと、CI/CDの挙動が体でつかめます。
| シナリオ | 期待結果 |
|---|---|
| 正常なコード変更 | push後にSource、Build、Deployが成功 |
| テスト失敗 | Buildで停止し、Deployされない |
| デプロイ失敗 | Deployで失敗し、設定によりロールバック |
| 手動再実行 |
start-pipeline-execution で再実行 |
| ステージ再試行 | 失敗したステージだけを再試行 |
# パイプライン状態を確認
aws codepipeline get-pipeline-state \
--name code-series-demo-pipeline \
--query 'stageStates[*].{Stage:stageName,Status:latestExecution.status}'
# 手動実行
aws codepipeline start-pipeline-execution \
--name code-series-demo-pipeline
# 失敗アクションを再試行
aws codepipeline retry-stage-execution \
--pipeline-name code-series-demo-pipeline \
--stage-name Deploy \
--pipeline-execution-id <execution-id> \
--retry-mode FAILED_ACTIONS
14-13. (応用)すべてをAWSの中だけで完結させる
ここまでのハンズオンは、ソースの置き場所にGitHubを使う構成でした。応用編として、まったく同じCI/CDを、ソースもCodeCommitに置いてAWS側に寄せる構成に作り替えてみます。GitHub版はそのまま残したいので、ここからは置き換えではなく、別構成として追加で組み立てる形にします。
この構成の良さは、ソース管理からパイプライン実行までをAWSアカウント内に寄せられる点です。
- 外部サービス(GitHub)とのCodeConnections接続がいらなくなり、認証・認可をIAMに寄せられる
- CodeCommitやCodePipelineの操作をCloudTrailで追いやすくなり、AWS側の監査設計と合わせやすい
- 「どのサービスに、誰が、どんな権限を持つか」をIAMだけで説明できる
ただし、CodeCommitを使うだけでネットワークが自動的に閉域化されるわけではありません。閉域要件がある場合は、開発端末からのGitアクセス、AWS API、S3、CloudWatch Logs、SSM、STSなどへの到達経路を、VPCエンドポイントやネットワーク設計まで含めて検討します。
第3章でも触れたとおり、CodeCommitは2025年11月25日に新規利用が再開されています。新しいアカウントでも、これから紹介する手順で使えます。
ここで大きく変わるのは、入口であるSourceステージです。ビルド(CodeBuild)、デプロイ(CodeDeploy)、EC2側の準備は、GitHub版で作ったものをそのまま使い回せます。
図にすると、GitHubとCodeConnectionsの代わりにCodeCommitとEventBridgeが入ります。CodeCommitはCodePipelineのSourceステージへソースを渡し、EventBridgeはCodeCommitの変更を検知してCodePipelineを起動します。EventBridgeはソースを運ぶ役ではなく、あくまで起動のきっかけを作る役割です。
GitHub版との違いを表にまとめると、次のようになります。
| 項目 | GitHub版(14-1〜14-12) | CodeCommit版(この節) |
|---|---|---|
| ソース置き場 | GitHub | CodeCommit |
| AWSとの接続 | CodeConnections(GitHub App) | 不要(IAMで完結) |
| 認証 | GitHubアカウント | IAM |
| 変更の検知方法 | CodeConnections経由 | EventBridge(CodeCommitのイベント) |
| ビルド | CodeBuild(同じ) | CodeBuild(同じ) |
| デプロイ | CodeDeploy → EC2(同じ) | CodeDeploy → EC2(同じ) |
| 向いている場面 | GitHub中心のチーム開発 | AWS側に寄せたい、IAM統制や監査を重視する場合 |
14-14. CodeCommitリポジトリを作ってpushする
まず、AWS側にCodeCommitリポジトリを作ります。コンソールで作る場合は、「CodeCommit」→「リポジトリ」→「リポジトリを作成」を開き、リポジトリ名を aws-code-series-handson にします。暗号化やタグは、今回はデフォルトのままで構いません。
CLIで作る場合は、次のコマンドです。
aws codecommit create-repository \
--repository-name aws-code-series-handson \
--repository-description "CI/CD handson with CodeCommit"
次に、手元のGitからCodeCommitへpushできるようにします。CodeCommitはGitリポジトリですが、認証はGitHubアカウントではなくIAMで行います。ここでは、AWS CLIの認証情報をGitの認証へ使う「認証情報ヘルパー」方式を使います。
まず、AWS CLIで自分の認証情報が使えることを確認します。
aws sts get-caller-identity
次に、GitがCodeCommitへ接続するときにAWS CLIの認証情報を使うようにします。
# AWS CLIの認証情報を、gitの認証に使う設定
git config --global credential.helper '!aws codecommit credential-helper $@'
git config --global credential.UseHttpPath true
ここまでできたら、GitHub版で使ったローカルリポジトリへ移動します。buildspec.yml、appspec.yml、index.html、scripts/、tests/ が入っているディレクトリです。
# GitHub版で使ったローカルリポジトリへ移動
cd aws-code-series-handson
# 現在のremoteを確認
git remote -v
# CodeCommitを2つ目のremoteとして追加
git remote add codecommit [AWSコンソール上のcodecommitの「URLのクローン」からHTTPSをコピーしたもの]
# mainブランチをCodeCommitへpush
git push codecommit main
これで、同じローカルリポジトリからGitHubにもCodeCommitにもpushできる状態になります。GitHubへ反映したいときは git push origin main、CodeCommitへ反映したいときは git push codecommit main です。
IAM Identity Center(SSO)や一時クレデンシャル中心で使っている場合は、git-remote-codecommit を使う方法もあります。python3 -m pip install --user git-remote-codecommit でインストールし、git remote add codecommit codecommit::ap-northeast-1://aws-code-series-handson のようにremoteを追加します。どちらの方式でも、最終的にCodeCommitへ main ブランチをpushできれば問題ありません。
14-15. CodeCommit版パイプラインを作る
ここから、CodeCommitをSourceにした別パイプラインを作ります。GitHub版のパイプラインを消す必要はありません。入口だけCodeCommitに変えたパイプラインをもう1本作る、と考えると分かりやすいです。
- 「CodePipeline」→「パイプライン」→「パイプラインを作成する」を開く
- 作成オプションで「カスタムパイプラインを構築する」を選ぶ
- パイプライン名を
code-series-codecommit-pipelineにする - 実行モードは「キュー」を選ぶ
- サービスロールは「新しいサービスロール」を選ぶ
- ソースステージで、プロバイダーに「AWS CodeCommit」を選ぶ
- リポジトリ名に
aws-code-series-handson、ブランチ名にmainを指定する - 「ソースの変更を自動的に検出する EventBridge ルールを作成」を有効化
- ビルドステージで「その他のビルドプロバイダー」から「AWS CodeBuild」を選び、GitHub版で作った
code-series-demo-buildを指定する - テストステージが表示された場合はスキップする
- デプロイステージで「AWS CodeDeploy」を選び、アプリケーション
code-series-demo-app、デプロイグループcode-series-demo-dgを指定する - 内容を確認して「パイプラインを作成する」
ビルドステージとデプロイステージは、GitHub版と同じです。CodeBuildはSourceステージから渡されたArtifactを受け取り、buildspec.yml を実行します。CodeDeployも同じデプロイグループを使うので、反映先はGitHub版と同じEC2です。
CodeCommitの変更検出にはEventBridgeが使われます。コンソールからCodeCommitをSourceにしてパイプラインを作ると、パイプラインを起動するためのEventBridgeルールと起動用ロールは自動で用意されます。ここでは手作業でEventBridgeルールを書く必要はありません。便利ですね。
14-16. 動作確認
ここまで組めたら、index.html を少し書き換えてCodeCommitへpushしてみます。EventBridgeがコミットを検知してCodePipelineが起動し、Source → Build → Deployと、GitHub版と同じ流れが走れば成功です。
# index.htmlを少し編集してからコミット
git add index.html
git commit -m "Update index for CodeCommit pipeline"
# CodeCommitへpush
git push codecommit main
# パイプラインの状態を確認
aws codepipeline get-pipeline-state \
--name code-series-codecommit-pipeline \
--query 'stageStates[*].{Stage:stageName,Status:latestExecution.status}'
CodeCommit版も同じEC2へデプロイするため、GitHub版とCodeCommit版の両方を続けて実行すると、最後に成功したパイプラインの内容がWebページに表示されます。これは正常な動きです。
GitHub版と見比べてみると、「ソースの入口を差し替えるだけで、ビルドからデプロイまではそっくり使い回せる」ことが実感できるはずです。CI/CDの本体は、入口がGitHubでもCodeCommitでも変わらない、というのがよく分かる構成になっています。
14-17. クリーンアップ
検証が終わったら、課金され続けないように作ったものを順番に消します。GitHub版だけで終える場合も、CodeCommit版まで試す場合も、このタイミングでまとめて片付けます。
CodePipeline(GitHub版、CodeCommit版)
CodeDeployデプロイグループ
CodeDeployアプリケーション
CodeBuildプロジェクト
EC2インスタンス
セキュリティグループ
S3バケット内のオブジェクト、バージョン、削除マーカー
S3バケット(他のパイプラインと共有していない場合のみ)
CloudWatch Logsロググループ
CodeCommitリポジトリ
CodeConnections接続
CodeCommit用EventBridgeルール
EventBridge起動用IAMロール
CodePipeline、CodeBuildの自動作成IAMロール
IAMインスタンスプロファイル
IAMロール
EBSボリューム、Elastic IP、スナップショット
CodeCommit版まで試した場合は、追加で次の2つもCLIから削除できます。
# CodeCommit版パイプラインを削除
aws codepipeline delete-pipeline \
--name code-series-codecommit-pipeline
# CodeCommitリポジトリを削除
aws codecommit delete-repository --repository-name aws-code-series-handson
注意したい点が3つあります。CodePipelineを削除しても、CodeDeploy、CodeBuild、S3、IAMロールなどの関連リソースは自動では削除されません。コンソールが作成したS3アーティファクトバケットは、他のパイプラインと共有されている場合があるため、共有していないことを確認してから削除します。EC2インスタンスを終了するとインスタンス料金は止まりますが、EBSボリューム、Elastic IP、スナップショット、CloudWatch Logsなどが残っていないかも確認してください。
15. セキュリティとIAM設計
15-1. CI/CDのセキュリティレイヤー
CI/CDは本番環境へ変更を届ける経路なので、アプリケーション本体と同じくらい強く守る必要があります。
15-2. サービスロール設計
| ロール | 必要な権限 | 避ける権限 |
|---|---|---|
| CodeBuild | S3、CloudWatch Logs、必要時ECR | 管理者権限、不要なEC2操作 |
| CodeDeploy | EC2タグ参照、ELB、Auto Scaling、CloudWatchアラーム | EC2作成や削除 |
| CodePipeline | CodeBuild起動、CodeDeploy起動、S3、CodeConnections | 直接のサーバー操作 |
| EC2 | S3リビジョン取得、SSM通信 | 管理者権限 |
EC2やオンプレミスへデプロイする場合、S3からリビジョンを取得する主体はEC2上のCodeDeploy Agentです。そのため、S3読取権限は主にEC2インスタンスプロファイル側へ付けます。CodeDeployサービスロールは、対象EC2のタグ参照、Auto Scaling、ロードバランサー、CloudWatchアラームなど、デプロイを制御するための権限を担当します。
最初は「とにかく動かしたい」と、つい広めの権限を付けてしまいがちです。それ自体は悪くありませんが、動作確認が済んだら、本当に必要な権限だけに絞り込む作業(最小権限の原則と呼びます)を必ずやっておきましょう。広すぎる権限は、万が一漏れたときの被害を大きくしてしまうからです。
15-3. CodeBuild用カスタムポリシー例
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CreateLogGroup",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup"
],
"Resource": "*"
},
{
"Sid": "WriteCloudWatchLogs",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/codebuild/code-series-demo-build:*"
},
{
"Sid": "S3ArtifactBucketLocation",
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::my-artifact-bucket"
},
{
"Sid": "S3Artifacts",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-artifact-bucket/*"
}
]
}
ECRへpushする場合は、ecr:GetAuthorizationToken、ecr:BatchCheckLayerAvailability、ecr:InitiateLayerUpload、ecr:UploadLayerPart、ecr:CompleteLayerUpload、ecr:PutImage などを追加します。
15-4. シークレット管理
| 方法 | 向いている用途 | 注意点 |
|---|---|---|
| 平文環境変数 | 非機密の設定値 | シークレットには使わない |
| SSM Parameter Store | APIキー、環境別設定 | SecureStringを使う |
| Secrets Manager | DB認証情報 | コストとローテーション設計を考える |
| KMS | 暗号鍵管理 | キーポリシーを厳密に設計 |
CI/CDのログは、思っているよりずっと長く残ります。だからこそ、ビルドログにシークレットを出力しない、失敗時のデバッグ出力にもうっかり混ぜない、この2点は徹底しておきましょう。
15-5. アーティファクトバケットの暗号化
aws s3api put-bucket-encryption \
--bucket my-artifact-bucket \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms"
},
"BucketKeyEnabled": true
}]
}'
暗号化は「保存時の保護」です。HTTPS強制やパブリックアクセス防止は別の設定なので、あわせて入れます。同一アカウント内の基本構成では、キーIDを省略してS3用のAWS管理キーを使えます。クロスアカウントパイプラインでは、AWS管理キーを他アカウントへ共有できないため、カスタマー管理KMSキーを指定し、キーポリシーとIAMの両方で利用主体を許可します。
aws s3api put-public-access-block \
--bucket my-artifact-bucket \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
HTTPアクセスを拒否するには、バケットポリシーで aws:SecureTransport が false のリクエストを拒否します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-artifact-bucket",
"arn:aws:s3:::my-artifact-bucket/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
aws s3api put-bucket-policy \
--bucket my-artifact-bucket \
--policy file://deny-insecure-transport.json
put-bucket-policy は既存のバケットポリシーを置き換えます。すでにポリシーがある場合は、HTTPS拒否のStatementだけを単独で反映せず、既存内容へマージしてから適用してください。
16. コスト、監視、ログ運用
16-1. コスト最適化
| 対象 | 最適化 |
|---|---|
| CodeBuild | キャッシュ、適切なコンピュートサイズ、タイムアウト設定 |
| CodePipeline V2 | ブランチ、タグ、パスフィルタで不要実行を削減 |
| CodeDeploy | 検証環境のEC2を必要時だけ起動 |
| S3 | 古いアーティファクトをライフサイクルで削除 |
| CloudWatch Logs | 保持期間を設定 |
| KMS | キー数を増やしすぎない |
V2パイプラインでは、無駄な実行を減らすことが、そのままコスト削減につながります。たとえば docs/** の更新やREADMEの修正だけでは本番パイプラインを動かさないようにしておくと、地味ですが確実にコスト削減につながります。
16-2. 監視すべきメトリクス・イベント・実行状態
| サービス | 確認するもの | 種類 |
|---|---|---|
| CodeBuild | ビルド失敗数、ビルド時間、キュー待ち時間 | CloudWatchメトリクス |
| CodeDeploy | デプロイ失敗、停止、ロールバック | 実行状態、EventBridgeイベント |
| CodePipeline | パイプライン失敗、ステージ失敗、承認待ち | 実行状態、EventBridgeイベント |
| EC2、ALB | 5xx、レイテンシ、ターゲットヘルス | CloudWatchメトリクス |
| S3 | アーティファクトバケットの容量 | ストレージメトリクス |
本番向けには、CodePipeline失敗1回、CodeDeploy失敗1回でも通知対象にします。
16-3. ログとイベントの確認先
| サービス | 確認先 |
|---|---|
| CodeBuild | CloudWatch Logs /aws/codebuild/<project>
|
| CodeDeploy Agent | /var/log/aws/codedeploy-agent/ |
| CodeDeploy deployment | /opt/codedeploy-agent/deployment-root/ |
| CodePipeline | CloudTrail、コンソール履歴、EventBridgeイベント |
CloudWatch Logs Insightsでは、失敗ログを検索できます。
fields @timestamp, @message
| filter @message like /ERROR|FAIL|Exception/
| sort @timestamp desc
| limit 50
16-4. コスト監視
AWS Budgetsで月額予算を作っておき、CodeBuild、CodePipeline、S3、CloudWatch Logs、KMSあたりの費用の増え方を確認します。特にハンズオンで作った環境は、EC2やS3バケット、CloudWatch Logs、ECRのイメージなどを消し忘れたまま放置してしまうかもしれません。検証が終わったら、第14章のクリーンアップ手順にそって忘れずに片付けましょう。
17. トラブルシューティング
うまく動かないときは、あわてず「どのサービスで」「どんなメッセージを出して」止まっているのかを切り分けるのが、解決への近道です。ここでは、代表的なエラーと確認先を、サービスごとに表にまとめておきます。困ったときの逆引きとして使ってください。
17-1. CodeBuild
| エラー | 代表的な原因 | 対処 |
|---|---|---|
DOWNLOAD_SOURCE_FAILED |
GitHub接続や権限の問題 | CodeConnectionsの状態を確認 |
CLIENT_ERROR |
buildspec.ymlの構文ミス | YAML、インデント、タブを確認 |
BUILD_FAILED |
テスト失敗や依存関係不足 | CloudWatch Logsを確認 |
UPLOAD_ARTIFACTS_FAILED |
S3権限不足 | CodeBuildロールのS3権限を確認 |
| Docker daemonエラー | VPC構成、特権モード、Dockerデーモン起動、ビルドイメージの不一致 | VPC設定、特権モード、マネージドイメージ、ログを確認 |
| メモリ不足 | コンピュートサイズ不足 | サイズアップやテスト分割 |
17-2. CodeDeploy
| エラー | 代表的な原因 | 対処 |
|---|---|---|
| Agent未応答 | Agent停止、未インストール |
systemctl status codedeploy-agent を確認 |
ScriptFailed |
Hookスクリプト失敗 | deployment-root配下のログを確認 |
ScriptTimedOut |
timeout不足、処理停止 | timeout延長、スクリプト修正 |
HEALTH_CONSTRAINTS |
正常インスタンス不足 | デプロイ設定とヘルスチェックを確認 |
| S3 403 | EC2ロール、バケットポリシー、KMS、VPCエンドポイントポリシーの不足 | S3読取権限と関連ポリシーを確認 |
ApplicationStop 失敗 |
前回正常リビジョン側のスクリプト失敗 | 前回リビジョンのAppSpecとAgentログを確認 |
17-3. CodePipeline
| エラー | 代表的な原因 | 対処 |
|---|---|---|
| Source failed | GitHub接続やリポジトリ権限 | CodeConnectionsを再確認 |
| Build failed | CodeBuild側の失敗 | CodeBuildログを確認 |
| Deploy failed | appspec、Agent、権限問題 | CodeDeployログを確認 |
| Insufficient permissions | サービスロール不足 | CodePipelineロールを確認 |
| Action configuration error | アクション設定ミス | Provider、Artifact、Regionを確認 |
17-4. よく使うCLI
# Deployステージへの遷移を止める
aws codepipeline disable-stage-transition \
--pipeline-name my-pipeline \
--stage-name Deploy \
--transition-type Inbound \
--reason "メンテナンス中"
# 遷移を戻す
aws codepipeline enable-stage-transition \
--pipeline-name my-pipeline \
--stage-name Deploy \
--transition-type Inbound
# 特定コミットで再実行
aws codepipeline start-pipeline-execution \
--name my-pipeline \
--source-revisions actionName=Source,revisionType=COMMIT_ID,revisionValue=abc1234
18. 設計パターンとチーム運用
18-1. パイプライン設計パターン
| パターン | 向いているチーム |
|---|---|
| シングルブランチ | 個人開発、小規模検証 |
| ブランチベース | 一般的なチーム開発 |
| マルチアカウント | 本番権限を分けたい組織 |
| マイクロサービス | サービスごとに独立リリースしたい組織 |
おすすめは、ブランチベースから始めることです。
feature/*
Pull Requestでテストのみ
develop
dev環境へ自動デプロイ
release/*
itst環境へデプロイ
stg環境へデプロイ
main
手動承認後にprd環境へデプロイ
18-2. チーム運用ルール
| ルール | 内容 |
|---|---|
| main保護 | 直接push禁止、PR必須 |
| レビュー | 最低1名以上の承認 |
| テスト | 必須チェックを通過しないとマージ不可 |
| 本番承認 | CodePipelineで手動承認 |
| ロールバック | 手順書と担当者を決める |
| 命名規則 | <env>-<service>-<purpose>-pipeline |
| タグ付け |
Project、Environment、Owner、CostCenter
|
18-3. 障害対応フロー
アラート検知
影響範囲確認
原因特定
ロールバックまたはパイプライン停止
暫定対応
恒久対応
ポストモーテム
パイプラインの失敗は、ただ直すだけでなく、検知、通知、切り戻し、再発防止まで含めて運用設計します。
おわりに
ここまでお読みいただきありがとうございます。
AWS Codeシリーズは、単体ではそれぞれ小さなサービスに見えます。CodeCommitはソース管理、CodeBuildはビルド、CodeDeployはデプロイ、CodePipelineはつなぎ役です。
ただ、実際の価値はそれらを組み合わせたときに出ます。pushをきっかけにテストが走り、成果物が保存され、ステージングに反映され、承認後に本番へ出て、失敗時には自動で止まり、ログから原因を追える。ここまでそろって初めて、CI/CDは「便利な自動化」から「安全にリリースする仕組み」になります。
2026年時点では、CodeCommitが新規利用可能に戻ったことで、AWSの中だけで完結させる選択肢も復活しました。一方で、GitHubやGitLabを軸にしつつ、CodeBuild、CodeDeploy、CodePipelineをAWS側の実行基盤として使う構成も、今なお有力です。
大切なのは、どのサービスを使うかより、変更を小さく流し、検証を自動化し、承認とロールバックを設計し、失敗から学べる状態にしておくことです。
ではまた、お会いしましょう。
参考リンク
AWS Codeシリーズ公式ドキュメント
- AWS デベロッパーツール - AWS
- AWS CodeCommit - AWS
- AWS CodeCommit ユーザーガイドのドキュメント履歴 - AWS
- AWS CodeCommit の今後について - AWS Blog
- AWS CodeBuild ユーザーガイド - AWS
- AWS CodeBuild の料金 - AWS
- CodeBuild のビルド仕様に関するリファレンス - AWS
- CodeBuild のビルドキャッシュ - AWS
- CodeBuild のバッチビルド - AWS
- CodeBuild のテストレポート - AWS
- AWS CodeDeploy ユーザーガイド - AWS
- CodeDeploy AppSpec ファイル - AWS
- CodeDeploy AppSpec ファイル構造 - AWS
- CodeDeploy のデプロイ設定 - AWS
- AWS CodePipeline ユーザーガイド - AWS
- CodePipeline のパイプラインタイプ - AWS
- CodePipeline のトリガーとフィルタリング - AWS
- AWS CodePipeline の料金 - AWS

































