0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SAM Lambda を GitHub Actions で自動デプロイする — ローカル(LocalStack)から本番AWSへの完全パイプライン

0
Posted at

はじめに

前回の記事では、LocalStack + SAM + Docker Compose でローカル開発環境を作りました。

今回はその続きです。ローカルで動いたコードを「mainブランチにマージしたら自動で本番に届く」状態にします。


この記事でわかること

  • template.local.yml / template.stg.yml / template.prod.yml の3ファイルで環境を分離する設計
  • GitHub Actions で SAM のビルド〜デプロイを自動化する手順
  • STGは自動デプロイ、本番は手動トリガーにする理由と実装
  • sam build --use-container がCIでは不要になる理由
  • スモークテストで「デプロイできたこと」を確認する仕組み
  • 実際にハマった2つの落とし穴(依存関係注入・Authorizer TTLバグ)

完成形のパイプライン

STGは自動、本番は手動。この非対称な設計が重要で、後ほど詳しく説明します。


環境ごとのテンプレート設計

まず、テンプレートを環境ごとに分離します。

backend/
├── template.local.yml   # ローカル開発用(LocalStack + sam local)
├── template.stg.yml     # STG環境用(AWS Lambda + RDS)
└── template.prod.yml    # 本番環境用(AWS Lambda + RDS)

3ファイルの主な違いはここです。

項目 local stg prod
S3接続先 LocalStack (host.docker.internal) AWS S3 AWS S3
DB接続先 postgres-db (Docker) RDS (stg) RDS (prod)
CORS許可オリジン localhost:3000 https://stg.example.com https://www.example.com
スロットリング なし なし あり(DoS対策)
CloudFront なし(presigned URLフォールバック) あり あり

template.stg.yml のGlobalsセクション(本番も構造は同じ):

# template.stg.yml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Parameters:
  DatabasePassword:
    Type: String
    NoEcho: true # CloudFormationコンソールに値を表示しない
    Default: ""
  FrontendUrl:
    Type: String
    Default: https://stg.example.com

Globals:
  Function:
    Runtime: python3.12
    Timeout: 10
    MemorySize: 128
    Architectures:
      - x86_64
    Environment:
      Variables:
        DATABASE_HOST: your-rds-stg.xxxx.ap-northeast-1.rds.amazonaws.com
        DATABASE_NAME: mydb
        USE_LOCAL_S3: "false" # 本番S3を使う
        S3_BUCKET: myapp-uploads-stg
        COOKIE_SAMESITE: "None" # クロスオリジンCookieに必要
        CLOUDFRONT_DOMAIN: !GetAtt MyImageDistribution.DomainName

Resources:
  MyAppApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: stg
      Cors:
        AllowOrigin: !Sub "'${FrontendUrl}'"
        AllowCredentials: true

USE_LOCAL_S3: "false" の一行でLambdaコードの向き先が切り替わります。ローカルでは true にしてLocalStackに接続していたのと同じ環境変数です。


GitHub Actions の設定

GitHub Actions は、GitHubに組み込まれたCI/CDサービスです。リポジトリの .github/workflows/ に YAML ファイルを置くだけで、「pushをトリガーにビルドする」「手動ボタンでデプロイする」といった自動化フローを定義できます。GitHubのプライベートリポジトリでも月2,000分まで無料で利用できます。

ワークフローファイルの基本構造はシンプルです。

on:          # いつ実行するか(トリガー)
  push:
    branches: [develop]

jobs:
  deploy:
    runs-on: ubuntu-latest   # 実行環境
    steps:                   # 実行するステップを順に定義
      - uses: actions/checkout@v4
      - run: echo "hello"

uses: で公式・サードパーティのアクション(再利用可能な処理単位)を呼び出せます。SAM CLIのインストールやAWS認証設定も、専用アクションが用意されているため数行で済みます。

事前準備: シークレットの登録

GitHubリポジトリの Settings → Secrets and variables → Actions に以下を登録します。

シークレット名 内容
AWS_ACCESS_KEY_ID デプロイ用IAMユーザーのアクセスキー
AWS_SECRET_ACCESS_KEY 同シークレットキー
STG_DB_PASSWORD STG環境のDBパスワード
PROD_DB_PASSWORD 本番環境のDBパスワード

IAMユーザーに必要なポリシーは最低限これだけです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:*",
        "lambda:*",
        "apigateway:*",
        "s3:*",
        "iam:*",
        "logs:*"
      ],
      "Resource": "*"
    }
  ]
}

本番運用では Resource にARNを絞るとより安全です。


STGワークフロー — developブランチへのpushで自動実行

.github/workflows/deploy-stg.yml:

name: Deploy Backend to STG

