0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS Codeシリーズ】CI/CDの基礎から本番運用まで一気に整理して、AWS Codeシリーズをまとめてみた

0
Posted at

AWS Codeシリーズ記事サムネイル

はじめに

こんばんは、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連携の両方を選べる形で書いています。

この記事で扱う範囲

AWS Codeシリーズで扱う範囲の全体図

目次

  1. CI/CDの基本
  2. AWS Codeシリーズの全体像
  3. ソースコード管理とCodeCommitのいま
  4. GitHubとCodeConnectionsの準備
  5. CodeBuildの基礎
  6. buildspec.ymlの実践
  7. CodeDeployの基礎
  8. EC2へのデプロイ
  9. デプロイ戦略とロールバック
  10. ECSとLambdaへのカナリアデプロイ
  11. CodePipelineの基礎
  12. CodePipeline V2の実践機能
  13. 高度なパイプライン設計
  14. 【ハンズオン】ゼロから作るCI/CDパイプライン
  15. セキュリティとIAM設計
  16. コスト、監視、ログ運用
  17. トラブルシューティング
  18. 設計パターンとチーム運用

1. CI/CDの基本

CI/CDという言葉を初めて聞くと難しそうに感じますが、やっていることは「コードを書いてから本番で動くまでの作業を、できるだけ自動化する」ことです。

CodeCommitとEventBridgeを使ったCodePipeline構成図
図で書くとこのようになります。後半のハンズオンセクションで、実際にこの構成を作成しますので、お楽しみに!

この、コードを書いてから本番環境で動かす作業を手でやると、テストを流し忘れたり、サーバーへのファイルのコピーを間違えたり、リリースのたびに誰かが手順書を確認しながら深夜作業をしたり、といったことが起きます。こうした一連の流れを機械に任せて、毎回まったく同じ手順で、速く、安全に回せるようにするのが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 CodeConnectionsを使ったCodePipelineハンズオン構成図

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をインストールします(ここでやらなくても、ハンズオンセクションでスクショを入れつつ、もう一度詳しく解説しますのでご安心ください)。

CodeConnectionsでGitHub接続が利用可能になった画面

この流れで進めると、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

基本的な流れは、ソースを取得して、実行環境(コンテナ)を起動し、installpre_buildbuildpost_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 に従ってコマンドを実行します。

app.py
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)}")
test_app.py
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
requirements.txt
pytest

CodeBuildプロジェクトでは、ソースプロバイダ、GitHub接続、ブランチ、実行環境、サービスロール、Buildspec、アーティファクト、ログ出力を設定します。

5-5. 初回ビルドとログ確認

プロジェクト作成後は、まず手動でビルドを開始します。最初の目的は、アプリケーションが正しく動くことよりも、CodeBuildがソースを取得できるか、buildspec.yml を読めるか、ログをCloudWatch Logsへ出せるかを確認することです。

CodeBuildのビルド成功画面

まだ 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のビルドログ確認画面

失敗したときは、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を実行する」「成果物としてファイルを渡す」という流れになります。

buildspec.yml
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 builddist/ を作り、それを成果物として次の工程へ渡します。

buildspec-typescript.yml
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
ビルド中に使う環境変数を定義する場所です。上の例では ENVIRONMENTdev という値を入れています。APIキーやパスワードのような秘密の値は、ここに直接書かず、後述の安全な方法で渡します。

phases(ここが本体)
実際に動かすコマンドを、4つの段階(フェーズ)に分けて書く場所です。上から順番に実行され、途中のコマンドが1つでも失敗すると、その時点でビルドは止まります。「いつ動くか」と「どこに何を書くか」の目安は次のとおりです。

フェーズ いつ実行されるか よく書く内容
install 最初。実行環境を整える段階 言語ランタイムの指定、ツールや依存ライブラリのインストール
pre_build ビルドに入る直前の準備 テストの実行、ECRへのログイン、変数の組み立て
build メインの処理 コンパイル、Dockerイメージのビルドなど中心的な作業
post_build ビルドが終わったあとの仕上げ 成果物のアップロード、イメージのpush、完了ログ出力

この4分割は「絶対にこう書かなければいけない」という厳密な決まりではなく、読みやすさのための目安です。極端に言えば、全部 build に書いても動くには動きます。それでも、install で準備、pre_build でテスト、build で本処理、post_build で後片付け、と役割ごとに分けておくと、失敗したときにどの段階でコケたのかがログから一目でわかります。なお、install の中の runtime-versionspython: 3.12 のように書くと、指定したバージョンの言語をCodeBuild側が用意してくれます。

