Serverless Framework(主に V.4 / AWS 前提)で、安全・高速・運用しやすいサービスを作るための指針をまとめたものです。


目次

  1. バージョンとライセンス
  2. サービス設計とリポジトリ構成
  3. serverless.yml の書き方
  4. ステージ・環境分離
  5. シークレットと設定値の管理
  6. セキュリティ / IAM
  7. パッケージングとビルド
  8. パフォーマンスとコールドスタート
  9. エラーハンドリングと信頼性
  10. オブザーバビリティ
  11. ステートフルリソースの扱い
  12. ローカル開発とテスト
  13. CI/CD
  14. コスト最適化
  15. アンチパターン集
  16. チェックリスト

1. バージョンとライセンス

  • V.4 が現行メジャー。V.3 からの移行は大きな破壊的変更が少ないが、公式の「Upgrading to V4」ガイドを必ず確認する。
  • ライセンス: V.4 は年間売上 200 万ドル超の組織では有償ライセンスが必要。個人・小規模組織は無料。CI では SERVERLESS_ACCESS_KEY(または License Key)が必要になる点に注意。
  • バージョン固定: V.4 は 24 時間ごとに自動アップデートする。チーム・CI で挙動を揃えるため frameworkVersion を明示する。
frameworkVersion: '4'
  • 非 AWS プロバイダは非推奨化されている。AWS 以外を使う場合は別ツールを検討する。

2. サービス設計とリポジトリ構成

サービスの粒度

  • 1 サービス = 1 つの境界づけられたコンテキスト(例: users, orders, billing)。巨大な単一 serverless.yml は避ける。
  • CloudFormation の 1 スタック 500 リソース上限 に注意。関数 1 つで Log Group・Role・Permission・API リソースなど複数リソースが生成される。
  • ステートフル(DB, S3, Cognito 等)とステートレス(関数, API)を分ける。アプリのデプロイ失敗やスタック削除でデータが巻き添えにならないようにする。

Serverless Compose で複数サービスを束ねる

# serverless-compose.yml
services:
  infra:
    path: infra          # DynamoDB / S3 / Cognito など
  users:
    path: services/users
    params:
      tableName: ${infra.UsersTableName}
  orders:
    path: services/orders
    dependsOn: users
  • サービス間の値の受け渡しは Compose の出力参照(${infra.Output})や ${cf:stack.Output}、SSM で行い、ハードコードしない。

推奨ディレクトリ構成例

.
├── serverless-compose.yml
├── infra/
│   └── serverless.yml
├── services/
│   ├── users/
│   │   ├── serverless.yml
│   │   ├── src/
│   │   │   ├── handlers/     # Lambda エントリポイント(薄く保つ)
│   │   │   ├── domain/       # ビジネスロジック(AWS 非依存)
│   │   │   └── adapters/     # DynamoDB / 外部 API などの I/O
│   │   └── tests/
│   └── orders/
└── packages/                 # 共有ライブラリ
  • ハンドラは薄く: イベントのパース → ドメインロジック呼び出し → レスポンス整形だけにする。ロジックはテスト可能な純粋関数へ。

3. serverless.yml の書き方

provider レベルで共通設定をまとめる

service: users
frameworkVersion: '4'
 
provider:
  name: aws
  runtime: nodejs22.x
  architecture: arm64           # Graviton: 安くて速いことが多い
  region: ap-northeast-1
  stage: ${opt:stage, 'dev'}
  memorySize: 512
  timeout: 10
  logRetentionInDays: 30
  tracing:
    lambda: true
    apiGateway: true
  tags:
    service: ${self:service}
    stage: ${sls:stage}
  stackTags:
    owner: platform-team
  deploymentBucket:
    blockPublicAccess: true
    serverSideEncryption: AES256
  environment:
    STAGE: ${sls:stage}
    POWERTOOLS_SERVICE_NAME: ${self:service}

ポイント

  • デフォルトは provider に、例外は function に。関数ごとに同じ設定を繰り返さない。
  • memorySize / timeout は必ず明示する(デフォルトの 1024MB / 6 秒に依存しない)。
  • API Gateway のタイムアウト(29 秒)より Lambda の timeout を短くする。
  • 変数は ${sls:stage}, ${param:xxx}, ${ssm:...}, ${env:...} などを使い分ける。${self:custom.xxx} の過度なネストは可読性を落とす。
  • 長くなったら ${file(./config/xxx.yml)} で分割してもよいが、分割しすぎると追いにくくなる。

4. ステージ・環境分離

V.4 の stages ブロックを使う

