「update-function-code で本番に上書きデプロイしたら、不具合が全ユーザーに一斉に出た」——Lambda は1コマンドでデプロイできてしまうがゆえに、安全装置のない出荷をしがちです。決済の確定処理やユーザーの操作を捌くAPIで、新バージョンの不具合が100%のトラフィックに即座に当たるのは、本番運用として許容できません。
この記事は、AWS Lambda をゼロダウンタイム・自動ロールバック付きで安全にデプロイするための実装ガイドです。バージョンとエイリアスという土台から、カナリアリリース、自動ロールバック、IaC(SAM/CDK/Terraform)の選定、OIDCによる鍵レスCI/CDまでを一気通貫で解説します。題材として、私が中核開発者として構築したサーバーレス決済プラットフォーム(本番二重課金0件)での出荷判断も交えます。Lambda 本体の実行モデルは姉妹記事 AWS Lambda 本番運用ガイド に委ね、本稿は**「どう安全に出荷するか」一点**に集中します。
この記事のルール:仕様・パラメータ名・定義済み設定名は AWS 公式ドキュメント(2026年6月時点) に基づきます。CodeDeployの設定名やランタイム、各ツールの仕様は改定されます。本番投入前に必ず公式(末尾「参考」)で最新値を確認してください。
0. メンタルモデル:「不変なバージョン」と「動くポインタ」を分ける
安全なデプロイの全ては、この2つの分離から始まります。
- バージョン=不変のスナップショット。公開(publish)すると、その時点のコードと設定が固定された番号付きバージョンになる。番号は単調増加で、削除・再作成しても再利用されない。
- エイリアス=バージョンを指す、動かせるポインタ。
liveやprodという名前で特定バージョンを指す。デプロイとは**「エイリアスの指す先を新バージョンに、安全に切り替える」**こと。 $LATESTは可変。update-function-codeのたびに上書きされる。だから本番トラフィックを$LATESTに直接向けない——必ずエイリアス経由で呼ぶ。- カナリア=ポインタを一気に動かさず、重みで少しずつ動かす。新バージョンに10%だけ流し、問題なければ100%へ。問題があれば自動で戻す。
この「不変な土台+動くポインタ+段階的切り替え+自動ロールバック」が、本記事の設計です。
1. バージョンとエイリアス:本番はエイリアス経由で呼ぶ
まず土台を作ります。$LATEST に対してコードを更新し、安定したらバージョンを公開し、エイリアスをそのバージョンに向ける。クライアントはエイリアスのARNを呼びます。
# 1) コードを更新($LATEST が変わる。本番はまだこれを見ていない)
aws lambda update-function-code --function-name orders --zip-file fileb://build.zip
# 2) 更新完了を待つ(重要。LastUpdateStatus=Successful になるまで次の操作は失敗する)
aws lambda wait function-updated-v2 --function-name orders
# 3) 不変バージョンを公開(番号が振られる。例: 42)
VERSION=$(aws lambda publish-version --function-name orders --query Version --output text)
# 4) エイリアス live をそのバージョンへ。クライアントは live を呼ぶ
aws lambda update-alias --function-name orders --name live --function-version "$VERSION"
ここで効く公式仕様を3つ:
- エイリアスは「修飾ARN(qualified ARN)」。
...:function:orders:liveのように修飾子(バージョン番号 or エイリアス名)が付く。修飾子なし(unqualified)で呼ぶと暗黙的に$LATESTが走る——本番では事故の元。 - プロビジョンド同時実行・SnapStart は公開バージョン/エイリアスでのみ有効(
$LATEST不可)。レイテンシ対策(コールドスタート最適化)を効かせるにも、この土台が前提。 - すべての設定変更がバージョンを公開するわけではない。例えば予約済み同時実行はバージョンを作らない(関数全体の運用設定のため)。
2. カナリアリリース:加重エイリアスで「10%だけ」流す
エイリアスは最大2つの公開バージョンを指し、重みでトラフィックを分配できます(routing config / AdditionalVersionWeights)。これがカナリアリリースの心臓部です。
# エイリアス live:97%を現行、3%を新バージョン(43)へ。問題なければ重みを上げていく
aws lambda update-alias --function-name orders --name live \
--function-version 42 \
--routing-config 'AdditionalVersionWeights={"43"=0.03}'
公式が課す制約を押さえます(守らないとエラー or 事故)。
- 両バージョンとも公開済みであること(
$LATEST不可)。 - 両バージョンの実行ロールが同一であること。
- **DLQ設定が同一(または両方なし)**であること。
- 同一関数の2バージョンであること。
手で重みを上げ下げするのは現実的でないので、CodeDeployに任せて自動化します(次章)。
プロビジョンド同時実行との両立:カナリア中にコールドスタックを避けたいなら、ルーティングが有効な間だけプロビジョンド同時実行を多めに用意できます(公式が言及)。レイテンシSLAのあるAPIでは、カナリアとプロビジョンドを組み合わせます。
3. 自動ロールバック付きカナリア:CodeDeploy + SAM
CodeDeploy は、加重エイリアスの重みを定義済みのスケジュールで自動的に動かし、CloudWatchアラームが鳴れば自動ロールバックします。手動の重み調整は不要になります。
3.1 定義済みデプロイ設定(公式の正式名)
| 種別 | 設定名(CodeDeployDefault. を冠する) | 挙動 |
|---|---|---|
| カナリア | LambdaCanary10Percent5Minutes / 10Minutes / 15Minutes / 30Minutes | 10%流し、指定分後に残り90%を一括 |
| リニア | LambdaLinear10PercentEvery1Minute / Every2Minutes / Every3Minutes / Every10Minutes | 10%ずつ段階的に増やす |
| 一括 | LambdaAllAtOnce | 一気に100%(カナリアなし) |
名前の注意:リニアの最短だけ
Every1Minute(単数)、他は複数形(Every2Minutes)。SAM のDeploymentPreference.Typeでは先頭のCodeDeployDefault.Lambdaを外した短縮名(例:Canary10Percent10Minutes)を使います。
3.2 SAM なら数行で「検証+自動ロールバック」が組める
SAM の AutoPublishAlias(コード変更を検知して自動でバージョン公開+エイリアス更新)と DeploymentPreference(カナリア戦略・アラーム・フック)を組み合わせると、安全デプロイが宣言的に書けます。
# template.yaml(AWS SAM):カナリア+プリ/ポスト検証+アラームで自動ロールバック
Resources:
OrdersFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs22.x
Architectures: [arm64] # Arm64で実行料金20%減(互換があれば)
AutoPublishAlias: live # これが無いと DeploymentPreference は使えない
DeploymentPreference:
Type: Canary10Percent5Minutes # 10%を5分流し、問題なければ残りを切替
Alarms: # どれか1つでも ALARM になれば自動ロールバック
- !Ref OrdersErrorsAlarm
- !Ref OrdersLatencyP99Alarm
Hooks:
PreTraffic: !Ref PreTrafficCheck # 切替前にスモークテスト
PostTraffic: !Ref PostTrafficCheck # 切替後に結合検証
# 新バージョンのエラー率を監視(鳴ったらロールバック)
OrdersErrorsAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
Namespace: AWS/Lambda
MetricName: Errors
Dimensions:
- { Name: FunctionName, Value: !Ref OrdersFunction }
- { Name: Resource, Value: !Sub "${OrdersFunction}:live" }
Statistic: Sum
Period: 60
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
プリ/ポストトラフィックフックは、CodeDeployがトラフィック切替の前後に呼ぶ検証用Lambdaです。フックは結果を PutLifecycleEventHookExecutionStatus でCodeDeployにコールバックし、失敗すればデプロイは中断・ロールバックされます。フック関数名は慣例で CodeDeployHook_ で始めます。
# プリトラフィックフック:切替前に新バージョンをスモークテストし、合否をCodeDeployへ返す
import boto3
codedeploy = boto3.client("codedeploy")
def handler(event, context):
deployment_id = event["DeploymentId"]
hook_id = event["LifecycleEventHookExecutionId"]
status = "Succeeded"
try:
run_smoke_tests() # 新バージョン(エイリアス未切替の version)を直接叩いて検証
except Exception:
status = "Failed" # ここでFailedを返すと切替されずロールバックされる
codedeploy.put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id, lifecycleEventHookExecutionId=hook_id, status=status,
)
return {"status": status}
最初のデプロイは2段階:CodeDeployは「切替元の旧バージョン」が必要なので、初回は
AutoPublishAliasだけでデプロイ→2回目からDeploymentPreferenceを有効化、という順序になります。
4. IaC の選定:SAM / CDK / Terraform / Serverless Framework
「どれでLambdaを管理するか」は買い手意図の強い問いです。安全デプロイの組みやすさとチームの資産で選びます。
| ツール | 安全デプロイ | 強み | 向くチーム |
|---|---|---|---|
| AWS SAM | ◎ DeploymentPreference 数行 | サーバーレス特化・最小設定でカナリア | AWSサーバーレスに全振り、最短で安全に出荷したい |
| AWS CDK | ◎ LambdaDeploymentGroup | 型安全なIaC・NodejsFunctionのesbuildバンドル | 型・補完・複雑な構成をコードで組みたい |
| Terraform | ○ 自前で組む | 多クラウド・既存Terraform資産 | すでにTerraform運用・AWS以外も管理 |
| Serverless Framework | ○ プラグイン | YAMLの手軽さ | 小規模・素早く。ただしv4はライセンス注意 |
要点:
- SAM:
AutoPublishAlias+DeploymentPreference(Type/Alarms/Hooks)で、カナリア+自動ロールバックが最小コスト。CloudFormationの拡張なので、生成されるリソースも追える。 - CDK:
aws-lambdaのFunction/NodejsFunction(esbuildで自動トランスパイル・バンドル)と、aws-codedeployのLambdaDeploymentGroup+LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTESで同等の安全デプロイ。Python/Goのバンドルはalphaモジュール。 - Terraform:
aws_lambda_function(publish = true)+aws_lambda_alias(routing_config.additional_version_weights)+aws_codedeploy_app(compute_platform = "Lambda")/aws_codedeploy_deployment_groupを自分で組み合わせる。制御は効くが配線は増える。 - Serverless Framework:
serverless.ymlの手軽さは随一だが、v4 は「直近会計年度の売上が200万ドル超の個人・組織」に有償サブスクリプションが必要(v3は無償・OSS継続)。組織規模によってはコストを織り込む。
# Terraform:バージョン公開+エイリアスの加重ルーティング(カナリアの土台)
resource "aws_lambda_function" "orders" {
function_name = "orders"
role = aws_iam_role.orders.arn
handler = "index.handler"
runtime = "nodejs22.x"
architectures = ["arm64"]
filename = "build.zip"
publish = true # 変更のたびに不変バージョンを公開
}
resource "aws_lambda_alias" "live" {
name = "live"
function_name = aws_lambda_function.orders.function_name
function_version = aws_lambda_function.orders.version
routing_config {
additional_version_weights = { } # CodeDeploy/手動でカナリア時に重みを注入
}
}
5. CI/CD:GitHub Actions から「鍵レス」でデプロイする
長期的なAWSアクセスキーをGitHub Secretsに置くのは、漏洩リスクの塊です。公式の正解は OIDC(OpenID Connect)——GitHub が発行する短命のJWTを、AWSのIAMロールに AssumeRoleWithWebIdentity で交換し、一時クレデンシャルで動く。保存する鍵がゼロになります。
# .github/workflows/deploy.yml:OIDCで鍵レスにSAMデプロイ(長期キーをSecretsに置かない)
name: deploy
on:
push: { branches: [main] }
permissions:
id-token: write # OIDCトークンの発行に必須
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v6 # 推奨:OIDCで一時クレデンシャル取得
with:
role-to-assume: arn:aws:iam::123456789012:role/github-deploy
aws-region: ap-northeast-1
- uses: aws-actions/setup-sam@v2
- run: sam build
- run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset
IAM側は、信頼ポリシーで発行者 token.actions.githubusercontent.com・オーディエンス sts.amazonaws.com を許可し、**sub クレームで「このリポジトリのこのブランチだけ」**に絞ります(最小権限)。OIDCの詳しい設計は姉妹記事 OIDCで実現する鍵レスCI/CD に。
6. ゼロダウンタイムの落とし穴:関数ステートを待つ
最後に、地味だが本番で必ず踏む落とし穴。Lambdaの更新は非同期で、ステートマシンを持ちます。
| State | 意味 | 操作可否 |
|---|---|---|
| Pending | 作成/設定中(VPCのENI作成など) | 呼び出し不可。更新も失敗する |
| Active | 稼働中 | 呼び出せる唯一の状態 |
| Inactive | アイドルで回収(VPCは14日) | 次回呼び出しは一旦失敗→Pendingで再作成 |
| Failed | 失敗 | 削除して作り直す |
加えて LastUpdateStatus(Successful/Failed/InProgress)があり、InProgress の間は次の UpdateFunctionCode/UpdateFunctionConfiguration/PublishVersion が失敗(ResourceConflictException / 409)します。だから:
- コード更新 →
wait function-updated-v2→ 設定更新/公開、という順序を守る(CIで連続実行すると409で落ちる典型)。 - VPC関数は反映に時間がかかる(Hyperplane ENI。詳細はコールドスタート記事)。デプロイ直後に叩くテストは、Activeを確認してから。
# CIでの安全な連続更新:各ステップで完了を待ってから次へ
aws lambda update-function-code --function-name orders --zip-file fileb://build.zip
aws lambda wait function-updated-v2 --function-name orders # ← これを挟まないと409
aws lambda update-function-configuration --function-name orders --environment "Variables={LOG_LEVEL=INFO}"
aws lambda wait function-updated-v2 --function-name orders
aws lambda publish-version --function-name orders
7. まとめ:安全デプロイ・チートシート
- 土台:
$LATESTは可変。**公開バージョン(不変)+エイリアス(ポインタ)**を作り、本番は修飾ARN(エイリアス)経由で呼ぶ。 - カナリア:加重エイリアス(最大2バージョン・重み分配)。両バージョンは公開済み・同一実行ロール・同一DLQが条件。
- 自動化:CodeDeploy の
Canary10Percent5Minutes等+プリ/ポストフックで検証+CloudWatchアラームで自動ロールバック。SAMならAutoPublishAlias+DeploymentPreferenceの数行。 - IaC:安全デプロイ最短はSAM、型安全はCDK、多クラウド/既存資産はTerraform、手軽だがServerless v4は年商200万ドル超で有償。
- CI/CD:OIDCで鍵レス(
id-token: write+AssumeRoleWithWebIdentity、subでリポジトリ/ブランチを限定)。 - 落とし穴:更新は非同期。
LastUpdateStatus=Successfulを待つ(InProgress中の更新は409)。VPC関数は反映に時間がかかる。
私は決済プラットフォームで、本番二重課金0件を支える出荷規律として「不変バージョン+カナリア+アラーム自動ロールバック+鍵レスCI/CD」を徹底しました。新バージョンの不具合が全ユーザーに当たる前に、10%で検知して自動で戻す——これが、止められない決済基盤を安全に進化させ続ける土台です。
「自社のLambdaを、止めず・壊さず・自動で戻せる形で継続的に出荷したい」——カナリア戦略の設計からCI/CDの鍵レス化、IaCの選定まで、一人 × 生成AI(Claude Code)の速さで伴走します。 既存のデプロイフロー監査からでも、お気軽にご相談ください。
参考(公式ドキュメント)
- Lambda function versions / aliases — 不変バージョン、修飾/非修飾ARN
- Implementing canary deployments using alias routing — 加重エイリアス、
AdditionalVersionWeights、制約 - Deployment configurations (CodeDeploy) —
LambdaCanary…/LambdaLinear…/LambdaAllAtOnce - Gradual deployments / DeploymentPreference (SAM) —
AutoPublishAlias、Type/Alarms/Hooks - AWS::Serverless::Function DeploymentPreference — アラームによる自動ロールバック
- AWS CDK aws-codedeploy LambdaDeploymentGroup — CDKでの安全デプロイ
- Terraform aws_lambda_alias / aws_codedeploy_app — 加重ルーティング、
compute_platform - Configuring OpenID Connect in Amazon Web Services (GitHub Docs) —
id-token: write、AssumeRoleWithWebIdentity - Lambda function states — Pending/Active/Inactive/Failed、
LastUpdateStatus - Serverless Framework pricing — v4の有償条件(年商200万ドル)