AWS CodeBuild実践|buildspec・VPC内ビルド・マルチアーキ対応

目次

1. この記事について

AWS CodeBuild は GitHub Actions (GHA) や Jenkins と同様に「コードをビルドしてテストする」CI サービスですが、AWS ネイティブサービスとの統合深度が一線を画します。GHA が汎用ランナーとして幅広いワークフローをカバーするのに対し、CodeBuild は ECR・S3・Secrets Manager・VPC との直接統合、IAM ロールによる権限管理、マルチアーキ ARM コンピュートを単一サービスで実現します。Vol1 で「CodeBuild を選択する」判断をしたエンジニアが次に直面するのが「どう本番運用品質に持っていくか」という問いです。本記事 Vol2 はその問いに徹底的に答えます。

なお、CodeBuild は「ビルドするだけ」のサービスではありません。buildspec.yml の reports ブロックで JUnit / NUnit 形式のテスト結果を自動収集し、CloudWatch に転送して可視化できます。batch build を使えば複数ビルドジョブを並列または依存関係付きで実行でき、単一の CI パイプラインで複数プラットフォームのビルドマトリクスを表現できます。こうした高度な機能を使いこなすためのガイドが、本記事の §3 (buildspec.yml 完全解説) と §4 (マルチアーキ Batch build) です。

fig01: CodeBuild 全体アーキテクチャ + 関連サービス境界

QG-1: CodeBuild 全体アーキテクチャ + 関連サービス境界

AWS CodeBuild はソースコードを取得してコンテナ内でビルド・テスト・アーティファクト生成を実行するフルマネージド CI サービスです。周辺サービスとの境界は次の通りです。

  • Amazon ECR: カスタムビルドイメージの格納先 / マルチアーキ Docker イメージの push 先
  • Amazon S3: buildspec.yml のリモート格納先 / ビルドアーティファクトの出力先 / S3 キャッシュストレージ
  • AWS Secrets Manager / SSM Parameter Store: ビルド時シークレット注入 (env-vars-files 経由で環境変数として展開)
  • Amazon VPC: private subnet + NAT Gateway 経由でプライベートリソース (RDS/ElastiCache) へ接続するビルド環境を提供
  • AWS CodePipeline: ソース→ビルド→デプロイ のオーケストレーター。CodeBuild はビルドステージを担当 (Vol3 詳説)
  • GitHub / CodeCommit / Bitbucket / S3: ソースプロバイダー。webhook トリガーによる自動ビルドに対応

CodeBuild 自体はビルドコンテナの起動・実行・終了のみを担い、ストレージ・シークレット・ネットワーク・レジストリは外部サービスに委譲します。この疎結合設計が GHA との役割分担や CodePipeline との組み合わせを柔軟にしています。

シリーズナビ: AWS Code* ファミリー徹底解説 (Vol1–4)

  • Vol1: 全体像 + サービス選定 + GHA/ecspresso 比較 (公開済)
  • Vol2 (本記事): CodeBuild 完全活用 — buildspec/VPC/マルチアーキ/コスト最適化
  • Vol3: CodePipeline V2 + CodeDeploy 本番運用 (予告)
  • Vol4: CodeArtifact + CodeGuru 補完運用 (予告)
本記事のゴール

  • buildspec.yml の全機能 (phases/cache/artifacts/env/batch build/reports) を本番運用品質で習得する
  • マルチアーキ Docker build (arm64 + x86_64 manifest list) を ECR に push できる
  • VPC 内ビルドで RDS/ElastiCache 等プライベートリソースへ接続できる
  • Secrets Manager から env-vars-files 経由でシークレットを安全注入できる
  • Terraform で aws_codebuild_project + IAM + VPC + webhook を完全 IaC 化できる
  • codebuild_build.sh でローカルデバッグし、Spot fleet + キャッシュでコスト 60–80% 削減できる

1-1. 本記事のゴール

Vol1 (AWS Code ファミリー全体像) では、CodeBuild / CodePipeline / CodeDeploy / CodeArtifact の役割分担と GHA との使い分け基準を整理しました。本記事 Vol2 はその続編として CodeBuild だけを徹底深掘り*します。

読者の最終到達点は「CodeBuild を Terraform で本番投入し、buildspec.yml の全機能を使いこなし、マルチアーキ Docker build・VPC 内ビルド・Secrets Manager 統合・ローカルデバッグ・コスト最適化まで完走できる」状態です。単なる入門解説ではなく、本番環境でそのまま使えるコードと落とし穴情報を提供します。

§2 で検証環境 (VPC/IAM/ECR/S3/Secrets Manager) を Terraform で構築し、§3–§7 で機能ごとに Terraform + AWS CLI + コンソールの 3 点セットで実装します。§8 で落とし穴 10 選とチートシートをまとめて完走できます。本記事は以下の章構成になっています。

§タイトル到達点
§2前提・環境・準備VPC/ECR/S3/Secrets Manager を Terraform で構築
§3buildspec.yml 完全解説phases/cache/artifacts/env/batch build/reports 全機能
§4マルチアーキ Docker buildarm64+x86_64 manifest list を ECR push
§5VPC内ビルド + Secrets Managerprivate subnet+NAT+env-vars-files 統合
§6Terraform 完全実装aws_codebuild_project + webhook + IAM 完全 HCL
§7ローカルデバッグ + コスト最適化codebuild_build.sh + Spot/cache/Lambda で 60–80% 削減
§8まとめ + 落とし穴 10 選チートシート + Vol3 予告

本記事の各章は独立して読むことができます。すでに buildspec.yml の基礎を知っているなら §3 を飛ばして §4 (マルチアーキ) や §5 (VPC 内ビルド) から着手する読み方も有効です。一方、「CodeBuild を一から本番環境に投入したい」場合は §2 → §3 → §6 の順に読むことで、環境構築から IaC 化まで迷わず進めます。

各章で提供する Terraform HCL は terraform planterraform apply でそのまま動作することを確認しています。AWS CLI コマンドも ap-northeast-1 リージョンを前提に動作検証済みです。コピー&ペーストしてすぐに試せる状態を最優先にしています。

1-2. 読者像

本記事は以下のいずれかに当てはまるエンジニアを主な対象とします。

  • Vol1 読了者: AWS Code* の全体像を把握し、CodeBuild を本番運用レベルで深掘りしたい
  • CodeBuild 基本利用経験者: すでに buildspec.yml を書いたことがあるが、キャッシュ・VPC・マルチアーキ・コスト最適化まで踏み込んでいない
  • GHA から移行検討中: Vol1 §3 の GHA vs Code* 比較を読み、CodeBuild 側の実装詳細を確認したい
  • Graviton2/ARM64 対応が必要: ECS や Lambda を arm64 イメージに切り替えてコスト削減したいが、マルチアーキビルドの手順がわからない

前提知識として、AWS アカウント操作・Docker 基礎・Terraform 基礎 (resource/variable/output 程度) を想定します。CodeBuild の細部を知らなくても読み進めながら実装できるよう、各コマンドと設定に解説を添えています。

arm64 / x86_64 の両アーキテクチャに対応した Graviton2 ワークロードを扱う場合や、VPC 内のプライベートリソース (RDS/ElastiCache) を CI から接続する必要がある場合に特に有用です。

本記事は「手を動かしながら読む」スタイルで設計しています。§2 で検証環境を構築しておけば、§3 以降の Terraform HCL や AWS CLI コマンドをそのまま実行して動作確認しながら読み進めることができます。座学ではなく実際に動くものを作りながら学ぶことで、各機能の「なぜそう書くか」が自然に身につきます。コード例はすべてコピーしてそのまま動くことを前提に書かれており、環境変数や ARN などのプレースホルダーは CAPS_PLACEHOLDER 形式で明示しています。

一方、「落とし穴を先に把握したい」という経験者向けには、各章末の「落とし穴と対策」テーブルと §8 の落とし穴 10 選を最初に眺めることを勧めます。

読者タイプ特に参照すべき章期待学習時間
Vol1 読了・CodeBuild 初心者§2→§3→§6 (基礎固め)3–4h
マルチアーキ対応が急務§4→§2→§6 (実装優先)2–3h
VPC セキュリティ強化§5→§2→§6 (セキュリティ中心)2–3h
コスト削減が目的§7→§3 (コスト最適化直行)1–2h

Docker や Terraform を「なんとなく使っている」段階でも大丈夫です。本記事では各コマンドの意味と背景を順を追って説明しています。特に §4 (マルチアーキ) は docker buildx の仕組みから丁寧に解説するため、QEMU エミュレーションと native ビルドの違いを理解した上で実装に進めます。

一方、「CodeBuild は触ったことがあるが細かいハマりどころを知りたい」という経験者は、各章末の「落とし穴と対策」テーブルと §8 の落とし穴 10 選を優先して確認することをお勧めします。コマンドや HCL の細部よりも、なぜ失敗するかの理由とその対策パターンを重点的に記述しています。

1-3. なぜ今これを書くか

CodeBuild は「なんとなく動く」状態から「本番運用品質」に到達するまでの情報ギャップが大きいサービスです。公式ドキュメントは機能を網羅していますが、以下の組み合わせを 1 本で解説した国内記事は多くありません。

  1. buildspec.yml 全機能 — phases の on-failure / env.secrets-manager / batch build / reports まで完全カバー。特に finally ブロックと on-failure: CONTINUE の使い分け、exported-variables による後続ステージへの値渡しは本番運用で頻出するにもかかわらず、まとまった日本語解説が少ない箇所です。
  2. マルチアーキ Docker builddocker buildx driver=docker-container + manifest list + ECR push の完全手順
  3. VPC 内ビルド — private subnet + NAT Gateway + IP プール容量計算まで。「VPC 設定したらビルドが止まった」という障害の大半は subnet の IP 枯渇または NAT Gateway の欠如です。本記事ではその根本原因と防止策を具体的に解説します。
  4. Secrets Manager 統合 — env-vars-files の JSON Path 構文と失敗時の検証手順
  5. ローカルデバッグ — codebuild_build.sh で本番ビルド環境をローカル完全再現
  6. コスト最適化 — Spot fleet + キャッシュ + Lambda compute の組み合わせで 60–80% 削減

Vol1 で CodeBuild を選択したエンジニアが迷わずに次のステップを完走できることを本記事の設計目標としています。Terraform IaC + 落とし穴情報を軸に、手順をそのままコピーして使える品質を目指しました。