stages:
  default:
    params:
      logLevel: INFO
      provisionedConcurrency: 0
  dev:
    params:
      logLevel: DEBUG
  prod:
    params:
      provisionedConcurrency: 5
    observability: true
  • default にフォールバック値を置き、ステージ固有の差分だけ書く。
  • 参照は ${param:logLevel}。

アカウント分離

  • ステージごとに AWS アカウントを分ける(最低でも prod と非 prod)。AWS Organizations + SSO(V.4 は AWS SSO / Login をビルトインサポート)を利用。
  • 開発者ごとの個人ステージ(--stage alice)を dev アカウント上に作ると、衝突なく並行開発できる。不要になったら serverless remove で片付ける。

5. シークレットと設定値の管理

種類推奨の置き場所
非機密な設定(ログレベル、フラグ)stages.*.params / 環境変数
機密情報(API キー、DB パスワード)AWS Secrets Manager / SSM Parameter Store (SecureString) / Vault / Doppler
他スタックの出力Compose 出力参照 / ${cf:...} / SSM
  • serverless.yml やリポジトリに平文のシークレットを書かない。
  • ${ssm:/path/secret} でデプロイ時解決すると値が Lambda の環境変数に平文で入り、コンソールや CloudFormation テンプレートで見えてしまう。機密性が高いものは実行時に SDK / Powertools Parameters で取得してキャッシュする方が安全。
  • V.4 では Vault / Terraform state / Doppler などを Variable Resolver 経由で参照できる。既存のシークレット基盤があるなら活用する。
functions:
  api:
    handler: src/handlers/api.handler
    environment:
      DB_SECRET_ARN: ${param:dbSecretArn}   # ARN だけ渡し、値は実行時に取得

6. セキュリティ / IAM

  • 最小権限: Resource: '*' や Action: 'dynamodb:*' を避け、対象 ARN とアクションを絞る。
  • 関数ごとに IAM ロールを分けるのが理想。provider 共通ロールに全権限を積むと、1 つの関数の脆弱性が全リソースに波及する。
    • 方法: 関数ごとの role: 指定、または serverless-iam-roles-per-function プラグイン。
provider:
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:Query
          Resource:
            - !GetAtt UsersTable.Arn
            - !Join ['/', [!GetAtt UsersTable.Arn, 'index/*']]
  • デプロイ用 IAM も最小化する。CI には長期アクセスキーではなく OIDC(GitHub Actions など)による一時クレデンシャルを使う。
  • CloudFormation サービスロール(provider.iam.deploymentRole)を使い、デプロイ実行者自身の権限を小さくする。
  • API には認可を必ず付ける(Cognito / JWT Authorizer / Lambda Authorizer / IAM)。cors: true の安易な全許可は避け、オリジンを限定する。
  • 公開 API には AWS WAF とスロットリング設定を検討する。
  • 依存パッケージの脆弱性スキャン(npm audit, Dependabot, Snyk 等)を CI に組み込む。

7. パッケージングとビルド

V.4 のネイティブビルド(esbuild)

V.4 は JS/TS を esbuild でネイティブにバンドルするため、serverless-esbuild / serverless-webpack は基本的に不要。

build:
  esbuild:
    bundle: true
    minify: true
    sourcemap:
      type: linked
      setNodeOptions: true
    external:
      - '@aws-sdk/*'     # Node.js ランタイム同梱の AWS SDK v3 を利用

既存の esbuild/webpack プラグインを残すとネイティブビルドと競合することがある。移行時はどちらかに統一する。

パッケージの最適化

  • 関数ごとに個別パッケージ化(package.individually: true)して、各関数のデプロイサイズを最小化する。
  • 不要ファイル(テスト、ドキュメント、.map の扱いなど)は package.patterns で除外。
  • Python は V.4 でビルトイン化された Python requirements 機能を使い、ネイティブ依存は Docker ビルドや Lambda 互換環境でビルドする。
  • 重い共通依存(例: 画像処理ライブラリ)は Lambda Layer やコンテナイメージを検討。ただし Layer の乱用はバージョン管理を複雑にする。
  • パッケージサイズはコールドスタート時間に直結する。定期的にサイズを確認する。

8. パフォーマンスとコールドスタート

  • 初期化処理はハンドラの外(SDK クライアント、DB 接続、設定読み込み)に置き、実行環境の再利用を活かす。
// ハンドラ外で初期化(再利用される)
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
 
