Serverless 構成で lambda ローカルデバッグ用に AWS SAM CLI を使用する方法を調査したものを備忘録として残しておく

環境

  • Infrastructure:
    • AWS
      • lambda: nodejs(20.X)
      • Secret Manager
      • RDS: PostgreSQL
      • AWS API Gateway
        • API Key あり
  • AWS SAM CLI
  • Docker Compose
  • API 仕様書
    • OpenAPI 3.0

AWS SAM 環境について

必要なファイルの内、重要な部分のみ記載

[template.yaml]

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: nodejs20.x
    Timeout: 5
    MemorySize: 256
    Environment:
      Variables:
        DB_NAME: test_db
        DB_HOST: host.docker.internal
        DB_USER: postgres
        DB_PORT: 5432
        SECRET_NAME: local-secret
        IS_LOCAL: true
        NODE_ENV: local
        AWS_ENDPOINT_SECRETS_MANAGER: http://host.docker.internal:4566
        AWS_REGION: ap-northeast-1
        AWS_ACCESS_KEY_ID: test
        AWS_SECRET_ACCESS_KEY: test
        AWS_SDK_LOAD_CONFIG: 1
        AWS_STS_REGIONAL_ENDPOINTS: regional
        AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1

Resources:
  # API Gateway
  LocalApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: local
      Cors:
        AllowMethods: "'*'"
        AllowHeaders: "'*'"
        AllowOrigin: "'*'"
      Auth:
        ApiKeyRequired: true
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: ./docs/open_api.yml # Parameterized OpenAPI 3.0 file

  # Lambda Function
  GetUserDetailFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: functions/getUserDetail/
      Handler: index.handler
      Layers:
        - !Ref PgLayer
      Events:
        ApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref LocalApi
            Path: /users/{userId}
            Method: GET

  PgLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: pg-layer
      Description: PostgreSQL client for Node.js
      ContentUri: layers/pg-layer.zip
      CompatibleRuntimes:
        - nodejs20.x

[docker-compose.yml]

services:
  postgres:
    image: postgres:13
    container_name: test-postgres
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  localstack:
    image: localstack/localstack:latest
    ports:
      - "4566:4566"            # LocalStack Gateway
      - "4510-4559:4510-4559"  # External services port range
    environment:
      - DEBUG=1
      - DOCKER_HOST=unix:///var/run/docker.sock
      - LAMBDA_EXECUTOR=docker
      - PERSISTENCE=1
      - SERVICES=secretsmanager
      - DEFAULT_REGION=ap-northeast-1
      - AWS_DEFAULT_REGION=ap-northeast-1
      - AWS_ACCESS_KEY_ID=test
      - AWS_SECRET_ACCESS_KEY=test
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./scripts/init-localstack.sh:/etc/localstack/init/ready.d/init-localstack.sh"
      - "./scripts/local-secret.json:/etc/localstack/local-secret.json"

volumes:
  postgres_data:

[init-localstack.sh]

#!/bin/bash

# LocalStackのエンドポイント
ENDPOINT="http://localhost:4566"

# シークレット値を外部ファイルから読み込む
SECRET_STRING=$(cat /etc/localstack/local-secret.json)

# シークレットの作成
aws --endpoint-url=$ENDPOINT secretsmanager create-secret \
    --name local-secret \
    --description "Local development secret" \
    --secret-string "$SECRET_STRING" \
    --region ap-northeast-1

echo "LocalStack secrets initialized"

[local-secret.json]

{
  "password": "postgres"
}

[create-pg-layer.sh]

#!/bin/bash

set -e

LAYER_DIR="layers/pg-layer"
ZIP_FILE="layers/pg-layer.zip"

mkdir -p "$LAYER_DIR/nodejs"

cp layers/package.json "$LAYER_DIR/nodejs/package.json"

cd "$LAYER_DIR/nodejs"
npm install --production
cd ../../..

rm -f "$ZIP_FILE"

cd "$LAYER_DIR"
zip -r "../../$ZIP_FILE" .
cd ../../

echo "pg-layer の作成が完了しました: $ZIP_FILE"
rm -rf "$LAYER_DIR"

使用方法

実施手順

  1. $ docker-compose up -d
  2. Copy the DB in the Dev environment
  3. If the Layer is updated, do the following $ ./scripts/create-pg-layer.sh
  4. $ sam local start-api

シークレットマネージャーに値が追加された場合

ローカルデバッグでは localstack を使用する

  1. scripts/local-secret.json を編集して、値を追加する
  2. docker-compose down & docker-compose up -d
  3. localstack の初期化スクリプトが実行され、シークレットマネージャーに値が追加される
  4. localstack のシークレットマネージャーに値が追加されたことを確認する
$ AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test aws --endpoint-url=http://localhost:4566 --region ap-northeast-1 secretsmanager get-secret-value --secret-id local-secret