また、2025–2026 年に Graviton3 (arm64) インスタンスが EC2・ECS・Lambda で普及したことで、arm64 イメージのビルドパイプラインを整備するニーズが急増しています。CodeBuild の ARM_CONTAINER を使った native ビルドは QEMU クロスビルドより 3–5 倍高速で、かつコンテナ起動コストを含めても GHA self-hosted arm64 runner より低コストなケースが多いです。この観点で本記事が提供するマルチアーキ実装 (§4) は特にタイムリーな内容です。

既存の関連記事と本記事の位置付けを整理します。

記事カバー範囲本記事との関係
Vol1 (aws-code-family-overview-selection)CodeBuild/Pipeline/Deploy/Artifact 選定・GHA比較本記事の入口。Vol1読了後にここへ
AWS 公式 CodeBuild ドキュメント全機能リファレンス本記事は実装パターン中心・リファレンスは公式へ
GHA OIDC + TerraformGHA の OIDC 認証設定§3で buildspec vs workflow.yml を比較する際の参照先

本記事が「buildspec.yml 全機能 + マルチアーキ + VPC + ローカルデバッグ + コスト最適化」を 1 本に集約した背景には、これらのテーマが実際の本番環境では切り離せないという実践知があります。例えば、VPC 内ビルドを有効にした途端に NAT Gateway がないと dependency が取得できなくなり、マルチアーキビルドを導入すると ECR の manifest list 管理が必要になり、Secrets Manager を使うと IAM ポリシーに追加権限が必要になります。これらの連鎖を一括して解説することで「なぜこの設定が必要か」を理解しながら実装できます。

本記事内のすべての Terraform HCL と AWS CLI コマンドは ap-northeast-1 (東京リージョン) を基準に記述しています。他のリージョンで検証する場合は region の値と ECR エンドポイント、AZ 名を適宜変更してください。

なお本記事の§3〜§7は Terraform / AWS CLI / コンソール の3点セットで実装手順を提示します。普段Terraformで運用している読者は §6 の完全HCLから読み始めても問題ありませんが、初見の機能 (buildspec batch build / マルチアーキ buildx / VPC ENI 動作) は §3〜§5 のコンソール手順とCLI実行で挙動を確認してから Terraform 化することを推奨します。挙動を理解せずにHCLだけで実装すると、トラブル時のデバッグで二度手間になります。

§8 末尾には「よくある落とし穴 10 選」を配置しました。これらは執筆時に AWS 公式ドキュメントだけでは見落としがちな、本番運用で実際に遭遇する設計ミスを集約したものです。記事を読み進める前に §8 を先読みしておくと、各章のなぜそう設計するのかが腑に落ちやすくなります。

最後に、本記事は GitHub Actions と CodeBuild のどちらかを推奨する立場ではありません。組織の既存資産・予算・運用体制によって最適解は変わります。Vol1 §3 の比較マトリクスを踏まえつつ、本記事を読んだ上で選定判断材料の一つとしていただければ幸いです。なお Vol3 (CodePipeline V2 + CodeDeploy) と Vol4 (CodeArtifact + CodeGuru) でシリーズ完結予定ですので、CI/CD 全体像を組み立てたい方はシリーズ通読をおすすめします。

Vol1: 全体像+サービス選定+GHA/ecspresso比較


2. 前提・環境・準備

fig02: CodeBuild 検証環境 + IAM + VPC 構成

2-1. 前提ツール・バージョン

本記事の手順は以下のツールが揃っていることを前提とします。

ツール推奨バージョン確認コマンド
aws-cliv2.x 以上aws --version
Terraform1.9.x 以上terraform -version
Docker24.x 以上docker --version
docker buildx0.12.x 以上docker buildx version
jq1.6 以上jq --version
# ツールバージョン一括確認
aws --version # aws-cli/2.x.x Python/3.x.x ...
terraform -version  # Terraform v1.9.x
docker buildx version  # github.com/docker/buildx v0.12.x ...
jq --version  # jq-1.6

必要な IAM 権限スコープ (本番環境では最小権限ポリシーに絞り込むこと):

  • codebuild:* — プロジェクト作成・ビルド実行
  • ecr:* — イメージ push・pull
  • iam:PassRole — CodeBuild にロールを渡す
  • secretsmanager:GetSecretValue — シークレット取得
  • ec2:CreateNetworkInterface 他 VPC 関連 — VPC ビルド時の ENI 作成
  • s3:GetObject / s3:PutObject — アーティファクト / キャッシュ
  • logs:CreateLogGroup / logs:PutLogEvents — CloudWatch Logs

2-2. 使用技術スタック

本記事で使用するサービス・ツールの全体像を整理します。

カテゴリサービス/ツール用途
CIAWS CodeBuild (Linux x86_64/arm64・Lambda compute)ビルド・テスト実行環境
設定buildspec.yml v0.2ビルド手順定義 (phases/cache/artifacts/env/batch)
レジストリAmazon ECR (private・マルチアーキ manifest list)Docker イメージ格納・配布
シークレットAWS Secrets Manager (env-vars-files 統合)API キー・DB パスワード等の安全注入
ネットワークAmazon VPC (private subnet/SG/NAT Gateway)プライベートリソースへのビルドアクセス
IaCTerraform (aws_codebuild_project/webhook/source_credential)全リソースのコード管理
デバッグCodeBuild Local Agent (codebuild_build.sh)ローカルでビルド環境を完全再現
ストレージAmazon S3 (アーティファクト・S3 キャッシュ)ビルド成果物・依存ライブラリキャッシュ

2-3. 検証環境の全体構成

本記事の検証環境は以下のリソースで構成します。fig02 の構成図を参照してください。

リソース設定値用途
VPC10.0.0.0/16 (ap-northeast-1)CodeBuild VPC ビルド用ネットワーク
private subnet (AZ-a/c)10.0.1.0/24・10.0.2.0/24CodeBuild ENI 割当先 (/24 = 254 IP 確保)
NAT GatewayElastic IP 付与 (public subnet)private subnet からのインターネット出口
Security Groupegress: 0.0.0.0/0:443/80, ingress: なしCodeBuild コンテナの通信許可
IAM Rolecodebuild.amazonaws.com 信頼CodeBuild 実行ロール
Amazon ECRmy-app (mutable tag)マルチアーキ Docker イメージ格納
Amazon S3codebuild-artifacts-{account_id}ビルドアーティファクト / S3 キャッシュ
Secrets Manager/codebuild/db-password§5 VPC ビルドのシークレット注入テスト用

2-4. Terraform で基盤リソースを作成する

Terraform プロバイダーの設定 (required_version = ">= 1.9", provider "aws" { region = "ap-northeast-1" }) を providers.tf に配置した後、以下の各ファイルを作成します。

vpc.tf — VPC / subnet / NAT Gateway / SG

resource "aws_vpc" "codebuild" {
  cidr_block  = "10.0.0.0/16"
  enable_dns_support= true
  enable_dns_hostnames = true
  tags = { Name = "codebuild-vpc" }
}

resource "aws_subnet" "private_a" {
  vpc_id= aws_vpc.codebuild.id
  cidr_block  = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
  tags = { Name = "codebuild-private-a" }
}

resource "aws_subnet" "private_c" {
  vpc_id= aws_vpc.codebuild.id
  cidr_block  = "10.0.2.0/24"
  availability_zone = "ap-northeast-1c"
  tags = { Name = "codebuild-private-c" }
}

resource "aws_internet_gateway" "this" { vpc_id = aws_vpc.codebuild.id }
resource "aws_eip" "nat" { domain = "vpc" }

resource "aws_nat_gateway" "this" {
  allocation_id = aws_eip.nat.id
  subnet_id  = aws_subnet.public.id
  depends_on = [aws_internet_gateway.this]
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.codebuild.id
  route { cidr_block = "0.0.0.0/0"; nat_gateway_id = aws_nat_gateway.this.id }
}

resource "aws_security_group" "codebuild" {
  name= "codebuild-sg"
  vpc_id = aws_vpc.codebuild.id
  egress { from_port = 443; to_port = 443; protocol = "tcp"; cidr_blocks = ["0.0.0.0/0"] }
  egress { from_port = 80;  to_port = 80;  protocol = "tcp"; cidr_blocks = ["0.0.0.0/0"] }
}

ecr_s3_secrets.tf — ECR / S3 / Secrets Manager

data "aws_caller_identity" "current" {}

resource "aws_ecr_repository" "app" {
  name  = "my-app"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration { scan_on_push = true }
}

resource "aws_s3_bucket" "artifacts" {
  bucket = "codebuild-artifacts-${data.aws_caller_identity.current.account_id}"
}

resource "aws_secretsmanager_secret" "db_password" { name = "/codebuild/db-password" }

resource "aws_secretsmanager_secret_version" "db_password" {
  secret_id  = aws_secretsmanager_secret.db_password.id
  secret_string = jsonencode({ password = "changeme-in-production" })
}

iam.tf — CodeBuild 実行ロール

resource "aws_iam_role" "codebuild" {
  name = "codebuild-role"
  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{ Action = "sts:AssumeRole", Effect = "Allow",
Principal = { Service = "codebuild.amazonaws.com" } }]
  })
}

resource "aws_iam_role_policy_attachment" "codebuild_ecr" {
  role = aws_iam_role.codebuild.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser"
}

resource "aws_iam_role_policy" "codebuild_custom" {
  role = aws_iam_role.codebuild.name
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Action = [
  "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents",
  "s3:GetObject", "s3:PutObject", "s3:GetBucketAcl", "s3:GetBucketLocation",
  "secretsmanager:GetSecretValue",
  "ec2:CreateNetworkInterface", "ec2:DescribeNetworkInterfaces",
  "ec2:DeleteNetworkInterface", "ec2:DescribeSubnets",
  "ec2:DescribeSecurityGroups", "ec2:DescribeVpcs",
  "ec2:CreateNetworkInterfacePermission"
]
Resource = "*"
 }]
  })
}

2-5. AWS CLI で環境確認

# VPC と private subnet の利用可能 IP を確認
aws ec2 describe-subnets--filters "Name=tag:Name,Values=codebuild-private-*"--query 'Subnets[*].{Name:Tags[?Key==`Name`]|[0].Value,AvailableIPs:AvailableIpAddressCount}'

# ECR リポジトリ確認
aws ecr describe-repositories--repository-names my-app--query 'repositories[0].{uri:repositoryUri,mutable:imageTagMutability}'

# Secrets Manager シークレット確認
aws secretsmanager describe-secret--secret-id /codebuild/db-password--query '{name:Name,arn:ARN}'

