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 環境について

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

フォルダ構成

├── layers/
│   ├── package.json
│   └── pg-layer.zip
│
├── scripts/
│   ├── create-pg-layer.sh
│   ├── init-localstack.sh
│   └── local-secret.json
│
├── .env
├── docker-compose.yml
└── template.yaml

実際のファイル

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

Globals:
  Function:
    Runtime: nodejs20.x
    Timeout: 30
    MemorySize: 128
    Environment:
      Variables:
        DB_NAME: !Sub "${DB_NAME}"
        DB_HOST: !Sub "${DB_HOST}"
        DB_USER: !Sub "${DB_USER}"
        DB_PORT: !Sub "${DB_PORT}"
        SECRET_NAME: !Sub "${SECRET_NAME}"
        IS_LOCAL: !Sub "${IS_LOCAL}"
        NODE_ENV: !Sub "${NODE_ENV}"
        AWS_ENDPOINT_SECRETS_MANAGER: !Sub "${AWS_ENDPOINT_SECRETS_MANAGER}"
        AWS_REGION: !Sub "${AWS_REGION}"
        AWS_ACCESS_KEY_ID: !Sub "${AWS_ACCESS_KEY_ID}"
        AWS_SECRET_ACCESS_KEY: !Sub "${AWS_SECRET_ACCESS_KEY}"
        AWS_SDK_LOAD_CONFIG: !Sub "${AWS_SDK_LOAD_CONFIG}"
        AWS_STS_REGIONAL_ENDPOINTS: !Sub "${AWS_STS_REGIONAL_ENDPOINTS}"
        AWS_NODEJS_CONNECTION_REUSE_ENABLED: !Sub "${AWS_NODEJS_CONNECTION_REUSE_ENABLED}"

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
services:
  postgres:
    image: postgres:13
    container_name: test-postgres
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    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=${DEBUG}
      - DOCKER_HOST=${DOCKER_HOST}
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR}
      - PERSISTENCE=${PERSISTENCE}
      - SERVICES=${SERVICES}
      - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
    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:
#!/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"

echo "LocalStack secrets initialized"
{
  "password": "postgres"
}
#!/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"
# Only used for local debugging

# DB
DB_NAME=test_db
DB_HOST=host.docker.internal
DB_USER=postgres
DB_PORT=5432
DB_PASSWORD=postgres

# SecretsManager
SECRET_NAME=local-secret

# Lambda
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

# LocalStack
DEBUG=1
DOCKER_HOST=unix:///var/run/docker.sock
LAMBDA_EXECUTOR=docker
PERSISTENCE=1
SERVICES=secretsmanager
AWS_DEFAULT_REGION=ap-northeast-1
LOCALSTACK_VOLUME_DIR=./volume

使用方法

実施手順

  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 のシークレットマネージャーに値が追加されたことを確認する
$ docker-compose exec localstack bash
$ aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value --secret-id local-secret