artifacts
ビルドが終わったあと、どのファイルを成果物(アーティファクト)として次の工程(S3やCodePipeline)へ渡すかを指定します。files:'**/*' は「すべてのファイルを渡す」という意味です。ここで渡したものが、あとでCodeDeployがデプロイする中身になります。

最後の finally は、同じフェーズの commands が途中で失敗しても必ず実行されるブロックです。通常コマンドが失敗するとそのフェーズは失敗扱いになりますが、finally は実行されます。ログの収集や一時ファイルの片付けなど、「成功しても失敗してもやっておきたいこと」を書くのに向いています。

6-2. 環境変数とシークレット

buildspec.yml では、ビルド中に使う値を環境変数として渡せます。ただし、何でも env.variables に書けばよいわけではありません。

たとえば、ENVIRONMENT=devAPI_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後続アクションへ渡す値
buildspec.yml
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内で使えるようにしています。

ビルド中は、普通の環境変数と同じように参照できます。

buildspec.yml
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ファイル名に入れておくと、障害調査のときに「この本番環境はどのコミットから作られたのか」をたどりやすくなります。

buildspec.yml
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 に書きます。

buildspec.yml
cache:
  paths:
    - '/root/.cache/pip/**/*'
    - 'node_modules/**/*'
    - '/root/.m2/**/*'

キャッシュは早くするためのものであって、正しさを保証するものではありません。依存関係のバージョンを変えたのに古いキャッシュが使われると、原因が見えにくい不具合になります。package-lock.jsonrequirements.txtpom.xml などのロックファイルをきちんと管理し、必要に応じてキャッシュを削除して切り分ける運用も用意しておくと安心です。

初心者の方向けですが、まずはキャッシュなしで動かし、ビルドが安定してからキャッシュを入れるのが安全です。最初から高速化を狙うより、まずは「毎回同じ結果になる」状態を作るほうが大事です。

6-5. DockerイメージのビルドとECRプッシュ

ECSやEKSへデプロイする場合は、CodeBuildでDockerイメージを作り、ECRへpushする構成がよく使われます。非VPCビルドではDockerデーモンが有効なマネージドイメージを使えますが、VPC内のビルドでDockerコンテナを使う場合やDockerレイヤーキャッシュを使う場合は、特権モードを有効にします。特権モードは強い権限なので、必要なプロジェクトだけで使うのが安全です。

buildspec.yml
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へ絞れます。

