# Lambdaの安全なデプロイ：バージョン・エイリアス・カナリアリリース（CodeDeploy）とSAM/CDK/Terraform選定

> AWS Lambdaをゼロダウンタイムで安全にデプロイする実装ガイド。不変なバージョンとエイリアス、加重エイリアスとCodeDeployのカナリア/リニア配信、プリ/ポストトラフィックフックとCloudWatchアラームによる自動ロールバック、関数ステートの待機、SAM/CDK/Terraform/Serverless Frameworkの選定、GitHub Actions OIDCによる鍵レスCI/CDまでAWS公式仕様に忠実な実コードで解説します。

- 公開日: 2026-06-27
- 著者: 友田 陽大
- タグ: AWS, Lambda, CI/CD, サーバーレス, IaC
- URL: https://tomodahinata.com/blog/aws-lambda-deployment-versions-aliases-canary-sam-cdk-terraform-guide

## 要点

- 公開バージョンはコード+設定の不変スナップショット、エイリアスはそれを指すポインタ。$LATESTは可変なので本番はエイリアス経由で呼ぶ
- カナリアは加重エイリアス（最大2バージョンへ重み分配）＋CodeDeployで実現。Canary10Percent5Minutes等の定義済み設定で段階的に流す
- プリ/ポストトラフィックフックで検証し、CloudWatchアラームが鳴れば自動ロールバック。SAMならAutoPublishAlias＋DeploymentPreferenceの数行で完結
- IaC選定：SAMは安全デプロイが最小設定で組める／CDKは型安全とesbuild／Terraformは多クラウド・既存資産／Serverless v4は年商200万ドル超で有償
- CI/CDはGitHub Actions OIDCで鍵レス（id-token:write＋AssumeRoleWithWebIdentity）。更新はLastUpdateStatusがSuccessfulになるまで待つ

---

「`update-function-code` で本番に上書きデプロイしたら、不具合が全ユーザーに一斉に出た」——Lambda は1コマンドでデプロイできてしまうがゆえに、**安全装置のない出荷**をしがちです。決済の確定処理やユーザーの操作を捌くAPIで、新バージョンの不具合が**100%のトラフィックに即座に当たる**のは、本番運用として許容できません。

この記事は、AWS Lambda を**ゼロダウンタイム・自動ロールバック付きで安全にデプロイする**ための実装ガイドです。**バージョンとエイリアス**という土台から、**カナリアリリース**、**自動ロールバック**、**IaC（SAM/CDK/Terraform）の選定**、**OIDCによる鍵レスCI/CD**までを一気通貫で解説します。題材として、私が中核開発者として構築した[サーバーレス決済プラットフォーム](/case-studies/payment-platform-reliability)（**本番二重課金0件**）での出荷判断も交えます。Lambda 本体の実行モデルは姉妹記事 [AWS Lambda 本番運用ガイド](/blog/aws-lambda-production-guide) に委ね、本稿は**「どう安全に出荷するか」一点**に集中します。

> **この記事のルール**：仕様・パラメータ名・定義済み設定名は **AWS 公式ドキュメント（2026年6月時点）** に基づきます。CodeDeployの設定名やランタイム、各ツールの仕様は改定されます。本番投入前に必ず公式（末尾「参考」）で最新値を確認してください。

---

## 0. メンタルモデル：「不変なバージョン」と「動くポインタ」を分ける

安全なデプロイの全ては、この2つの分離から始まります。

- **バージョン＝不変のスナップショット**。公開（publish）すると、その時点の**コードと設定**が固定された番号付きバージョンになる。番号は単調増加で、削除・再作成しても**再利用されない**。
- **エイリアス＝バージョンを指す、動かせるポインタ**。`live` や `prod` という名前で特定バージョンを指す。デプロイとは**「エイリアスの指す先を新バージョンに、安全に切り替える」**こと。
- **`$LATEST` は可変**。`update-function-code` のたびに上書きされる。だから**本番トラフィックを `$LATEST` に直接向けない**——必ずエイリアス経由で呼ぶ。
- **カナリア＝ポインタを一気に動かさず、重みで少しずつ動かす**。新バージョンに10%だけ流し、問題なければ100%へ。問題があれば自動で戻す。

この「不変な土台＋動くポインタ＋段階的切り替え＋自動ロールバック」が、本記事の設計です。

---

## 1. バージョンとエイリアス：本番はエイリアス経由で呼ぶ

まず土台を作ります。`$LATEST` に対してコードを更新し、**安定したらバージョンを公開**し、**エイリアスをそのバージョンに向ける**。クライアントは**エイリアスのARN**を呼びます。

```bash
# 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` 不可）。レイテンシ対策（[コールドスタート最適化](/blog/aws-lambda-cold-start-snapstart-provisioned-concurrency-performance-guide)）を効かせるにも、この土台が前提。
- **すべての設定変更がバージョンを公開するわけではない**。例えば**予約済み同時実行はバージョンを作らない**（関数全体の運用設定のため）。

---

## 2. カナリアリリース：加重エイリアスで「10%だけ」流す

エイリアスは**最大2つの公開バージョンを指し、重みでトラフィックを分配**できます（routing config / `AdditionalVersionWeights`）。これが**カナリアリリースの心臓部**です。

```bash
# エイリアス 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`（カナリア戦略・アラーム・フック）を組み合わせると、**安全デプロイが宣言的に**書けます。

```yaml
# 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_` で始めます。

```python
# プリトラフィックフック：切替前に新バージョンをスモークテストし、合否を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継続）。組織規模によってはコストを織り込む。

```hcl
# 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` で交換し、**一時クレデンシャルで動く**。保存する鍵がゼロになります。

```yaml
# .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](/blog/github-actions-oidc-keyless-cicd-aws-gcp-guide) に。

---

## 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。[詳細はコールドスタート記事](/blog/aws-lambda-cold-start-snapstart-provisioned-concurrency-performance-guide)）。デプロイ直後に叩くテストは、Activeを確認してから。

```bash
# 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](https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html) / [aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) — 不変バージョン、修飾/非修飾ARN
- [Implementing canary deployments using alias routing](https://docs.aws.amazon.com/lambda/latest/dg/configuring-alias-routing.html) — 加重エイリアス、`AdditionalVersionWeights`、制約
- [Deployment configurations (CodeDeploy)](https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html) — `LambdaCanary…` / `LambdaLinear…` / `LambdaAllAtOnce`
- [Gradual deployments / DeploymentPreference (SAM)](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html) — `AutoPublishAlias`、`Type`/`Alarms`/`Hooks`
- [AWS::Serverless::Function DeploymentPreference](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-deploymentpreference.html) — アラームによる自動ロールバック
- [AWS CDK aws-codedeploy LambdaDeploymentGroup](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_codedeploy.LambdaDeploymentGroup.html) — CDKでの安全デプロイ
- [Terraform aws_lambda_alias](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_alias) / [aws_codedeploy_app](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_app) — 加重ルーティング、`compute_platform`
- [Configuring OpenID Connect in Amazon Web Services (GitHub Docs)](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) — `id-token: write`、`AssumeRoleWithWebIdentity`
- [Lambda function states](https://docs.aws.amazon.com/lambda/latest/dg/functions-states.html) — Pending/Active/Inactive/Failed、`LastUpdateStatus`
- [Serverless Framework pricing](https://www.serverless.com/pricing) — v4の有償条件（年商200万ドル）