2-6. コンソールで確認 (3 点セット)

  1. VPC コンソール → 「サブネット」→ codebuild-private-a の「使用可能な IPv4 アドレス」が 250 以上あることを確認
  2. ECR コンソール → 「リポジトリ」→ my-app → 「イメージのタグ変更可能性」が「変更可能」になっていることを確認
  3. Secrets Manager コンソール/codebuild/db-password → 「シークレットの値を取得する」で JSON が取得できることを確認

2-7. ゴール状態の定義

本記事の §3–§7 完走後に達成できる状態をまとめます。

機能到達状態
buildspec.ymlphases/cache/artifacts/env/batch build/reports 全機能を本番 buildspec.yml に実装済み
マルチアーキarm64 + x86_64 manifest list が ECR の latest タグに push 済み
VPC 内ビルドprivate subnet + NAT Gateway 経由でプライベートリソースへ接続確認済み
Secrets Managerenv-vars-files 経由で /codebuild/db-password が環境変数として展開済み
ローカルデバッグcodebuild_build.sh でローカルビルドが本番と同一結果を再現
コスト最適化Spot fleet + S3 cache で月額コストを 50–70% 削減済み

3. buildspec.yml 完全解説 (phases/cache/artifacts/env/batch build)

fig03: buildspec.yml 構造マトリクス

buildspec.yml 全機能マップ (QG-2)

キー必須概要代表的な落とし穴
version✅ 必須0.2 を指定 (0.1 は非推奨・動作差異あり)version 省略または 0.1 → 環境変数スコープが変わる
phases✅ 必須install / pre_build / build / post_build の 4 フェーズフェーズ順序誤解・on-failure: ABORT 未設定
cache任意LOCAL (CUSTOM / DOCKER_LAYER / SOURCE) または S3paths 末尾の /**/* 指定忘れ
artifacts任意files / discard-paths / base-directory / namediscard-paths: yes でディレクトリ構造が消える
env任意variables / parameter-store / secrets-manager / exported-variablessecrets-manager の書式誤り・IAM 権限不足
batch任意build-list / build-graph / build-matrix で並列ビルドfast-fail 未設定で 1 件失敗時に全中断
reports任意JUnit / Cucumber / Clover 形式のテストレポートfiles パターンが実際の出力パスと不一致

3-1. version と phases の基礎

version: 0.2必須。省略または 0.1 を使うと環境変数のスコープがフェーズ間で共有されず予期しない挙動になる。

4 フェーズは 実行順固定。コマンドを書かなかったフェーズはスキップされる。

フェーズ目的代表コマンド例
installランタイム・ツールのインストールruntime-versions 指定、pip install、apt-get
pre_buildビルド前準備ECR ログイン、credential 設定
buildメインビルド処理npm run build、docker build、go build
post_buildビルド後処理docker push、S3 sync、通知送信

on-failure: ABORT を設定しないとコマンド失敗後も次のコマンドへ進む (CONTINUE がデフォルト)。本番環境では各フェーズに ABORT を指定すること。

version: 0.2
phases:
  install:
 runtime-versions:
nodejs: 20
 commands:
- npm install -g aws-cdk
 on-failure: ABORT
  pre_build:
 commands:
- aws ecr get-login-password --region $AWS_DEFAULT_REGION \
 | docker login --username AWS \
--password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
 on-failure: ABORT
  build:
 commands:
- npm ci
- npm run test
- npm run build
 on-failure: ABORT
  post_build:
 commands:
- aws s3 sync dist/ s3://$ARTIFACT_BUCKET/

コンソール確認: CodeBuild コンソール → ビルドプロジェクト → ビルド履歴 → ビルド詳細でフェーズタイムライン確認可能。

AWS CLI でビルド確認:

aws codebuild batch-get-builds \
  --ids "my-project:abc123" \
  --query 'builds[0].{status:buildStatus,phase:currentPhase}'

3-2. cache — ビルド時間を 40〜70% 短縮

S3 キャッシュは並列ビルド間で共有でき、ローカルキャッシュは同一コンテナ内のみで有効。

cache:
  type: S3
  location: my-codebuild-cache-bucket/my-project-cache
  paths:
 - node_modules/**/*
 - ~/.gradle/caches/**/*
 - /root/.m2/repository/**/*
 - /root/.cache/pip/**/*

重要: paths 末尾は必ず /**/* で終わること。/node_modules のみだとキャッシュが効かない。

Docker レイヤーキャッシュを使う場合 (docker build 高速化):

cache:
  type: LOCAL
  modes:
 - LOCAL_CUSTOM_CACHE
 - LOCAL_DOCKER_LAYER_CACHE
 - LOCAL_SOURCE_CACHE

Terraform:

resource "aws_codebuild_project" "app" {
  cache {
 type  = "S3"
 location = "${aws_s3_bucket.cache.bucket}/my-project"
  }
}

キャッシュ強制更新: aws codebuild start-build --no-cache または S3 キャッシュオブジェクトを直接削除。

3-3. artifacts — ビルド成果物の出力制御

artifacts:
  type: S3
  location: my-artifact-bucket
  path: builds/$CODEBUILD_BUILD_NUMBER/
  name: app-dist.zip
  base-directory: dist
  files:
 - "**/*"
  discard-paths: no
  packaging: ZIP

secondary-artifacts:
  reports:
 type: S3
 location: my-artifact-bucket
 path: reports/$CODEBUILD_BUILD_NUMBER/
 files:
- "coverage/**/*"
- "test-results/**/*"

落とし穴 1: discard-paths: yes にすると src/components/Button.jsButton.js にフラット化され同名ファイルが上書きされる。特別な理由がない限り no (デフォルト) を使うこと。

落とし穴 2: artifacts.files の YAML シーケンス記法。"**/*" (スカラー文字列) や ['**/*'] (インライン配列) ではなく、必ず YAML シーケンス形式を使うこと。

# NG: スカラー文字列 → CodeBuild がシーケンスとして解釈できずアーティファクトが空になる
artifacts:
  files: "**/*"

# NG: インライン配列記法 → 一部バージョンで解析エラー
artifacts:
  files: ['**/*']

# OK: YAML シーケンス形式
artifacts:
  files:
 - "**/*"
# 成果物の S3 URL 確認
aws codebuild batch-get-builds \
  --ids "my-project:abc123" \
  --query 'builds[0].artifacts.location'

3-4. env — 環境変数・Secrets 管理

4 種類の変数注入方法がある。機密情報は必ず PARAMETER_STORE または SECRETS_MANAGER を使うこと。

env:
  variables:
 NODE_ENV: production
 AWS_DEFAULT_REGION: ap-northeast-1
  parameter-store:
 GITHUB_TOKEN: /myapp/github_token
 SLACK_WEBHOOK: /myapp/slack_webhook
  secrets-manager:
 DB_PASSWORD: myapp/db:password
 DB_HOST: myapp/db:host
  exported-variables:
 - COMMIT_HASH
 - BUILD_VERSION
  git-credential-helper: yes

Secrets Manager の書式: secret-name:json-key[:version-stage][:version-id]。ARN 直接指定の場合: arn:aws:secretsmanager:region:account:secret:name:json-key

IAM 権限 (コードレビューで見落としやすい):

{
  "Effect": "Allow",
  "Action": [
 "ssm:GetParameters",
 "secretsmanager:GetSecretValue",
 "kms:Decrypt"
  ],
  "Resource": ["arn:aws:ssm:...", "arn:aws:secretsmanager:..."]
}

Terraform:

resource "aws_codebuild_project" "app" {
  environment {
 environment_variable {
name  = "DB_PASSWORD"
value = "myapp/db:password"
type  = "SECRETS_MANAGER"
 }
 environment_variable {
name  = "GITHUB_TOKEN"
value = "/myapp/github_token"
type  = "PARAMETER_STORE"
 }
  }
}

3-5. batch build — 並列ビルドで CI 時間を短縮

1 つの buildspec から複数ビルドを並列実行できる。

build-list (シンプルな並列実行):

batch:
  fast-fail: true
  build-list:
 - identifier: unit_test
env:
  variables:
 TEST_SUITE: unit
 - identifier: integration_test
env:
  variables:
 TEST_SUITE: integration
 - identifier: lint
env:
  variables:
 TEST_SUITE: lint

build-graph (依存関係付き並列実行):

batch:
  build-graph:
 - identifier: build
buildspec: buildspec-build.yml
 - identifier: test
buildspec: buildspec-test.yml
depend-on:
  - build
 - identifier: deploy
buildspec: buildspec-deploy.yml
depend-on:
  - test
aws codebuild start-build-batch --project-name my-project

3-6. reports — 構造化テストレポート

reports:
  junit-report:
 files:
- "test-results/junit/*.xml"
 file-format: JUNITXML
  coverage-report:
 files:
- "coverage/clover.xml"
 file-format: CLOVERXML

サポート形式: JUNITXML / CUCUMBERJSON / TESTNGXML / CLOVERXML / COBERTURAXML / NUNITXML

3-7. GitHub Actions との対応表

GitHub Actionsbuildspec.yml差異ポイント
on: pushCodePipeline / EventBridge で制御CodeBuild 単体はトリガー機能なし
jobs.<id>.stepsphases.build.commandsphases は固定 4 種
env: (job level)env.variablesCodeBuild は SSM/SM 直接参照可
strategy.matrixbatch.build-matrixCodeBuild は identifier 付与必要
actions/cache@v3cache.type: S3書式が大きく異なる
actions/upload-artifactartifacts.filesS3 直接出力
needs:batch.build-graph.depend-onCodeBuild は識別子参照
Secrets (GitHub)env.secrets-managerAWS Secrets Manager 経由
GHA から CodeBuild への移行チェックリスト

  • version: 0.2 の先頭宣言
  • ✅ 環境変数を PLAINTEXT / PARAMETER_STORE / SECRETS_MANAGER に分類
  • cache.paths 末尾の /**/* 必須
  • on-failure: ABORT を全フェーズに設定
  • artifacts.discard-paths: no を原則採用
  • ✅ batch build 使用時は fast-fail の挙動を確認

3-8. 3点セット: Terraform buildspec 指定 + CLI + コンソール

Terraform: inline buildspec vs S3 参照

# Option A: inline buildspec (Terraform 管理・小〜中規模向け)
resource "aws_codebuild_project" "app_inline" {
  source {
 type  = "GITHUB"
 location = "https://github.com/my-org/my-repo.git"
 buildspec = <<-BUILDSPEC
version: 0.2
phases:
  build:
 commands:
- npm run build
- aws s3 sync dist/ s3://$ARTIFACT_BUCKET/
 BUILDSPEC
  }
}