on:
  push:
    branches:
      - develop
    paths:
      - "**.py"
      - "**/requirements.txt"
      - "template.stg.yml"
      - ".github/workflows/deploy-stg.yml"
  workflow_dispatch: # 手動実行も可能にしておく

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Install SAM CLI
        uses: aws-actions/setup-sam@v2
        with:
          use-installer: true

      - name: Set up Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: SAM Build
        run: sam build -t template.stg.yml # --use-container は不要(後述)

      - name: SAM Deploy
        run: |
          sam deploy \
            --stack-name myapp-stg \
            --s3-bucket myapp-lambda-deploy \
            --no-confirm-changeset \
            --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
            --no-fail-on-empty-changeset \
            --force-upload \
            --parameter-overrides \
              DatabasePassword=${{ secrets.STG_DB_PASSWORD }} \
              FrontendUrl=https://stg.example.com

      - name: Smoke test
        run: |
          STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
            -X POST https://your-api-id.execute-api.ap-northeast-1.amazonaws.com/stg/user/login \
            -H "Content-Type: application/json" -d '{}')
          echo "smoke test status: $STATUS"
          if [[ "$STATUS" == 5* ]]; then
            echo "Smoke test FAILED: got HTTP $STATUS"
            exit 1
          fi
          echo "Smoke test passed"

pathフィルタの意図: .py ファイル・requirements.txttemplate.stg.yml のいずれかが変更されたときだけ実行されます。ドキュメントの修正やフロントエンドのコミットでLambdaデプロイが走らないようにする設定です。差分のないデプロイでも --no-fail-on-empty-changeset で正常終了します。


本番ワークフロー — 手動トリガーのみ

.github/workflows/deploy-prod.yml:

name: Deploy Backend to PROD

# workflow_dispatch のみ(自動トリガーなし)
on:
  workflow_dispatch:
    inputs:
      reason:
        description: "手動実行の理由(例: v1.2.0 本番リリース)"
        required: true
        default: "手動デプロイ"

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Install SAM CLI
        uses: aws-actions/setup-sam@v2
        with:
          use-installer: true

      - name: Set up Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: SAM Build
        run: sam build -t template.prod.yml

      - name: SAM Deploy
        run: |
          sam deploy \
            --stack-name myapp-prod \
            --s3-bucket myapp-lambda-deploy \
            --no-confirm-changeset \
            --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
            --no-fail-on-empty-changeset \
            --force-upload \
            --parameter-overrides \
              DatabasePassword=${{ secrets.PROD_DB_PASSWORD }} \
              FrontendUrl=https://www.example.com

      - name: Smoke test
        run: |
          PROD_API_URL=$(aws cloudformation describe-stacks \
            --stack-name myapp-prod \
            --query "Stacks[0].Outputs[?OutputKey=='ApiGatewayUrl'].OutputValue" \
            --output text)
          STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
            -X POST "${PROD_API_URL}/user/login" \
            -H "Content-Type: application/json" -d '{}')
          echo "smoke test status: $STATUS"
          if [[ "$STATUS" == 5* ]]; then
            echo "Smoke test FAILED: got HTTP $STATUS"
            exit 1
          fi
          echo "Smoke test passed"

なぜ本番は手動トリガーにするのか

workflow_dispatch だけにした理由は、developへのpush = 本番リリース準備完了 ではないからです。

個人開発ではブランチ保護が甘くなりがちで、途中の実装が develop に入ることもあります。「STGで1〜2日動作確認した上で、意図を持って本番に押す」という人間の判断ステップを挟むことで、誤デプロイを防ぎます。

GitHub上の操作手順はシンプルです。

Actions → Deploy Backend to PROD → Run workflow
→ 理由を入力して実行

これだけで SAM build → deploy → smoke test が全自動で走ります。


まとめ

構成要素 役割
template.{env}.yml 環境ごとのインフラ差分をIaCで管理
deploy-stg.yml develop push で自動STGデプロイ
deploy-prod.yml workflow_dispatch で手動本番デプロイ
pathフィルタ 無関係なpushでデプロイが走るのを防ぐ
依存注入ステップ SAM build の pip install が不安定な場合の保険
スモークテスト 5xxのみ検知、Lambda死活確認

ローカルでLocalStackを使って開発し、developにpushしたらSTGに自動デプロイされ、確認したら手動で本番に届ける。この3段構えのパイプラインが揃うと、コードを書くことに集中できる環境になります。


この記事の背景

このパイプラインは、グループ旅行のしおり管理・費用精算・旅行レポート共有ができる個人開発サービス 旅BASE(tabibase) の実際のデプロイ構成をベースにしています。

ローカル環境構築の前回記事はこちらです。

AI(Claude Code)をチームメンバーとして開発する全体像については、こちらをご覧ください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?