export const handler = async (event) => {
  // ...
};
  • メモリサイズの調整: Lambda は CPU がメモリに比例する。AWS Lambda Power Tuning で最適値を計測する。
  • arm64(Graviton) をデフォルトに。ネイティブ依存がある場合は互換性を確認。
  • レイテンシ要件が厳しい関数のみ Provisioned Concurrency または SnapStart(Java / Python / .NET)を使う。全関数に付けるとコスト増。
  • AWS SDK v3 はモジュール単位で import し、バンドルを小さく保つ。
  • VPC 内 Lambda は必要な場合のみ。RDS 接続には RDS Proxy を使い接続枯渇を防ぐ。
  • 長時間・高スループットのワークロードには Managed Instances(EC2 バックの Lambda 実行)やコンテナ(Serverless Container Framework)も選択肢になる。

9. エラーハンドリングと信頼性

非同期呼び出し

  • 失敗時の送り先(On-failure Destination / DLQ)を必ず設定し、イベントを失わないようにする。
functions:
  processor:
    handler: src/handlers/processor.handler
    destinations:
      onFailure: arn:aws:sqs:${aws:region}:${aws:accountId}:processor-dlq
    maximumRetryAttempts: 2
    maximumEventAge: 3600

SQS / Kinesis / DynamoDB Streams

  • 部分バッチ失敗を有効化し、1 件の失敗でバッチ全体が再処理されるのを防ぐ。
    events:
      - sqs:
          arn: !GetAtt JobsQueue.Arn
          batchSize: 10
          functionResponseType: ReportBatchItemFailures
  • SQS の可視性タイムアウトは Lambda timeout の 6 倍程度を目安にする。
  • ソースキュー側にも DLQ(redrive policy)を設定する。

冪等性

  • Lambda は少なくとも 1 回実行される前提。決済・メール送信など副作用のある処理は冪等性キーで重複実行を防ぐ(Powertools の Idempotency ユーティリティが便利)。

その他

  • 長いワークフローや複数ステップの処理は Lambda 内でオーケストレーションせず、Step Functions や Durable Functions を使う。
  • 外部 API 呼び出しにはタイムアウト・リトライ(指数バックオフ + ジッター)を設定する。
  • reservedConcurrency で下流(DB 等)を守る、または暴走時の被害を限定する。

10. オブザーバビリティ

  • 構造化ログ(JSON) を出力し、requestId / 相関 ID を含める。AWS Lambda Powertools(Logger / Tracer / Metrics)の利用を推奨。
  • ログ保持期間を必ず設定(logRetentionInDays)。デフォルトは無期限で、コストが膨らむ。
  • X-Ray トレーシング(または OpenTelemetry)で分散トレースを取る。
  • CloudWatch アラームを最低限設定:
    • 関数の Errors, Throttles, Duration(p95/p99)
    • DLQ の ApproximateNumberOfMessagesVisible > 0
    • API Gateway の 5XXError, Latency
    • SQS の ApproximateAgeOfOldestMessage
  • ログレベルはステージパラメータで切り替える(dev: DEBUG / prod: INFO)。
  • Serverless Dashboard の Observability や、Datadog などの外部ツールも選択肢。

11. ステートフルリソースの扱い

  • DynamoDB / S3 / RDS / Cognito などは別スタック(infra サービス)に分離する。
  • 誤削除防止:
resources:
  Resources:
    UsersTable:
      Type: AWS::DynamoDB::Table
      DeletionPolicy: Retain
      UpdateReplacePolicy: Retain
      Properties:
        BillingMode: PAY_PER_REQUEST
        PointInTimeRecoverySpecification:
          PointInTimeRecoveryEnabled: true
        SSESpecification:
          SSEEnabled: true
        # ...
  • テーブル名などの物理名を固定すると置換更新ができなくなるので、変更が必要なプロパティ(キースキーマ等)の扱いに注意。
  • prod スタックには CloudFormation のスタックポリシーや TerminationProtection を検討する。
  • S3 バケットは Block Public Access・暗号化・バージョニングを有効に。