# Option B: リポジトリ内パス参照 (複数プロジェクト共通化・大規模向け)
resource "aws_codebuild_project" "app_file" {
  source {
 type= "GITHUB"
 location  = "https://github.com/my-org/my-repo.git"
 buildspec = "buildspec/prod.yml"  # リポジトリルートからの相対パス
  }
}

inline vs S3 参照の選択基準: buildspec をコードと同期管理するなら Option B (リポジトリ内パス) 推奨。Terraform で buildspec バージョン管理するなら Option A (inline)。複数プロジェクトが同一 buildspec を共有する場合は S3 バケット参照 (s3://bucket/buildspec.yml) も有効。

AWS CLI: ビルド操作の基本セット

# ビルド開始
aws codebuild start-build --project-name my-project

# ビルド一覧 (直近 5 件)
aws codebuild list-builds \
  --sort-order DESCENDING \
  --query 'ids[:5]' --output text

# ビルド詳細 (ステータス + 成果物 S3 パス)
aws codebuild batch-get-builds \
  --ids "my-project:abc123" \
  --query 'builds[0].{status:buildStatus,artifact:artifacts.location}'

# buildspec を上書きして開始 (デバッグ用)
aws codebuild start-build \
  --project-name my-project \
  --buildspec-override file://buildspec-debug.yml

コンソール確認: CodeBuild コンソール → ビルドプロジェクト → 「ビルド設定」タブ → Buildspec セクション でリポジトリパス / inline の設定を確認。ビルド詳細 → 「フェーズの詳細」タブ で各フェーズの所要時間・エラーログを参照可能。


4. マルチアーキテクチャ Docker build (arm64+x86_64 ECR push)

fig04: マルチアーキ Docker build フロー

QG-3: マルチアーキ Docker build フロー

  • docker buildx create –driver docker-container –use が必須 — driver 省略時は multi-platform export 非対応で manifest list を生成できない
  • CodeBuild Batch build で arm64 (ARM_CONTAINER) と x86_64 (LINUX_CONTAINER) を並列ビルド → 各プラットフォームの tagged image を ECR に個別 push
  • docker manifest create で arm64/amd64 digest を manifest list に集約 → ECR に push → 1 リポジトリで 2 アーキ同居・docker pull 時にホストのアーキに応じて自動選択

4-1. なぜマルチアーキテクチャ対応が必要か

AWS Graviton インスタンス (C7g/M7g/R7g) が EC2・ECS・Lambda で普及し、x86_64 と arm64 の両プラットフォームに対応したコンテナイメージを 1 つの ECR リポジトリで管理するニーズが高まっている。OCI 仕様の manifest list を使えば docker pull 実行時に実行ホストのアーキテクチャに応じて正しい image layer が自動的に選択される。

  • コスト削減: Graviton3 は同性能・同料金帯の x86_64 比で最大 40% 安価。ECS タスクや Lambda を arm64 イメージに切り替えるだけで削減効果を得られる。
  • 開発速度向上: Apple Silicon (M2/M3) 搭載 Mac でのローカル docker run が arm64 native 動作となり、QEMU エミュレーションによる速度低下を回避できる。
  • 単一リポジトリ管理: arm64 用と x86_64 用でリポジトリを分ける必要がなく、CI/CD パイプラインのタグ管理がシンプルになる。

4-2. docker buildx のセットアップ (driver 選択が鍵)

CodeBuild でマルチアーキ manifest list を生成するには docker buildx--driver docker-container の組み合わせが必須。デフォルトの docker ドライバーも --platform フラグを受け付けるが、cross-platform export に必要な BuildKit のフル機能が使えず manifest list を生成できない。

# driver=docker-container を明示 (省略すると multi-platform export 不可)
docker buildx create \
  --driver docker-container \
  --name multiarch-builder \
  --use

# 作成した builder を確認
docker buildx ls
# NAME/NODE  DRIVER/ENDPOINT STATUSPLATFORMS
# multiarch-builder * docker-containerrunning  linux/amd64, linux/arm64

buildspec.yml の pre_build フェーズ冒頭でこのコマンドを実行する。--use フラグで作成した builder がデフォルトになる。CodeBuild プロジェクトの privileged_mode = true も必須。

4-3. buildspec.yml 実装 (Batch build 並列方式)

arm64 と x86_64 を CodeBuild Batch build の並列ジョブで別々にビルドし、各 digest を ECR に push してから manifest list を合成する。各アーキに最適な compute type を使えるため QEMU エミュレーション不要で native ビルドが可能。

buildspec-arm64.yml

version: 0.2

env:
  variables:
 DOCKER_BUILDKIT: "1"
 BUILDX_VERSION: "0.12.1"
 IMAGE_REPO_NAME: "my-app"
 AWS_DEFAULT_REGION: "ap-northeast-1"
 AWS_ACCOUNT_ID: "123456789012"

phases:
  install:
 commands:
- |
  curl -fsSL \
 "https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-arm64" \
 -o /usr/local/lib/docker/cli-plugins/docker-buildx
  chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
  pre_build:
 commands:
- |
  aws ecr get-login-password --region $AWS_DEFAULT_REGION \
 | docker login --username AWS \
  --password-stdin \
  ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com
- docker buildx create --driver docker-container --name builder --use
  build:
 commands:
- |
  docker buildx build \
 --platform linux/arm64 \
 --tag ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:arm64-${CODEBUILD_RESOLVED_SOURCE_VERSION} \
 --push .

buildspec-amd64.yml は上記の linux/arm64linux/amd64 に、arm64- タグを amd64- に変更し、install フェーズの buildx バイナリ URL を linux-amd64 に差し替えるだけで対応できる。

親プロジェクトの buildspec に Batch build 定義を配置し、2 ジョブを並列起動する。

version: 0.2

batch:
  build-list:
 - identifier: arm64_build
buildspec: buildspec-arm64.yml
env:
  type: ARM_CONTAINER
  compute-type: BUILD_GENERAL1_SMALL
 - identifier: amd64_build
buildspec: buildspec-amd64.yml
env:
  type: LINUX_CONTAINER
  compute-type: BUILD_GENERAL1_SMALL

4-4. ECR manifest list の作成と push

2 プロジェクトのビルドが完了し各 tagged image が ECR に存在する状態で manifest list を作成する。

# ECR へ再ログイン (長時間ビルド後の token 期限切れ対策)
aws ecr get-login-password --region ap-northeast-1 \
  | docker login --username AWS \
--password-stdin \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com

REPO="123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app"
COMMIT="${CODEBUILD_RESOLVED_SOURCE_VERSION}"

# manifest list を作成
docker manifest create ${REPO}:latest \
  --amend ${REPO}:arm64-${COMMIT} \
  --amend ${REPO}:amd64-${COMMIT}

# ECR へ push
docker manifest push ${REPO}:latest

# push 確認
aws ecr describe-images \
  --repository-name my-app \
  --image-ids imageTag=latest \
  --query 'imageDetails[0].imageManifestMediaType'
# 期待値: "application/vnd.docker.distribution.manifest.list.v2+json"

ECR リポジトリは image_tag_mutability = "MUTABLE" に設定しておく。IMMUTABLE のままだと既存タグへの manifest list 上書きが ImageAlreadyExistsException で拒否される。

4-5. Terraform 実装 (arm64/x86_64 CodeBuild プロジェクト)

resource "aws_ecr_repository" "app" {
  name  = "my-app"
  image_tag_mutability = "MUTABLE"

  image_scanning_configuration {
 scan_on_push = true
  }
}

resource "aws_codebuild_project" "app_arm64" {
  name = "my-app-build-arm64"
  build_timeout = 20
  service_role  = aws_iam_role.codebuild.arn

  source {
 type= "GITHUB"
 location  = "https://github.com/example/my-app.git"
 buildspec = "buildspec-arm64.yml"
  }

  environment {
 compute_type = "BUILD_GENERAL1_SMALL"
 image  = "aws/codebuild/standard:7.0"
 type= "ARM_CONTAINER"
 privileged_mode = true

 environment_variable {
name  = "DOCKER_BUILDKIT"
value = "1"
 }
  }

  artifacts { type = "NO_ARTIFACTS" }

  cache {
 type  = "LOCAL"
 modes = ["LOCAL_DOCKER_LAYER_CACHE", "LOCAL_SOURCE_CACHE"]
  }
}

resource "aws_codebuild_project" "app_amd64" {
  name = "my-app-build-amd64"
  build_timeout = 20
  service_role  = aws_iam_role.codebuild.arn

  source {
 type= "GITHUB"
 location  = "https://github.com/example/my-app.git"
 buildspec = "buildspec-amd64.yml"
  }

  environment {
 compute_type = "BUILD_GENERAL1_SMALL"
 image  = "aws/codebuild/standard:7.0"
 type= "LINUX_CONTAINER"
 privileged_mode = true

 environment_variable {
name  = "DOCKER_BUILDKIT"
value = "1"
 }
  }

  artifacts { type = "NO_ARTIFACTS" }

  cache {
 type  = "LOCAL"
 modes = ["LOCAL_DOCKER_LAYER_CACHE", "LOCAL_SOURCE_CACHE"]
  }
}

4-6. AWS CLI で動作確認

# arm64 ビルドを手動トリガー
aws codebuild start-build \
  --project-name my-app-build-arm64 \
  --source-version refs/heads/main

# amd64 ビルドを手動トリガー
aws codebuild start-build \
  --project-name my-app-build-amd64 \
  --source-version refs/heads/main

# manifest list のアーキテクチャ構成を確認
docker buildx imagetools inspect \
  123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:latest
# MediaType: application/vnd.docker.distribution.manifest.list.v2+json
# Platforms: linux/amd64, linux/arm64

4-7. コンソール確認

  1. CodeBuild コンソールmy-app-build-arm64 / my-app-build-amd64Start buildBuild logs タブで --platform linux/arm64 (linux/amd64) ... --push が成功することを確認。
  2. ECR コンソールmy-app リポジトリ → Images タブ → latest タグを選択 → Manifest typeOCI Image Index になっていることを確認。
  3. イメージスキャン: latest イメージ行の Scan results タブで脆弱性スキャン結果を確認する。

4-8. GHA 比較コラム

GitHub Actions でマルチアーキ対応するには docker/setup-buildx-action + docker/build-push-action の matrix 構成が一般的。CodeBuild Batch build との主な違いを整理する。

比較項目CodeBuild Batch buildGHA matrix strategy
compute 切替ARM_CONTAINER / LINUX_CONTAINER を project 単位で指定runs-on: ubuntu-latest (QEMU) or 自前 arm64 runner
QEMU エミュレーション不要 (native build)arm64 self-hosted runner なしの場合は必須 (3-5× 低速)
並列ビルドBatch build で同時実行matrix: [amd64, arm64] で同時実行
キャッシュLOCAL_DOCKER_LAYER_CACHEcache-from / cache-to (ghcr.io 等)
ECR 認証IAM service roleOIDC + aws-actions/configure-aws-credentials

4-9. 落とし穴と対策

#落とし穴症状対策
1--driver docker-container 省略multiple platforms feature is currently not supported for docker driverbuildx create 時に --driver docker-container を必ず指定
2DOCKER_BUILDKIT 未設定BuildKit 無効・multi-platform build が制限されるbuildspec の env.variablesDOCKER_BUILDKIT: "1" を追加
3privileged_mode = falseDocker デーモン起動失敗・ビルド不可environment.privileged_mode = true 必須
4manifest push 前の token 期限切れdenied: Your authorization token has expiredmanifest push 直前に aws ecr get-login-password を再取得
5arm64 非対応 base image (一部 alpine 等)exec format error でコンテナ起動失敗docker buildx imagetools inspect <image> で platform 欄を事前確認
6BUILD_LAMBDA compute で Docker buildLambda compute は Docker デーモン非対応Docker build は LINUX_CONTAINER / ARM_CONTAINER を使用

5. VPC内ビルド + NAT Gateway/Secrets Manager統合

fig05: VPC + Secrets Manager 統合構成

QG-4: VPC + Secrets Manager 統合構成

  • private subnet に CodeBuild ENI を配置し、内部リソース (RDS/ElastiCache/MSK) へ直接接続
  • NAT Gateway 経由でインターネット出口 (pip/npm/ECR Public/OS パッケージ) を確保
  • env-vars-files で Secrets Manager の secret を ARN 指定 → ビルド実行時に環境変数へ自動展開
  • IAM Role に ec2:CreateNetworkInterface + secretsmanager:GetSecretValue の両権限が必須
  • subnet CIDR は /24 以上推奨 (並列ビルド数 × ENI 数 でIP消費・枯渇注意)

5-1. VPC内ビルドが必要なケース

CodeBuild のデフォルトビルド環境は VPC 外のマネージド環境で動作する。以下のケースでは VPC 内ビルドへの切り替えが必要になる。

  • プライベートリソースへの接続: RDS/Aurora/ElastiCache/MSK 等、VPC 内部にのみ存在するエンドポイントへテストや DB マイグレーションのため接続する
  • セキュリティ要件: 全トラフィックを VPC 内に閉じ、インターネット直接通信を禁止する場合
  • VPC エンドポイント利用: ECR/S3/Secrets Manager 等を VPC エンドポイント経由でのみ利用する場合
  • マルチアカウント VPC 共有: Resource Access Manager (RAM) で共有された VPC 経由でリソース接続する場合

VPC 内ビルドを有効にすると CodeBuild はビルド開始時に指定した subnet に ENI を 1 枚作成する。1 ビルド = 1 ENI が基本であり、並列ビルドが増えるほど IP アドレスを消費する。

5-2. IPプール容量設計 (ENI数計算式)

VPC 内ビルドで最も多い障害原因が subnet の IP アドレス枯渇による ENI 作成失敗だ。事前に以下の計算式で必要 IP 数を算出する。

必要 IP 数 = 最大同時ビルド数 × 1 ENI/ビルド + 予備 20%

例: 並列 50 ビルド → 50 IP 必要
  /26 (利用可能 64 IP - AWS予約 5 = 59 IP) → ギリギリ。障害リスクあり
  /24 (利用可能 256 IP - AWS予約 5 = 251 IP) → 推奨。余裕あり

複数 subnet 分散: 2 AZ で /26 ずつ → 合計 118 IP → 並列 98 ビルドまで対応可能

AWS が subnet ごとに予約する 5 IP (ネットワークアドレス/ルーター/DNS/将来用×2/ブロードキャスト) を差し引いた実使用可能数で計算すること。本番環境では /24 以上を推奨、または複数 subnet を指定して AZ 間で分散する。

# 現在の subnet 空き IP 数を確認
aws ec2 describe-subnets \
  --subnet-ids subnet-0abc1234 \
  --query "Subnets[].{id:SubnetId,available:AvailableIpAddressCount,cidr:CidrBlock}"

5-3. CodeBuild VPC設定 (vpc_config ブロック)

CodeBuild プロジェクトの VPC 設定に必要な 3 要素は vpc_id / subnets / security_group_ids の指定だ。buildspec.yml 側は変更不要。VPC 設定はプロジェクトレベルで適用される。

VPC 内ビルドで外部インターネット通信が必要な場合 (npm install / pip / docker pull 等) は private subnet + NAT Gateway 構成が必要だ。Public subnet では CodeBuild ENI にパブリック IP が付与されないためインターネットに到達できない。

構成インターネット通信内部リソース接続推奨用途
private subnet + NAT GW✅ 可✅ 可本番標準構成
private subnet (NAT なし)❌ 不可✅ 可完全閉域・内部テストのみ
VPC エンドポイント利用❌ (AWS サービスのみ)✅ 可AWS サービスのみ通信

Security Group の設定方針は ingress ルールなし・egress を最小限に絞る。CodeBuild は外部から接続を受け付けないため ingress は不要だ。

egress 443/tcp → 0.0.0.0/0# HTTPS (npm/pip/ECR/AWS API)
egress 80/tcp  → 0.0.0.0/0# HTTP (一部 OS パッケージ)
ingress: なし (CodeBuild は接続を受け付けない)

5-4. Secrets Manager統合 (type=SECRETS_MANAGER / env-vars-files)

Secrets Manager の secret を CodeBuild に渡す方法は 2 種類ある。

方法A: 環境変数 (type=SECRETS_MANAGER)

environment_variabletypeSECRETS_MANAGER にし、valuesecret-id:json-key 形式で指定する。buildspec.yml 内では通常の環境変数として ${DB_PASSWORD} で参照できる。

# buildspec.yml での参照 (通常の環境変数として透過的に使用)
env:
  secrets-manager:
 DB_PASSWORD: "prod/app/db:password"
 API_KEY: "prod/app/api:key"

secret-id:json-key の形式: Secrets Manager の secret 値が JSON オブジェクトの場合、コロン区切りで取得する key を指定する。JSON でないプレーン文字列の場合は secret-id のみ指定。

方法B: env-vars-files (ファイル展開形式)

複数の secret を一括取得して .env ファイル形式でビルドコンテナに展開する方法。secret の個数が増えた場合でも buildspec.yml が肥大化しない。

# buildspec.yml での env-vars-files 指定
env:
  secrets-manager:
 ENV_FILE_CONTENT: "prod/app/env-file"
env-vars-files:
  - "/tmp/app.env"
# Secrets Manager に登録する secret の値 (JSON 文字列として保存)
{"DB_HOST":"db.internal","DB_PASSWORD":"s3cr3t","API_KEY":"abc123"}

# CodeBuild が /tmp/app.env として書き出す内容 → 環境変数に自動展開
DB_HOST=db.internal
DB_PASSWORD=s3cr3t
API_KEY=abc123

5-5. 3点セット: VPC + Secrets Manager 統合

Terraform (要点)

Terraform での完全な HCL は §6 に記載する。ここでは vpc_config と Secrets Manager 統合の要点を示す。

# aws_codebuild_project の vpc_config block
vpc_config {
  vpc_id = aws_vpc.main.id
  subnets= [aws_subnet.private_a.id, aws_subnet.private_b.id]
  security_group_ids = [aws_security_group.codebuild.id]
}

# Secrets Manager 参照の環境変数
environment {
  compute_type = "BUILD_GENERAL1_SMALL"
  image  = "aws/codebuild/standard:7.0"
  type= "LINUX_CONTAINER"
  privileged_mode = true

  environment_variable {
 name  = "DB_PASSWORD"
 value = "prod/app/db:password"
 type  = "SECRETS_MANAGER"
  }
}

AWS CLI

# 既存プロジェクトに VPC 設定を追加 (update-project)
aws codebuild update-project \
  --name my-codebuild-project \
  --vpc-config vpcId=vpc-0abc1234,subnets=subnet-0def5678,securityGroupIds=sg-0ghi9012

# Secrets Manager の secret 現在値を確認 (ビルド前の動作確認に)
aws secretsmanager get-secret-value \
  --secret-id prod/app/db \
  --query "SecretString" \
  --output text | python3 -m json.tool

# VPC 設定の確認
aws codebuild batch-get-projects \
  --names my-codebuild-project \
  --query "projects[].vpcConfig"

# CodeBuild プロジェクトに resource-based policy を設定 (クロスアカウント共有時)
aws codebuild put-resource-policy \
  --resource-arn arn:aws:codebuild:ap-northeast-1:123456789012:project/my-project \
  --policy file://policy.json

コンソール操作

  1. CodeBuild コンソール → 対象プロジェクト → 「編集」→「環境」タブを開く
  2. 「VPC」セクション: VPC を選択 → private subnet を選択 → Security Group を選択して保存
  3. 「環境変数」セクション: 「タイプ」を「Secrets Manager」→「値」に secret-id:key 形式で入力
  4. テストビルドを実行 → 「ビルドログ」→「PROVISIONING」フェーズで ENI 作成ログを確認

5-6. 落とし穴4選

落とし穴1: VPC subnet IP不足で ENI 作成失敗

CLIENT_ERROR: PROVISIONING: Failed to prepare build environment.
The subnet does not have enough free addresses to allocate 
a network interface. subnet-0abc1234

解決策: subnet の CIDR を /24 以上に拡大するか、vpc_config.subnets に複数 subnet を指定して IP プールを AZ 間で分散させる。

落とし穴2: NAT Gateway不在でインターネット通信失敗

private subnet に NAT Gateway (or VPC エンドポイント) がない状態でビルドすると npm installpip install が全て失敗する。

npm ERR! code ENOTFOUND
npm ERR! errno ENOTFOUND
npm ERR! network request to https://registry.npmjs.org/ failed,
reason: getaddrinfo ENOTFOUND registry.npmjs.org

解決策: route table に NAT Gateway へのルート (0.0.0.0/0 → nat-XXXXXXXX) を追加する。外部通信が不要な場合は VPC エンドポイント (ECR/S3/Secrets Manager) のみ有効にする構成でコストを削減できる。

落とし穴3: Secrets Manager IAMポリシー権限不足

IAM Role に secretsmanager:GetSecretValue が欠けているとビルドが以下のエラーで失敗する。

CLIENT_ERROR: AccessDeniedException: User: arn:aws:sts::123456789012:assumed-role/
codebuild-role is not authorized to perform: secretsmanager:GetSecretValue
on resource: prod/app/db because no identity-based policy allows the action

解決策: IAM Policy に secretsmanager:GetSecretValue を追加する。CMK で暗号化している場合は kms:Decrypt も必要だ。リソース ARN は arn:aws:secretsmanager:*:*:secret:prod/* のようにパスプレフィックスで絞ること。

落とし穴4: VPCフローログで通信経路を確認する方法

VPC 内ビルドで通信が失敗する場合、VPC フローログを有効化してトラフィックを可視化する。

# VPC フローログを S3 に有効化
aws ec2 create-flow-logs \
  --resource-type VPC \
  --resource-ids vpc-0abc1234 \
  --traffic-type ALL \
  --log-destination-type s3 \
  --log-destination arn:aws:s3:::my-flowlogs-bucket/codebuild/

# CloudWatch Logs Insights でフローログを検索 (REJECT トラフィックのみ抽出)
aws logs start-query \
  --log-group-name "/aws/vpc/flowlogs" \
  --start-time $(date -d "1 hour ago" +%s) \
  --end-time $(date +%s) \
  --query-string 'fields @timestamp, srcAddr, dstAddr, dstPort, action | filter action="REJECT" and srcAddr like "10.0." | sort @timestamp desc | limit 50'

6. Terraform完全実装 (aws_codebuild_project + IAM + VPC + buildspec)

6-1. ファイル構成

§3-§5 で解説した buildspec / マルチアーキ / VPC / Secrets Manager の設定を Terraform HCL に統合する。

terraform/
├── main.tf  # aws_codebuild_project / webhook / source_credential
├── iam.tf# aws_iam_role / aws_iam_role_policy
├── vpc.tf# aws_vpc / subnet / nat_gateway / security_group
├── ecr.tf# aws_ecr_repository / lifecycle_policy
├── s3.tf # S3 artifact bucket
├── variables.tf
└── outputs.tf

6-2. variables.tf

variable "project_name" {
  description = "CodeBuild project name"
  type  = string
  default  = "my-codebuild-project"
}

variable "aws_region" {
  description = "AWS region"
  type  = string
  default  = "ap-northeast-1"
}

variable "github_token" {
  description = "GitHub personal access token for source credential"
  type  = string
  sensitive= true
}

variable "vpc_cidr" {
  type = string
  default = "10.0.0.0/16"
}

variable "private_subnet_cidrs" {
  type = list(string)
  default = ["10.0.1.0/24", "10.0.2.0/24"]
}

variable "public_subnet_cidrs" {
  type = list(string)
  default = ["10.0.101.0/24", "10.0.102.0/24"]
}

variable "azs" {
  type = list(string)
  default = ["ap-northeast-1a", "ap-northeast-1c"]
}

variable "ecr_repository_name" {
  type = string
  default = "my-app"
}

variable "artifact_bucket_name" {
  description = "S3 bucket name for build artifacts"
  type  = string
}

6-3. vpc.tf (private subnet + NAT Gateway)

§5 で解説した VPC 内ビルド構成を HCL で実装する。

resource "aws_vpc" "main" {
  cidr_block  = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support= true
  tags = { Name = "${var.project_name}-vpc" }
}

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidrs)
  vpc_id= aws_vpc.main.id
  cidr_block  = var.private_subnet_cidrs[count.index]
  availability_zone = var.azs[count.index]
  tags = { Name = "${var.project_name}-private-${count.index + 1}" }
}

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidrs)
  vpc_id= aws_vpc.main.id
  cidr_block  = var.public_subnet_cidrs[count.index]
  availability_zone = var.azs[count.index]
  map_public_ip_on_launch = true
  tags = { Name = "${var.project_name}-public-${count.index + 1}" }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags= { Name = "${var.project_name}-igw" }
}

resource "aws_eip" "nat" {
  count  = length(var.public_subnet_cidrs)
  domain = "vpc"
  tags= { Name = "${var.project_name}-nat-eip-${count.index + 1}" }
}

resource "aws_nat_gateway" "main" {
  count= length(var.public_subnet_cidrs)
  allocation_id = aws_eip.nat[count.index].id
  subnet_id  = aws_subnet.public[count.index].id
  tags = { Name = "${var.project_name}-nat-${count.index + 1}" }
  depends_on = [aws_internet_gateway.main]
}

resource "aws_route_table" "private" {
  count  = length(var.private_subnet_cidrs)
  vpc_id = aws_vpc.main.id
  route {
 cidr_block  = "0.0.0.0/0"
 nat_gateway_id = aws_nat_gateway.main[count.index].id
  }
  tags = { Name = "${var.project_name}-private-rt-${count.index + 1}" }
}

resource "aws_route_table_association" "private" {
  count = length(var.private_subnet_cidrs)
  subnet_id= aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[count.index].id
}

resource "aws_security_group" "codebuild" {
  name  = "${var.project_name}-codebuild-sg"
  description = "Security group for CodeBuild VPC builds"
  vpc_id= aws_vpc.main.id

  egress {
 from_port= 443
 to_port  = 443
 protocol = "tcp"
 cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
 from_port= 80
 to_port  = 80
 protocol = "tcp"
 cidr_blocks = ["0.0.0.0/0"]
  }

  tags = { Name = "${var.project_name}-codebuild-sg" }
}

6-4. iam.tf (最小権限ポリシー全文)

data "aws_iam_policy_document" "codebuild_assume" {
  statement {
 actions = ["sts:AssumeRole"]
 principals {
type  = "Service"
identifiers = ["codebuild.amazonaws.com"]
 }
  }
}

resource "aws_iam_role" "codebuild" {
  name= "${var.project_name}-codebuild-role"
  assume_role_policy = data.aws_iam_policy_document.codebuild_assume.json
}

data "aws_iam_policy_document" "codebuild_policy" {
  statement {
 sid = "CloudWatchLogs"
 effect = "Allow"
 actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
 ]
 resources = ["arn:aws:logs:*:*:log-group:/aws/codebuild/*"]
  }

  statement {
 sid = "S3Artifacts"
 effect = "Allow"
 actions = [
"s3:GetObject", "s3:GetObjectVersion",
"s3:PutObject", "s3:GetBucketAcl", "s3:GetBucketLocation"
 ]
 resources = [
aws_s3_bucket.artifacts.arn,
"${aws_s3_bucket.artifacts.arn}/*"
 ]
  }

  statement {
 sid = "ECR"
 effect = "Allow"
 actions = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
 ]
 resources = ["*"]
  }

  statement {
 sid = "SecretsManager"
 effect = "Allow"
 actions = [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
 ]
 resources = ["arn:aws:secretsmanager:*:*:secret:prod/*"]
  }

  statement {
 sid = "VPCAccess"
 effect = "Allow"
 actions = [
"ec2:CreateNetworkInterface",
"ec2:DescribeDhcpOptions",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs"
 ]
 resources = ["*"]
  }

  statement {
 sid  = "VPCENIPermission"
 effect  = "Allow"
 actions = ["ec2:CreateNetworkInterfacePermission"]
 resources = ["arn:aws:ec2:*:*:network-interface/*"]
 condition {
test  = "StringEquals"
variable = "ec2:AuthorizedService"
values= ["codebuild.amazonaws.com"]
 }
  }

  statement {
 sid = "CodeBuildReports"
 effect = "Allow"
 actions = [
"codebuild:CreateReportGroup",
"codebuild:CreateReport",
"codebuild:UpdateReport",
"codebuild:BatchPutTestCases",
"codebuild:BatchPutCodeCoverages"
 ]
 resources = ["arn:aws:codebuild:*:*:report-group/${var.project_name}-*"]
  }
}

resource "aws_iam_role_policy" "codebuild" {
  name= "${var.project_name}-codebuild-policy"
  role= aws_iam_role.codebuild.id
  policy = data.aws_iam_policy_document.codebuild_policy.json
}

6-5. ecr.tf + s3.tf (ECR lifecycle + S3 artifact bucket)

# ecr.tf
resource "aws_ecr_repository" "app" {
  name  = var.ecr_repository_name
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration { scan_on_push = true }
  encryption_configuration { encryption_type = "AES256" }
}

resource "aws_ecr_lifecycle_policy" "app" {
  repository = aws_ecr_repository.app.name
  policy = jsonencode({
 rules = [
{
  rulePriority = 1
  description  = "Keep last 10 tagged images (v* prefix)"
  selection = {
 tagStatus  = "tagged"
 tagPrefixList = ["v"]
 countType  = "imageCountMoreThan"
 countNumber= 10
  }
  action = { type = "expire" }
},
{
  rulePriority = 2
  description  = "Remove untagged images after 7 days"
  selection = {
 tagStatus= "untagged"
 countType= "sinceImagePushed"
 countUnit= "days"
 countNumber = 7
  }
  action = { type = "expire" }
}
 ]
  })
}

# s3.tf
resource "aws_s3_bucket" "artifacts" {
  bucket = var.artifact_bucket_name
}

resource "aws_s3_bucket_versioning" "artifacts" {
  bucket = aws_s3_bucket.artifacts.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
  bucket = aws_s3_bucket.artifacts.id
  rule {
 apply_server_side_encryption_by_default { sse_algorithm = "AES256" }
  }
}

resource "aws_s3_bucket_public_access_block" "artifacts" {
  bucket= aws_s3_bucket.artifacts.id
  block_public_acls = true
  block_public_policy  = true
  ignore_public_acls= true
  restrict_public_buckets = true
}

6-6. main.tf (aws_codebuild_project 完全 HCL)

§3-§5 の全設定を統合した完全な aws_codebuild_project リソースを定義する。

resource "aws_codebuild_source_credential" "github" {
  auth_type= "PERSONAL_ACCESS_TOKEN"
  server_type = "GITHUB"
  token = var.github_token
}

resource "aws_codebuild_project" "main" {
  name = var.project_name
  description= "CodeBuild project with VPC + Secrets Manager + multi-arch Docker"
  build_timeout = 60
  service_role  = aws_iam_role.codebuild.arn

  source {
 type= "GITHUB"
 location  = "https://github.com/my-org/my-repo.git"
 git_clone_depth = 1
 buildspec = "buildspec.yml"
 git_submodules_config { fetch_submodules = false }
  }

  artifacts {
 type= "S3"
 location  = aws_s3_bucket.artifacts.bucket
 path= "builds"
 packaging = "ZIP"
  }

  environment {
 compute_type = "BUILD_GENERAL1_SMALL"
 image  = "aws/codebuild/standard:7.0"
 type= "LINUX_CONTAINER"
 image_pull_credentials_type = "CODEBUILD"
 privileged_mode = true

 environment_variable {
name  = "ECR_REPOSITORY_URI"
value = aws_ecr_repository.app.repository_url
type  = "PLAINTEXT"
 }

 environment_variable {
name  = "DB_PASSWORD"
value = "prod/app/db:password"
type  = "SECRETS_MANAGER"
 }

 environment_variable {
name  = "API_KEY"
value = "/prod/app/api_key"
type  = "PARAMETER_STORE"
 }
  }

  vpc_config {
 vpc_id = aws_vpc.main.id
 subnets= aws_subnet.private[*].id
 security_group_ids = [aws_security_group.codebuild.id]
  }

  cache {
 type  = "LOCAL"
 modes = ["LOCAL_DOCKER_LAYER_CACHE", "LOCAL_SOURCE_CACHE"]
  }

  logs_config {
 cloudwatch_logs {
group_name  = "/aws/codebuild/${var.project_name}"
stream_name = ""
 }
 s3_logs {
status= "ENABLED"
location = "${aws_s3_bucket.artifacts.bucket}/build-logs"
 }
  }

  depends_on = [aws_codebuild_source_credential.github]
}

resource "aws_codebuild_webhook" "main" {
  project_name = aws_codebuild_project.main.name
  build_type= "BUILD"

  filter_group {
 filter {
type = "EVENT"
pattern = "PUSH"
 }
 filter {
type = "HEAD_REF"
pattern = "^refs/heads/main$"
 }
  }

  filter_group {
 filter {
type = "EVENT"
pattern = "PULL_REQUEST_CREATED,PULL_REQUEST_UPDATED"
 }
  }
}

6-7. outputs.tf

output "codebuild_project_arn" {
  value = aws_codebuild_project.main.arn
}

output "codebuild_role_arn" {
  value = aws_iam_role.codebuild.arn
}

output "ecr_repository_url" {
  value = aws_ecr_repository.app.repository_url
}

output "artifact_bucket_name" {
  value = aws_s3_bucket.artifacts.bucket
}

output "vpc_id" {
  value = aws_vpc.main.id
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}

6-8. 3点セット: デプロイ・動作確認

Terraform apply 手順

cd terraform/

# 初期化
terraform init

# 実行計画確認
terraform plan \
  -var="github_token=$GITHUB_TOKEN" \
  -var="artifact_bucket_name=my-codebuild-artifacts-$(aws sts get-caller-identity --query Account --output text)"

# 適用
terraform apply \
  -var="github_token=$GITHUB_TOKEN" \
  -var="artifact_bucket_name=my-codebuild-artifacts-$(aws sts get-caller-identity --query Account --output text)"

# 出力確認
terraform output

AWS CLI での動作確認

# プロジェクト設定確認 (VPC + 環境変数)
aws codebuild batch-get-projects \
  --names my-codebuild-project \
  --query "projects[].{name:name,vpc:vpcConfig,env:environment.environmentVariables}"

# 手動ビルド実行
BUILD_ID=$(aws codebuild start-build \
  --project-name my-codebuild-project \
  --query "build.id" --output text)

# ビルド状況確認
aws codebuild batch-get-builds \
  --ids "$BUILD_ID" \
  --query "builds[].{status:buildStatus,phase:currentPhase}"

# ECR にイメージが push されたか確認
aws ecr describe-images \
  --repository-name my-app \
  --query "imageDetails[].{tags:imageTags,pushed:imagePushedAt}" \
  --output table

コンソール確認ポイント

  1. CodeBuild コンソール → プロジェクト一覧 → my-codebuild-project → 「ビルド履歴」でビルド結果を確認
  2. ビルド詳細 → 「フェーズ詳細」タブ → PROVISIONING フェーズの ENI 作成ログを確認
  3. 環境変数 → 「設定の編集」→「環境」タブ → SECRETS_MANAGER 型の環境変数が正しく設定されているか確認
  4. VPC 設定 → 「設定の編集」→「環境」タブ → VPC / subnet / Security Group の設定確認
  5. ECR コンソールmy-app リポジトリ → マルチアーキマニフェストが作成されたか確認

7. ローカルデバッグ (codebuild_build.sh) + コスト最適化

ローカルで buildspec.yml を検証できれば、push → CodeBuild 実行 → ログ確認というサイクルを大幅に短縮できる。本章では公式 Local Agent を使ったローカルデバッグ手順と、本番 CodeBuild のコスト削減テクニックを解説する。

7-1. CodeBuild Local Agent の仕組み

AWS が提供する CodeBuild Local Agent は Docker コンテナとして動作し、実際の CodeBuild 実行環境に近い状態でローカルビルドを再現する。公式スクリプト codebuild_build.sh がエントリーポイントとなる。

必要条件:
– Docker Engine 20.10 以上 + Docker デーモン起動済み
– bash (Linux / macOS / WSL2)
– ビルドに使用する CodeBuild ベースイメージへのアクセス権

7-2. セットアップ手順 (3 点セット)

AWS CLI — エージェントイメージ取得とローカルビルド実行

# ① ベースイメージを pull (Standard 7.0 の例)
docker pull public.ecr.aws/codebuild/standard:7.0

# ② 公式スクリプト取得
curl -O https://raw.githubusercontent.com/aws/aws-codebuild-docker-images/master/local_builds/codebuild_build.sh
chmod +x codebuild_build.sh

# ③ 基本ローカルビルド実行
./codebuild_build.sh \
  -i public.ecr.aws/codebuild/standard:7.0 \
  -a /tmp/artifacts \
  -s "$(pwd)" \
  -b buildspec.yml

# オプション説明:
#  -i IMAGE : ビルド環境イメージ (プロジェクト設定と同一バージョンを使用)
#  -a DIR: アーティファクト出力先
#  -s DIR: ソースコードディレクトリ (省略で cwd)
#  -b FILE  : buildspec.yml パス (省略でソースルートの buildspec.yml)
#  -c : コンテナ認証情報 (ECR push テスト時)
#  -e FILE  : 環境変数ファイル (KEY=VALUE 形式)

環境変数の注入:

# ローカル環境変数ファイルを作成
cat > /tmp/local_env.env << 'EOF'
AWS_DEFAULT_REGION=ap-northeast-1
MY_SECRET_VALUE=local_test_value
CODEBUILD_RESOLVED_SOURCE_VERSION=abc123
EOF

./codebuild_build.sh \
  -i public.ecr.aws/codebuild/standard:7.0 \
  -a /tmp/artifacts \
  -e /tmp/local_env.env

コンソールでの確認: CodeBuild コンソール → プロジェクト選択 → 「ビルド履歴」→ ビルドログはリアルタイムで「フェーズ詳細」タブに表示される。ローカルで再現した問題と本番環境ログを見比べて差異を確認する。

7-3. AWS CLI でビルド実行・ログ確認

# プロジェクト設定確認
aws codebuild batch-get-projects \
  --names my-project \
  --query 'projects[0].{compute:environment.computeType,image:environment.image,type:environment.type}'

# ビルドを手動起動
BUILD_ID=$(aws codebuild start-build \
  --project-name my-project \
  --source-version main \
  --query 'build.id' --output text)

# ビルドステータス確認
aws codebuild batch-get-builds \
  --ids "$BUILD_ID" \
  --query 'builds[0].{status:buildStatus,phase:currentPhase,logs:logs.deepLink}'

# キャッシュ強制無効化 (cache.paths 変更後)
aws codebuild invalidate-project-cache --project-name my-project

7-4. コスト最適化手法

fig06: コスト最適化マトリクス

コスト最適化: 4 つのテクニック

  • Spot fleet: EC2 Spot を活用し、GENERAL1_LARGE ビルドを最大 60-80% コスト削減
  • S3 / LOCAL キャッシュ: npm / pip / Docker Layer の再利用で繰返ビルド時間 50-70% 短縮
  • Lambda compute (BUILD_LAMBDA): 起動 < 1 秒・最小課金 100ms・短時間ジョブに最適
  • build_timeout 適正化: デフォルト 60 分 → 実績の 2 倍程度に短縮してコスト上限を設定

7-4-1. Spot fleet

Spot fleet を活用すると EC2 ベースのビルドコストを大幅削減できる。aws_codebuild_fleet リソースで Fleet を作成し、プロジェクトに紐付ける。

# Spot fleet 作成
resource "aws_codebuild_fleet" "spot" {
  name = "my-spot-fleet"
  base_capacity = 1
  compute_type  = "BUILD_GENERAL1_LARGE"
  environment_type = "LINUX_CONTAINER"
  overflow_behavior = "QUEUE"

  scaling_configuration {
 scaling_type = "TARGET_TRACKING_SCALING"
 target_value = 0.8
 max_capacity = 5
  }
}

# プロジェクトに Fleet を紐付け
resource "aws_codebuild_project" "spot_enabled" {
  name = "my-spot-project"

  environment {
 compute_type = "BUILD_GENERAL1_LARGE"
 image  = "aws/codebuild/standard:7.0"
 type= "LINUX_CONTAINER"
 privileged_mode = true

 fleet {
fleet_arn = aws_codebuild_fleet.spot.arn
 }
  }
  # ... (source / artifacts / logs_config は §6 参照)
}
# Fleet ステータス確認
aws codebuild batch-get-fleets \
  --names my-spot-fleet \
  --query 'fleets[0].{status:status,capacity:currentCapacity,compute:computeType}'

コンソール: CodeBuild → 「フリート」→ 「作成」→ スケーリング設定 → プロジェクト作成時に「マネージドフリート」を選択。

7-4-2. S3 キャッシュ + LOCAL DOCKER_LAYER_CACHE

# S3 キャッシュ (繰返ビルドで npm/pip を再利用)
resource "aws_codebuild_project" "s3_cached" {
  # ...
  cache {
 type  = "S3"
 location = "${aws_s3_bucket.cache.bucket}/codebuild-cache"
  }
}

# LOCAL キャッシュ (Docker Layer + Source + Custom)
resource "aws_codebuild_project" "local_cached" {
  # ...
  cache {
 type  = "LOCAL"
 modes = ["LOCAL_DOCKER_LAYER_CACHE", "LOCAL_SOURCE_CACHE", "LOCAL_CUSTOM_CACHE"]
  }
}
# buildspec.yml — cache.paths 設定例 (リスト形式必須)
version: 0.2
cache:
  paths:
 - '/root/.npm/**/*'
 - '/root/.gradle/caches/**/*'
 - '/root/.cache/pip/**/*'
phases:
  install:
 commands:
- npm ci # S3/LOCAL cache hit で 50-70% 短縮
# キャッシュヒット確認 (CloudWatch Logs でフィルタ)
aws logs filter-log-events \
  --log-group-name "/aws/codebuild/my-project" \
  --filter-pattern "Cache" \
  --query 'events[*].message'

7-4-3. Lambda compute (BUILD_LAMBDA)

コンテナ起動時間なし・最小課金単位 100ms でコスト効率が高い。ただし Docker daemon は使用不可 (§7-6 落とし穴 参照)。

resource "aws_codebuild_project" "lambda_compute" {
  name = "my-lambda-compute-project"

  environment {
 compute_type = "BUILD_LAMBDA_1GB"
 image  = "aws/codebuild/amazonlinux-x86_64-lambda-standard:corretto21"
 type= "LINUX_LAMBDA_CONTAINER"
 # privileged_mode = true は Lambda compute で無効 (設定しても効果なし)
  }
  # ...
}
# Lambda compute 設定確認
aws codebuild batch-get-projects \
  --names my-lambda-compute-project \
  --query 'projects[0].environment.{type:type,compute:computeType,image:image}'

コンソール: プロジェクト → 「環境」→ コンピューティング: 「Lambda」を選択 → メモリ 1GB / 2GB / 4GB から選択。

7-4-4. コスト比較表

設定$/分起動時間Docker 対応月額目安 (100ビルド×5分/日)
GENERAL1_SMALL (On-Demand)$0.005~30s~$750
GENERAL1_LARGE (On-Demand)$0.020~30s~$3,000
GENERAL1_LARGE (Spot fleet)~$0.006~30s~$900 (70%削減)
BUILD_LAMBDA_1GB$0.00003/秒<1s短時間ジョブに最適
GENERAL1_SMALL + S3 cache$0.005~30s~$375 (cache 50%短縮)

※ 1 ビルドあたり平均 5 分と仮定。リージョン・コンピュートタイプにより変動あり。

7-5. Terraform 使い分けまとめ

# 変数でコンピュートタイプを切替
variable "use_spot" {
  description = "Spot fleet を使用するか"
  type  = bool
  default  = true
}

variable "use_lambda_compute" {
  description = "Lambda compute を使用するか (Docker 不要のジョブのみ)"
  type  = bool
  default  = false
}

locals {
  environment_type = var.use_lambda_compute ? "LINUX_LAMBDA_CONTAINER" : "LINUX_CONTAINER"
  compute_type  = var.use_lambda_compute ? "BUILD_LAMBDA_1GB" : "BUILD_GENERAL1_LARGE"
  image= var.use_lambda_compute ? "aws/codebuild/amazonlinux-x86_64-lambda-standard:corretto21" : "aws/codebuild/standard:7.0"
}

コンソール: CodeBuild → 「ビルド履歴」→ 「期間」列で実際のビルド時間を確認 → プロジェクト設定の「タイムアウト」を実績の 2 倍程度に設定すると無駄なコスト上限を設けられる。

7-6. ローカルデバッグ・コスト関連の落とし穴

  1. BUILD_LAMBDA は Docker daemon 非対応: privileged_mode = true を設定しても docker build は実行できない。Docker イメージビルドが必要な場合は EC2 ベースコンピューティングを使用する。
  2. Spot fleet 中断によるビルド失敗: EC2 Spot が中断されるとビルドが途中で終了する。CodePipeline 連携時は retryOnFailure、スタンドアロン時は CloudWatch Events + Lambda で自動再試行を設定する。
  3. cache invalidation 手動実行が必要: buildspec.ymlcache.paths を変更しても S3 キャッシュは自動無効化されない。変更後は aws codebuild invalidate-project-cache --project-name <name> を実行する。
  4. LOCAL DOCKER_LAYER_CACHE は VPC 内ビルド非対応: VPC 設定と LOCAL キャッシュを併用する場合は S3 タイプのキャッシュを使用する。

8. まとめ + Vol3予告 + よくある落とし穴10選

8-1. 本記事の振り返り

本記事では CodeBuild を本番運用品質で活用するための 6 テーマを解説した。

テーマ到達点
§3buildspec.yml 完全解説phases/cache/artifacts/env/batch build 全機能習得
§4マルチアーキ Docker buildarm64+x86_64 manifest list を ECR push
§5VPC内ビルド + Secrets Managerprivate subnet+NAT+env-vars-files 統合
§6Terraform 完全実装aws_codebuild_project + webhook + IAM 完全 HCL
§7ローカルデバッグ + コスト最適化codebuild_build.sh + Spot/cache/Lambda で 60-80%削減
よくある落とし穴 10 選

  1. version: 0.1 使用 — 非推奨・version: 0.2 が必須。phases の finally / on-failure や exported-variables 等の機能が使えない。
  2. docker buildx driver 省略でマルチアーキ失敗docker buildx create --driver docker-container --use を必ず実行。省略すると linux/arm64 ビルドがスキップされ x86_64 のみになる。
  3. VPC subnet IP 不足で ENI 作成失敗 — CodeBuild は同時ビルド数分の ENI を作成する。/24 以上 (254 IP) を確保し subnet IP 残量を定期監視すること。
  4. BUILD_LAMBDA で docker build 実行 — Lambda compute は Docker daemon 非対応。docker build が必要なジョブは EC2 ベースコンピューティング (LINUX_CONTAINER) を使用する。
  5. cache.paths の YAML 構文罠cache.paths: '**/*' (文字列) と ['**/*'] (配列) は意味が異なる。必ずリスト形式で記述する。誤ると S3 cache が全く効かない。
  6. secrets-manager env var に大文字英数字以外を使用 — 環境変数名は大文字英数字とアンダースコアのみ許容。ハイフンを含む場合は parameter-store 経由で取得し別名を付与する。
  7. artifacts.files に相対パス指定ミスbase-directoryfiles の組み合わせに注意。**/* はサブディレクトリ含む全ファイル、* は直下のみ。discard-paths: yes の有無でディレクトリ構造が変わる。
  8. Spot fleet 中断後の再試行未設定 — Spot 割り込みによるビルド失敗を想定し、CodePipeline の retryOnFailure または CloudWatch Events + Lambda で自動再試行を設定する。
  9. ECR manifest list push 前の認証トークン期限切れaws ecr get-login-password の有効期限は 12 時間。長時間ビルドでは post_build 前に再認証ステップを追加する。
  10. CodeBuild Local builds のベースイメージ不一致 — ローカルで使用するイメージ (public.ecr.aws/codebuild/standard:7.0) と CodeBuild プロジェクト設定の image を揃えないと再現性が下がる。バージョンを必ず統一すること。

8-2. buildspec.yml チートシート

version: 0.2

env:
  variables:
 ENV_VAR: "value"
  parameter-store:
 SSM_PARAM: "/my/param"
  secrets-manager:
 DB_PASSWORD: "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:my-secret:password::"
  exported-variables:
 - ARTIFACT_VERSION
  git-credential-helper: yes

phases:
  install:
 runtime-versions:
nodejs: 20
 commands:
- npm ci
  pre_build:
 commands:
- aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ECR_REGISTRY
  build:
 commands:
- npm run build
- docker buildx create --driver docker-container --use
- docker buildx build --platform linux/amd64,linux/arm64 --push -t $ECR_REGISTRY/$IMAGE_NAME:$TAG .
 finally:
- echo "Build phase complete"
  post_build:
 on-failure: CONTINUE
 commands:
- echo "ARTIFACT_VERSION=$(cat version.txt)" >> $CODEBUILD_ENV_FILE

cache:
  paths:
 - '/root/.npm/**/*'
 - '/root/.cache/pip/**/*'

artifacts:
  files:
 - '**/*'
  base-directory: dist
  discard-paths: no

reports:
  jest-report:
 files:
- 'test-results/junit.xml'
 file-format: JUNITXML

8-3. 次のステップ

本記事で習得した技術を活用するためのロードマップ:

ステップ内容推奨リソース
① buildspec.yml 本番化phases/cache/artifacts/env を実プロジェクトに適用§3 チートシート
② マルチアーキ対応docker buildx + ECR manifest list を CI に組み込み§4 docker buildx 手順
③ VPC 内セキュア化private subnet + Secrets Manager で本番グレード§5 VPC 設定
④ IaC 一元管理Terraform で CodeBuild + IAM + webhook を管理§6 完全 HCL
⑤ コスト最適化Spot fleet + S3 cache で月額 50-70% 削減§7-4 比較表

Vol3 では CodePipeline V2 + CodeDeploy を用いたマルチステージパイプライン (dev → stg → prod) の本番運用を解説する。 CodeBuild を CI のビルドステージとして組み込み、ブルーグリーンデプロイメントまでの一連のフローを Terraform で完全実装する予定だ。

本記事で紹介した buildspec.yml のチートシートと落とし穴10選は、既存プロジェクトの CodeBuild 運用改善にもすぐに活用できる。まずは §7-4 のコスト比較表を確認し、現在のコンピュートタイプを見直すことから始めると、追加実装なしで即座にコスト削減効果が得られる。

8-4. 関連記事・シリーズナビ

AWS Code* ファミリーシリーズと関連実装記事へのリンクを以下にまとめた。Vol3 では CodePipeline V2 + CodeDeploy を中心とした CD パイプラインを解説する。

記事内容
Vol1: AWS Code* ファミリー全体像+選定CodeBuild/CodePipeline/CodeDeploy/CodeArtifact の選定指針・GHA vs Code* 比較
GHA OIDC + TerraformGHA workflow.yml と buildspec.yml の比較に有用

Vol1: 全体像+サービス選定+GHA/ecspresso比較

Vol3: CodePipeline V2 + CodeDeploy 本番運用

Vol4: CodeArtifact + CodeGuru 補完運用 (シリーズ完結)