codebuild-ecr-policy.json
{
  "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を出力します。

buildspec.yml
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_CREATEDPULL_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

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スクリプトは次のような最小構成で動かせます。

scripts/stop_server.sh
#!/bin/bash
# ApplicationStop: 2回目以降のデプロイで、前回リビジョン側のスクリプトとして実行される
if systemctl is-active --quiet httpd; then
  systemctl stop httpd
fi
exit 0
scripts/install_dependencies.sh
#!/bin/bash
# BeforeInstall: Apacheが入っていなければインストールする
if ! rpm -q httpd >/dev/null 2>&1; then
  dnf install -y httpd
fi
scripts/after_install.sh
#!/bin/bash
# AfterInstall: 配置後の権限を整える
chown -R apache:apache /var/www/html
chmod -R 755 /var/www/html
scripts/start_server.sh
#!/bin/bash
# ApplicationStart: Apacheを起動する
systemctl start httpd
systemctl enable httpd
scripts/validate_service.sh
#!/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

この一連の決まった流れを「ライフサイクルイベント」と呼びます。上から順に、次のように進みます。

  1. ApplicationStop で、今動いているアプリを止める
  2. DownloadBundle で、新しい成果物をサーバーへ取り込む
  3. BeforeInstall で、ファイルを置く前の準備(バックアップなど)を行う
  4. Install で、成果物のファイルを所定の場所へ配置する
  5. AfterInstall で、置いたあとの調整(権限変更や設定ファイルの書き換えなど)を行う
  6. ApplicationStart で、新しいアプリを起動する
  7. ValidateService で、きちんと動いているかを最終確認する

このうち DownloadBundleInstall の2つはCodeDeployが自動でやってくれるので、私たちが中身を書く必要はありません。それ以外のイベントに対して、appspec.ymlのHookでスクリプトを割り当てていくことになります。

なお、ApplicationStop は少し特殊です。初回デプロイでは、まだ前回の正常リビジョンがないため実行されません。2回目以降は、今回のリビジョンではなく、前回正常にデプロイされたリビジョンのAppSpecとスクリプトが使われます。

ここで、第6章のbuildspec.ymlと混同しないようにまとめておきます。buildspecの installpre_buildビルドの段階 (CodeBuildが、まだサーバーへ配る前のソースをビルドするとき)に動くものです。一方、appspecの BeforeInstallAfterInstallデプロイの段階 (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-webEnv=dev sample-web-dev-dg
itst App=sample-webEnv=itst sample-web-itst-dg
stg App=sample-webEnv=stg sample-web-stg-dg
prd App=sample-webEnv=prd sample-web-prd-dg

タグの値が1文字でも違うと、CodeDeployは対象インスタンスを見つけられません。最初は Name タグだけで進めてもよいですが、実務では AppEnv を分けるほうが、環境を増やしたときに管理しやすくなります。

なお、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
scripts/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メトリクスでは LoadBalancerTargetGroup のディメンションで監視対象を絞り、そのアラームをCodeDeployデプロイグループへ関連付け、さらに DEPLOYMENT_STOP_ON_ALARM を自動ロールバック対象に入れます。

Blue/Greenの場合は、切り替えが終わっても旧環境(Blue)をすぐには消さず、しばらく残しておきましょう。そうしておけば、切り替えたあとで問題が見つかっても、入り口を旧環境へ戻すだけですぐに元の状態へやり直せます。

10. ECSとLambdaへのカナリアデプロイ

この章のタイトルにある「カナリア(Canary)」という言葉を、先に説明しておきます。由来はこの可愛らしい小鳥です。

カナリアイメージ.png

昔、炭鉱で働く人たちは、有毒ガスをいち早く察知するためにカナリアを連れて坑道に入りました。カナリアは人間より先にガスに反応するので、「カナリアが弱ったら危ない」という早期警報の役割を果たしていたのです。デプロイの「カナリアリリース」も発想は同じで、新しいバージョンをいきなり全ユーザーに出すのではなく、まずは一部のユーザー(たとえば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 では、どのタスク定義へ切り替えるか、どのコンテナ名とポートをロードバランサーへ登録するかを指定します。

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 編集中の最新コード
バージョン ある時点の関数コードを固定したもの
エイリアス liveprd のような名前で特定バージョンを指す入口

本番では、利用者が直接バージョン番号を呼ぶのではなく、live のようなエイリアスを呼ぶ構成にします。CodeDeployは、そのエイリアスが向いている先をバージョン1からバージョン2へ少しずつ動かします。

appspec.yml
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が長すぎると、デプロイの切り替えそのものが遅くなり、障害時の切り戻し判断も遅れます。

hook_function.py
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の設定画面で SourceOutputBuildOutput のような名前が出てきたら、「次のステージへ渡す荷物の名前」だと考えると分かりやすいです。

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などの接続ソースに対して、ブランチ、タグ、ファイルパス、プルリクエスト条件で起動を制御できます。

フィルタ
ブランチ mainrelease/*
タグ 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 を使います。

buildspec.yml
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 へ渡すと失敗するため、上の例では jqmetadata を除いています。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. 複数ソースと複数アーティファクト

フロントエンドとバックエンド、アプリケーションとインフラ定義を別リポジトリに分ける場合、複数ソースアクションを使います。

buildspec.yml
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-artifactcfn-artifact という識別子は、CodeBuildプロジェクト側のsecondary artifact設定と一致させる必要があります。これらの名前は後続アクションからも参照するため、わかりやすく付けます。

13-4. CloudFormation連携

CodePipelineはCloudFormationデプロイアクションを使えます。

アクションモード 用途
CREATE_UPDATE スタックを直接作成、更新
DELETE_ONLY スタック削除
REPLACE_ON_FAILURE 失敗スタックの置換
CHANGE_SET_REPLACE 変更セットがなければ作成し、既存なら削除して作り直す
CHANGE_SET_EXECUTE 変更セット実行

安全な本番反映では、変更セット作成、手動承認、変更セット実行の順にします。

template.yaml
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呼び出し、事前検証に使えます。

lambda_function.py
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とAWS CodeConnectionsを使ったCodePipelineハンズオン構成図

開発者が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ページ本体です。

index.html
<!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>
css/style.css
body {
  font-family: sans-serif;
  text-align: center;
  margin-top: 80px;
  background-color: #f4f6f8;
}
js/app.js
console.log("AWS Code series handson loaded");

次に、CodeBuildで走らせるテストです。ここでは「index.html にバージョン表記が入っているか」を確認するだけの、ごく簡単なシェルスクリプトにしておきます(本来はアプリのユニットテストなどを置く場所です)。

tests/test_app.sh
#!/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で説明します)。

scripts/install_dependencies.sh
#!/bin/bash
# BeforeInstall: Apacheが入っていなければインストールする
if ! systemctl list-unit-files | grep -q httpd; then
  dnf install -y httpd
fi
scripts/stop_server.sh
#!/bin/bash
# ApplicationStop: 2回目以降のデプロイでApacheを止める
if systemctl is-active --quiet httpd; then
  systemctl stop httpd
fi
scripts/start_server.sh
#!/bin/bash
# ApplicationStart: Apacheを起動し、再起動後も自動で立ち上がるようにする
systemctl start httpd
systemctl enable httpd
scripts/validate_service.sh
#!/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.ymlappspec.yml は重要なので、次の2項に分けて説明します。ここまでのファイルがそろったら、いったんGitHubへpushしておきましょう。

GitHubで空リポジトリを作成する画面

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に必要な制御ファイルだけを成果物として次へ渡す」という構成です。

buildspec.yml
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つのスクリプトを、デプロイの各タイミングへ割り当てています。

appspec.yml
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自身 AmazonEC2RoleforAWSCodeDeployAmazonSSMManagedInstanceCore
CodeDeployサービスロール CodeDeploy AWSCodeDeployRole
CodeBuildサービスロール CodeBuild 14-11のBuildステージで自動生成
CodePipelineサービスロール CodePipeline 14-11のウィザードで自動生成

EC2インスタンスロール
EC2用IAMロールのユースケース選択画面
EC2用IAMロールへCodeDeployとSSMのポリシーを付与する画面

  1. コンソールで「IAM」→「ロール」→「ロールを作成」を開く
  2. 信頼されたエンティティタイプで「AWS のサービス」、ユースケースで「EC2」を選んで次へ
  3. 許可ポリシーで AmazonEC2RoleforAWSCodeDeploy(成果物をS3から取得するため)と AmazonSSMManagedInstanceCore(管理・調査用)の2つにチェック
  4. ロール名を code-series-ec2-role として作成

CodeDeployサービスロール
CodeDeploy用IAMロールのユースケース選択画面
CodeDeployサービスロールの作成確認画面

  1. 同じく「ロールを作成」を開く
  2. ユースケースで「CodeDeploy」を選ぶ(これを選ぶと、CodeDeployを信頼する設定が自動で入ります)
  3. 許可ポリシーは AWSCodeDeployRole が自動で選ばれるので、そのまま次へ
  4. ロール名を 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
EC2起動時のAMIとインスタンスタイプ設定


4.キーペアは「キーペアなしで続行」で進める
EC2起動時のキーペアなし設定

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


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


7.「高度な詳細」を開き、「IAMインスタンスプロフィール」で `code-series-ec2-role` を選ぶ
8.同じく「高度な詳細」の「ユーザーデータ」に、次のスクリプトを貼り付ける

EC2ユーザーデータ入力画面

#!/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

EC2セキュリティグループでHTTPを自分のIPに限定する画面

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のテストページ)。

Apacheテストページの表示結果

14-9. GitHubと接続する(CodeConnections)

CodeConnectionsの接続作成画面

CodePipelineがGitHubのリポジトリを読めるように、接続を作ります(第4章の復習です)。すでに第4章で接続を作っていれば、それをそのまま使えるので、この項は飛ばして構いません。

  1. 「デベロッパー用ツール」と検索→「設定」→「接続」→「接続を作成」を開く
  2. プロバイダーで「GitHub」を選び、接続名を入力する。例として github-connection のような名前にします
  3. 「アプリインストール - オプショナル」で「新しいアプリをインストールする」をクリック
  4. GitHub側の画面で、AWS Connector for GitHub by AWS CodeConnectionsをインストールし、対象リポジトリへのアクセスを許可
  5. AWSマネコンに戻ると、アプリインストール欄にインストールIDが自動で入るので、その状態で接続を作成
  6. 接続のステータスが「利用可能(Available)」になればOK

GitHub Appインストール後のCodeConnections接続画面

この手順で作ると、CodePipeline作成画面で接続とリポジトリを選びやすくなります。接続は選べるのにリポジトリが出ない場合は、アプリインストール時に対象リポジトリを許可したか、main ブランチと初回コミットが存在するかを確認してください。

14-10. CodeDeployアプリとデプロイグループを作る

EC2へ配るためのCodeDeploy側を用意します。CodeDeployは「アプリケーション」と「デプロイグループ」の2階建てです。

アプリケーションの作成

  1. 「CodeDeploy」→「アプリケーション」→「アプリケーションの作成」を開く
  2. アプリケーション名を code-series-demo-app、コンピューティングプラットフォームを「EC2/オンプレミス」にして作成

CodeDeployアプリケーション作成画面

デプロイグループの作成

  1. 作ったアプリケーションを開き、「デプロイグループの作成」を押す
  2. デプロイグループ名を code-series-demo-dg にする
  3. サービスロールに、14-7で作った code-series-codedeploy-role を指定
  4. デプロイタイプは「インプレース」
  5. 環境設定で「Amazon EC2 インスタンス」を選び、タグで キー Name = 値 CodeDeploy-Demo を指定(14-8で付けたタグです)
  6. デプロイ設定は CodeDeployDefault.AllAtOnce(インスタンスが1台なので、これでOK)
  7. 「ロードバランシングを有効にする」はオフにしておく
  8. 「デプロイグループの作成」をクリック

CodeDeployデプロイグループ作成画面

ここで指定するタグが、EC2のNameタグと1文字でも違うと「デプロイ対象が見つからない」となります。CodeDeploy-Demo を正確に合わせてください。

14-11. CodePipelineで全体をつなぐ

いよいよ、ここまで作ったものを1本のパイプラインにつなぎます。

CodeBuildプロジェクトは、このCodePipeline作成画面のBuildステージで作ります。CodeBuild単体の作成画面からGitHubリポジトリを選ぶ必要はありません。今回の構成では、GitHubを読むのはCodePipelineのSourceステージで、CodeBuildはSourceステージから渡されたArtifactを受け取って buildspec.yml を実行します。

CodePipeline作成オプション選択画面

  1. 「CodePipeline」→「パイプライン」→「パイプラインを作成する」を開く
  2. 作成オプションで「カスタムパイプラインを構築する」を選ぶ
  3. パイプライン名を code-series-demo-pipeline にする
  4. 実行モードは「キュー」を選ぶ
  5. サービスロールは「新しいサービスロール」を選ぶ(CodePipeline用ロールが自動生成されます)
  6. ソースステージで、プロバイダーに「GitHub」を選び、14-9の接続・リポジトリ・ブランチ main を指定
  7. ビルドステージで、プロバイダーに「AWS CodeBuild」を選び、「プロジェクトを作成する」から新しいCodeBuildプロジェクトを作る
    • プロジェクト名は code-series-demo-build
    • 環境は「マネージド型イメージ」
    • オペレーティングシステムはAmazon Linux、ランタイムはStandard、イメージは最新版
    • コンピューティングは general1.small
    • サービスロールは「新しいサービスロール」
    • Buildspecは「buildspec ファイルを使用する」
  8. テストステージが表示された場合はスキップする
  9. デプロイステージで、プロバイダーに「AWS CodeDeploy」を選び、アプリケーション code-series-demo-app、デプロイグループ code-series-demo-dg を指定
  10. 内容を確認して「パイプラインを作成する」

CodePipelineパイプライン設定画面

CodePipelineのBuildステージとDeployステージ設定確認画面

作成オプションの画面では、ECRにプッシュECS Fargateにデプロイ などのテンプレートも表示されます。今回はGitHub → CodeBuild → CodeDeploy → EC2の流れを自分で組むので、テンプレートではなく「カスタムパイプラインを構築する」を選びます。

現在のコンソールでは、ここで「パイプラインタイプ V2」を直接選ぶ欄が表示されない場合があります。その場合は、実行モードで「キュー」を選んで進めます。キューや並行の実行モードはV2側の機能なので、V2という名前の選択肢がなくても問題ありません。

途中でテストステージが表示されることがありますが、今回は buildspec.ymlpre_buildtests/test_app.sh を実行しています。そのため、CodePipeline上で別のTestステージを作らず、スキップして進めます。結合テストやブラウザテストを別工程に分けたい場合は、後からTestステージを追加する形で十分です。

CodePipeline作成直後の初回実行結果

作成すると、パイプラインは自動で1回目の実行を始めます。Source → Build → Deploy の順に緑のチェックが付いていけば成功です。途中で失敗したら、まずそのステージのログ(CodeBuildならビルドログ、CodeDeployならデプロイの詳細画面)を開いて、エラーメッセージを読むのが解決の近道です。

14-12. 動作確認

パイプラインが最後まで緑になったら、ブラウザで http://<EC2のパブリックIP>/ を開きます。自分の index.html(「バージョン: v1」と書いたページ)が表示されれば、CI/CDがひととおり通った証拠です。

初回デプロイ後のv1ページ表示結果
こんな感じです。

次に、コードの変更が自動で反映されることを確かめます。index.htmlv1v2 に書き換えます。

index.htmlをv2へ変更した編集画面

変更したら、GitHubへpushしてみてください。

git add index.html
git commit -m "Bump version to v2"
git push origin main

push後に動き出したCodePipeline

しばらくすると、pushをきっかけにパイプラインがひとりでに動き出します。完了後にページを再読み込みして、表示が v2 に変わっていれば、自動デプロイまで成功です。

自動デプロイ後の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はソースを運ぶ役ではなく、あくまで起動のきっかけを作る役割です。

CodeCommitとEventBridgeを使ったCodePipeline構成図

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.ymlappspec.ymlindex.htmlscripts/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

CodeCommitへpushしたターミナル出力

これで、同じローカルリポジトリから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本作る、と考えると分かりやすいです。

CodeCommitソースステージ設定画面

  1. 「CodePipeline」→「パイプライン」→「パイプラインを作成する」を開く
  2. 作成オプションで「カスタムパイプラインを構築する」を選ぶ
  3. パイプライン名を code-series-codecommit-pipeline にする
  4. 実行モードは「キュー」を選ぶ
  5. サービスロールは「新しいサービスロール」を選ぶ
  6. ソースステージで、プロバイダーに「AWS CodeCommit」を選ぶ
  7. リポジトリ名に aws-code-series-handson、ブランチ名に main を指定する
  8. 「ソースの変更を自動的に検出する EventBridge ルールを作成」を有効化
  9. ビルドステージで「その他のビルドプロバイダー」から「AWS CodeBuild」を選び、GitHub版で作った code-series-demo-build を指定する
  10. テストステージが表示された場合はスキップする
  11. デプロイステージで「AWS CodeDeploy」を選び、アプリケーション code-series-demo-app、デプロイグループ code-series-demo-dg を指定する
  12. 内容を確認して「パイプラインを作成する」

CodeCommit版パイプラインのBuildステージとDeployステージ設定確認画面

ビルドステージとデプロイステージは、GitHub版と同じです。CodeBuildはSourceステージから渡されたArtifactを受け取り、buildspec.yml を実行します。CodeDeployも同じデプロイグループを使うので、反映先はGitHub版と同じEC2です。

CodeCommit版パイプラインの作成確認画面

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ページに表示されます。これは正常な動きです。

CodeCommit版パイプラインの自動実行結果

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用カスタムポリシー例

codebuild-policy.json
{
  "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:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr:InitiateLayerUploadecr:UploadLayerPartecr:CompleteLayerUploadecr: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:SecureTransportfalse のリクエストを拒否します。

deny-insecure-transport.json
{
  "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
タグ付け ProjectEnvironmentOwnerCostCenter

18-3. 障害対応フロー

アラート検知
影響範囲確認
原因特定
ロールバックまたはパイプライン停止
暫定対応
恒久対応
ポストモーテム

パイプラインの失敗は、ただ直すだけでなく、検知、通知、切り戻し、再発防止まで含めて運用設計します。

おわりに

ここまでお読みいただきありがとうございます。

AWS Codeシリーズは、単体ではそれぞれ小さなサービスに見えます。CodeCommitはソース管理、CodeBuildはビルド、CodeDeployはデプロイ、CodePipelineはつなぎ役です。

ただ、実際の価値はそれらを組み合わせたときに出ます。pushをきっかけにテストが走り、成果物が保存され、ステージングに反映され、承認後に本番へ出て、失敗時には自動で止まり、ログから原因を追える。ここまでそろって初めて、CI/CDは「便利な自動化」から「安全にリリースする仕組み」になります。

2026年時点では、CodeCommitが新規利用可能に戻ったことで、AWSの中だけで完結させる選択肢も復活しました。一方で、GitHubやGitLabを軸にしつつ、CodeBuild、CodeDeploy、CodePipelineをAWS側の実行基盤として使う構成も、今なお有力です。

大切なのは、どのサービスを使うかより、変更を小さく流し、検証を自動化し、承認とロールバックを設計し、失敗から学べる状態にしておくことです。

ではまた、お会いしましょう。

参考リンク

AWS Codeシリーズ公式ドキュメント

接続、関連サービス、運用

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?