12. ローカル開発とテスト

  • Dev Mode(serverless dev): 実際の AWS 上のイベントをローカルコードにルーティングでき、デプロイせずに高速に反復できる。
  • serverless invoke local は簡易確認向け。IAM や実イベントとの差異があるため過信しない。
  • テスト戦略:
    • ユニットテスト: ドメインロジック(AWS 非依存部分)を中心に。
    • 統合テスト: 個人ステージや一時ステージに実際にデプロイして、本物の AWS サービスに対してテストする(モックより信頼性が高い)。
    • E2E テスト: API エンドポイントに対して主要なユーザーフローを検証。
  • サンプルイベントは tests/events/*.json に置き、serverless invoke --path やユニットテストで再利用する。

13. CI/CD

基本方針

  • 本番デプロイは CI からのみ。開発者 PC からの prod デプロイは禁止する。
  • 同じ成果物を各ステージに昇格させたい場合は serverless package → serverless deploy --package を使う。
  • serverless deploy function -f xxx は CloudFormation を経由しない高速デプロイ。dev での反復専用とし、本番では使わない(ドリフトの原因)。

パイプライン例

PR 作成
 ├─ lint / 型チェック / ユニットテスト
 ├─ serverless package --stage pr-123(構文・ビルド検証)
 └─ (任意)一時ステージにデプロイ → 統合テスト → remove

main へマージ
 ├─ deploy --stage staging
 ├─ 統合 / E2E テスト
 └─ 承認 → deploy --stage prod

GitHub Actions(OIDC)例

permissions:
  id-token: write
  contents: read
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-deploy
          aws-region: ap-northeast-1
      - run: npx serverless deploy --stage prod
        env:
          SERVERLESS_ACCESS_KEY: ${{ secrets.SERVERLESS_ACCESS_KEY }}

その他

  • serverless は devDependencies に入れて npx で実行し、グローバルインストールのバージョン差異を避ける。
  • 古い Lambda バージョンの蓄積(コードストレージ 75GB 上限)を防ぐため、V.4 のビルトイン Prune 機能(旧 serverless-prune-plugin)で古いバージョンを削除する。
  • 段階的リリースが必要なら Lambda エイリアス + CodeDeploy によるカナリア / リニアデプロイを検討。
  • デプロイ前に serverless print で解決後の設定を確認すると、変数の解決ミスを防げる。

14. コスト最適化

  • arm64 に切り替える(同性能で単価が安い)。
  • メモリを計測して最適化(大きすぎても小さすぎても割高になりうる)。
  • ログ保持期間の設定とログレベルの適正化(本番での DEBUG ログ垂れ流しを避ける)。
  • HTTP API(API Gateway v2) は REST API より安価。REST 固有機能(API キー、使用量プラン、WAF 直結等)が不要なら HTTP API を選ぶ。
  • Provisioned Concurrency は必要な関数・時間帯だけ(Application Auto Scaling でスケジュール設定)。
  • 不要になった個人・PR ステージを定期的に削除する。
  • tags / stackTags でコスト配分タグを付け、サービス・ステージ単位でコストを可視化する。

15. アンチパターン集

アンチパターン問題代替案
1 つの巨大 serverless.yml500 リソース上限、デプロイ遅延、影響範囲拡大ドメインでサービス分割 + Compose
DB とアプリを同一スタックremove やデプロイ失敗でデータ消失リスクステートフル層を別スタック + DeletionPolicy: Retain
Resource: '*' の IAM侵害時の影響が全リソースに及ぶARN とアクションを限定、関数ごとのロール
シークレットを環境変数に平文コンソール・テンプレートから漏洩Secrets Manager / SSM を実行時取得
Lambda 同士の同期呼び出しチェーン二重課金、タイムアウト連鎖、密結合SQS / EventBridge / Step Functions
ハンドラ内で毎回クライアント初期化レイテンシ増、接続枯渇ハンドラ外で初期化
ログ保持無期限ストレージコスト増大logRetentionInDays を設定
非同期処理に DLQ なし失敗イベントが消えるDestinations / DLQ + アラーム
本番に deploy functionCloudFormation とのドリフトCI からフルデプロイ
プラグインの無制限な追加保守停止・V.4 非互換のリスクビルトイン機能を優先、プラグインは厳選

16. チェックリスト

設定

  • frameworkVersion を固定している
  • memorySize / timeout / runtime / architecture を明示している
  • logRetentionInDays を設定している
  • タグ(service / stage / owner)を付与している
  • ステージ差分は stages / params で管理している

セキュリティ

  • IAM は最小権限(ワイルドカードなし)
  • シークレットはリポジトリ・平文環境変数に置いていない
  • API に認可を設定し、CORS を限定している
  • CI は OIDC の一時クレデンシャルでデプロイしている
  • デプロイバケットは暗号化・パブリックアクセスブロック済み

信頼性

  • 非同期 / ストリーム処理に DLQ・失敗時送り先がある
  • SQS で ReportBatchItemFailures を有効化している
  • 副作用のある処理は冪等になっている
  • ステートフルリソースは別スタック + Retain

運用

  • 構造化ログ・トレーシングを有効化している
  • エラー / スロットリング / DLQ にアラームがある
  • 本番デプロイは CI 経由のみ
  • 古い Lambda バージョンを Prune している

参考リンク

0件のページ