デプロイ成功なのに本番が更新されない問題
Next.js を AWS Lambda コンテナでデプロイしている環境で、GitHub Actions のワークフローは成功しているのに本番サイトが一向に更新されない。こんな経験ありませんか?
実はこれ、Lambda エイリアスと CloudFront を組み合わせた構成で起こりがちな「見えない罠」です。デプロイパイプラインが Lambda の $LATEST しか更新せず、CloudFront が参照しているエイリアスは古いバージョンのままという状況が発生します。
この記事では、Lambda エイリアス構成で本番反映されない問題の原因特定から、GitHub Actions での完全自動化までを実装ベースで解説します。
問題が起きていた構成
まず、問題が発生していた環境の構成を整理します。
システム構成図
ユーザーからのリクエストは以下の経路で処理されていました:
- Route 53(DNS)
- CloudFront(CDN)
- Lambda エイリアス(
prd) - Lambda バージョン(固定)
Next.js アプリケーションは Docker コンテナイメージとして Lambda にデプロイされ、CloudFront 経由でカスタムドメインで配信される構成です。
なぜエイリアス構成なのか
Lambda の Provisioned Concurrency(コールドスタート対策機能)を使うには、$LATEST ではなくバージョンを発行してエイリアスに紐づける必要があります。この構成自体は正しい設計ですが、デプロイフローがそれに対応していませんでした。
何が起きていたのか:二重の罠
GitHub Actions でデプロイすると update-function-code コマンドで Lambda の $LATEST が更新されます。ここまでは問題ありません。
しかし CloudFront のオリジン設定は $LATEST ではなく、エイリアス prd を参照していました。このエイリアスは特定のバージョン(例:バージョン 72)に固定されているため、$LATEST がいくら更新されてもユーザーには古いコードが返り続けます。
トラップ1:エイリアスの更新漏れ
# GitHub Actions がやっていること
CloudFront → prd エイリアス → バージョン72(古い) ← ユーザーが見ている
GitHub Actions → $LATEST(最新) ← ここにしかデプロイされないGitHub Actions のワークフローには publish-version も update-alias も含まれていなかったため、エイリアスは古いバージョンを指したままでした。
トラップ2:CloudFront の長時間キャッシュ
さらに CloudFront のデフォルトビヘイビアで TTL が 24 時間に設定されていたため、仮にエイリアスを手動で更新しても、キャッシュが切れるまで反映されません。
前任者は手動でバージョン発行とエイリアス更新を行っていたと推測されますが、Invalidation 履歴が 0 件だったことから、TTL 切れを待つ運用をしていた可能性もあります。
原因特定までの調査フロー
実際に問題を特定するまでの調査手順を再現します。
mainブランチにマージ → GitHub Actions 成功 → 本番サイトに反映されない- CloudFront のキャッシュを疑う → Invalidation 実行 → 反映されない
- Lambda コンソールで確認 →
$LATESTは最新だが、エイリアスprdがバージョン 72(古い)に固定されていた - 手動でバージョン 73 を発行 → エイリアスを 73 に更新 → Invalidation → 反映された
- GitHub Actions のワークフローを確認 →
publish-versionもupdate-aliasもないことを確認 → 根本原因特定
この時点で、デプロイパイプラインにバージョン発行とエイリアス更新が含まれていないことが明確になりました。
GitHub Actions での自動化実装
問題を解決するため、GitHub Actions のワークフローに以下の 3 ステップを追加しました。
ステップ1:Lambda の更新完了を待つ
update-function-code は非同期で処理されるため、完了を待たずに次のステップに進むと、更新途中の古いコードでバージョンが作られる可能性があります。
- name: Wait for Lambda update to complete
run: |
aws lambda wait function-updated \
--function-name ${{ env.LAMBDA_FUNCTION_NAME }}このコマンドは Lambda が Active 状態になるまで待機します。内部的には GetFunctionConfiguration API を使ってポーリングしているため、後述する IAM 権限の設定が重要です。
ステップ2:バージョン発行とエイリアス更新
main ブランチのデプロイ時のみ、新しいバージョンを発行してエイリアスを更新します。
- name: Publish Lambda version and update alias
if: env.BRANCH_NAME == 'main'
run: |
VERSION=$(aws lambda publish-version \
--function-name ${{ env.LAMBDA_FUNCTION_NAME }} \
--query 'Version' --output text)
echo "Published Lambda version: ${VERSION}"
aws lambda update-alias \
--function-name ${{ env.LAMBDA_FUNCTION_NAME }} \
--name prd \
--function-version $VERSION
echo "Updated alias prd to version ${VERSION}"publish-version で現在の $LATEST のスナップショットをバージョンとして固定し、そのバージョン番号をエイリアスに割り当てます。
ステップ3:CloudFront キャッシュ無効化
エイリアスが更新されても、CloudFront のキャッシュが残っていると古いコンテンツが返されます。すべてのパスでキャッシュを無効化します。
- name: Invalidate CloudFront cache
if: env.BRANCH_NAME == 'main'
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
--paths "/*"CLOUDFRONT_DISTRIBUTION_ID は GitHub の Environment Variables(prd 環境)に設定しています。Secrets ではなく Variables を使っているのは、Distribution ID は秘密情報ではなくログに表示されても問題ないためです。
IAM 権限の追加
GitHub Actions の IAM ロールに以下の権限を追加する必要があります。
| 権限 | 用途 |
|---|---|
lambda:PublishVersion |
バージョン発行 |
lambda:UpdateAlias |
エイリアス更新 |
lambda:GetFunction |
wait コマンド用 |
lambda:GetFunctionConfiguration |
wait コマンド用(後述の通り、これがないとエラーになる) |
cloudfront:CreateInvalidation |
キャッシュ無効化 |
Lambda 系の権限は既存ポリシー(AllowLambdaUpdateFunction)に追加し、CloudFront は AllowCloudFrontInvalidation として新規ポリシーを作成しました。サービスごとにポリシーを分けることで、権限の管理が容易になります。
実装時のハマりどころ
wait コマンドで権限エラー(exit code 255)
最初のデプロイで wait function-updated が exit code 255 で失敗しました。原因は lambda:GetFunctionConfiguration の権限不足です。
wait コマンドは内部的に GetFunctionConfiguration API をポーリングして Lambda の状態を監視しています。AWS CLI のドキュメントには GetFunction が必要とだけ書かれていますが、実際には GetFunctionConfiguration も必要です。最初は GetFunction だけ追加して失敗し、ログを確認して GetFunctionConfiguration を追加したところ解決しました。
dev 環境との違いに注意
dev 環境は CloudFront を経由せず Lambda Function URL に直接アクセスする構成だったため、この問題は発生していませんでした。if: env.BRANCH_NAME == 'main' で本番のみ実行するようにしています。
CloudFront キャッシュ TTL の見直し
デフォルトビヘイビアの TTL が 24 時間に設定されていたため、Invalidation で都度クリアする運用にしています。根本的には SSR ページのキャッシュポリシーを CachingDisabled に変更するか、Next.js 側で適切な Cache-Control ヘッダーを返す対応が別途必要です。
自動化後のデプロイフロー
修正後、本番デプロイは以下のように自動化されました:
mainブランチにマージ- GitHub Actions が Docker イメージをビルド
- Lambda の
$LATESTを更新 - Lambda の更新完了を待機
- 新しいバージョンを発行(例:バージョン 74)
- エイリアス
prdをバージョン 74 に更新 - CloudFront のキャッシュを無効化
- 本番サイトに即座に反映
この一連の流れが完全自動化され、「マージしたら本番に反映される」が実現しました。
まとめ:エイリアス構成でのデプロイ自動化
Lambda エイリアスを使う構成では、デプロイパイプラインに publish-version と update-alias が必須です。update-function-code だけでは $LATEST しか更新されず、エイリアス経由のリクエストには反映されません。
さらに CloudFront を前段に置いている場合は Invalidation も忘れずに実行する必要があります。この 2 つが揃って初めて、CI/CD パイプラインが完結します。
Provisioned Concurrency を使う場合はエイリアス構成が必須ですが、デプロイフローもそれに合わせて設計することで、手動オペレーションをゼロにできます。
AWS Lambda と CI/CD をさらに強化する関連記事
Lambda のデプロイ自動化を習得したら、開発効率化やセキュリティ強化も合わせて実装しましょう:
セキュリティ・認証
- 「それ、上げちゃダメ!」GitHub管理で絶対守るべきセキュリティルールと対処法 – Lambda 環境変数やシークレット管理のベストプラクティスを解説
- Claude Code Securityとは?AIが脆弱性を検出し修正パッチまで提案する新時代のセキュリティツール – デプロイ前のコードレビュー自動化でセキュリティリスクを削減
開発ツール・効率化
- git-lrc – Gitコミット時にAIが自動コードレビューする無料開発支援ツール – Lambda へのデプロイ前にコード品質をチェック
- WordPress運用保守の「地味にしんどい」をAIで解決する5つの実践アプローチ – インフラ運用の自動化アイデア
設計・アーキテクチャ
- バイブコーディング時代に「自分で全部やったほうが速い」が危険な理由 – CI/CD パイプラインの設計思想とチーム開発のベストプラクティス
- AI開発時代、エンジニア的思考とビジネス的思考はどちらが強いのか – インフラ自動化の投資対効果の考え方
