はじめに
こんにちは!
普段はJavaのフルスタックエンジニアをしながら、サーバーレスシステムの構築もしています。
今回、実務で初めて AWS Lambdaの開発にCI/CDを組み込む というタスクに挑戦しました。
インフラ周りやAWSネイティブなCI/CDツールの実務経験が浅かったため、生成AI(Copilot)と二人三脚でペアプロしながらなんとか形にすることができました。
GitHub Actionsの記事はよく見かけるのですが、 CodeCommit / CodePipeline / CodeBuild を使った「1リポジトリ複数Lambdaの差分デプロイ」の情報は意外と少なかったので、同じように悩んでいる方の参考になればと思い、実装の全容を共有します!
今回実現したこと(要件)
モノリシックなリポジトリ(1リポジトリ)の中で、複数のLambda関数を管理する構成です。
-
課題: コードを修正するたびに、すべてのLambdaが毎回再ビルド・再デプロイされるのは非効率(時間もコストもかかる)。
-
ゴール: CodeCommitへのプッシュをトリガーにCodePipelineを起動し、CodeBuild内で「修正されたLambda(差分があるコード)」だけを自動検知。AWS SAM経由でS3バケットを介してターゲットのLambdaだけをアップロード・デプロイする。
全体構成イメージ
大まかな処理の流れは以下の通りです。
- 開発者が特定のLambdaのコードを修正して CodeCommit の対象ブランチにプッシュ
- CodePipelineが検知して自動起動
- CodeBuildが起動し、
git diffを使って前回コミットからの変更があったLambdaのディレクトリを特定 - 変更があったディレクトリに対してのみ
sam buildおよびsam deployを実行 - AWS SAMがS3バケットを介して差分のある成果物のみをLambdaへアップロード
ディレクトリ構造
今回は例として、lambda-a と lambda-b の2つの関数を1つのリポジトリで管理する構成にしました。CodeBuildの司令塔となる buildspec.yml はリポジトリのルートに配置します。Pythonの外部ライブラリをインストールできるように requirements.txt も含めています。
.
├── buildspec.yml # CodeBuildの実行定義(今回の肝!)
├── lambda-a/
│ ├── app.py # Lambda Aのコード(Python)
│ ├── requirements.txt # Lambda A用の依存ライブラリ
│ └── template.yaml # Lambda A用のSAMテンプレート
└── lambda-b/
├── app.py # Lambda Bのコード
├── requirements.txt # Lambda B用の依存ライブラリ
└── template.yaml # Lambda B用のSAMテンプレート
実装コード
1. AWS SAM テンプレート(例:lambda-a/template.yaml)
各Lambdaのディレクトリ配下にそれぞれの template.yaml を配置します。ランタイムは現行の python3.12 を指定しています。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda A Service
Resources:
LambdaAFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: my-lambda-a
CodeUri: .
Handler: app.lambda_handler
Runtime: python3.14
Architectures:
- x86_64
2. CodeBuild 設定ファイル(buildspec.yml)
ここが今回の最大のポイントです。CodeBuild内で直前のコミットとの git diff を取得し、変更があったディレクトリを動的に判定してSAMを叩いています。ランタイムバージョンも python: 3.12 を指定します。
version: 0.2
phases:
install:
runtime-versions:
python: 3.14
commands:
- echo "Installing AWS SAM CLI..."
# CodeBuildの環境にはデフォルトでSAMが入っていることが多いですが、必要に応じて最新化
pre_build:
commands:
- echo "Detecting changes..."
# CodePipeline経由だとシャロークローン(履歴なし)になることがあるため、念のため取得
- git fetch --unshallow || true
# 前回コミット(HEAD~1)との差分ファイル名一覧を取得
- CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD)
- echo "Changed files:\n$CHANGED_FILES"
build:
commands:
# --- Lambda A のデプロイ判定 ---
- |
if echo "$CHANGED_FILES" | grep -q "^lambda-a/"; then
echo "==== Deploying Lambda A ===="
cd lambda-a
sam build
sam deploy --no-confirm-changeset --no-fail-on-empty-changeset \
--s3-bucket my-sam-deploy-bucket-unique-name \
--stack-name my-lambda-a-stack \
--capabilities CAPABILITY_IAM
cd ..
else
echo "==== Skip Lambda A (No changes) ===="
fi
# --- Lambda B のデプロイ判定 ---
- |
if echo "$CHANGED_FILES" | grep -q "^lambda-b/"; then
echo "==== Deploying Lambda B ===="
cd lambda-b
sam build
sam deploy --no-confirm-changeset --no-fail-on-empty-changeset \
--s3-bucket my-sam-deploy-bucket-unique-name \
--stack-name my-lambda-b-stack \
--capabilities CAPABILITY_IAM
cd ..
else
echo "==== Skip Lambda B (No changes) ===="
fi
post_build:
commands:
- echo "Build and Deployment process completed successfully!"
工夫したポイント・ハマりどころ
1. CodeBuild内での git diff の挙動
CodePipelineのソースステージからCodeBuildにソースが渡される際、デフォルトの設定のままだと .git ディレクトリ(Gitの履歴情報)が含まれず、git diff が実行できずにエラーになる罠がありました。
CodePipelineのソースステージの設定(CodeCommit)で、「出力成果物のフォーマット」を「完全リポジトリ(Full clone)」 に変更することで、CodeBuild内でも無事にGitコマンドが使えるようになりました。
2. 生成AIとの二人三脚
「シェルスクリプトで特定ディレクトリの差分があるときだけコマンドを実行したい」という部分の記述(grep -q の使い方など)は、生成AIにガッツリ書いてもらいました。
CodeBuildのログをAIにペーストしながら、「Gitのリファレンスエラーが出るんだけど何で?」と聞き、上記の「完全リポジトリ(Full clone)」の設定が必要なことに気づけたのはAIのおかげです。
まとめ
初めての実務でのCI/CD構築で、しかもAWSのCodeSuite縛りという少し硬派な環境でしたが、AIの力を借りつつ、プロダクトにとって非常に効率の良いパイプラインを組むことができました!
AWSネイティブな環境で、同じように1つのリポジトリで複数のLambdaをスマートに管理したい方の参考になれば幸いです!