Serverless Framework(主に V.4 / AWS 前提)で、安全・高速・運用しやすいサービスを作るための指針をまとめたものです。
目次
- バージョンとライセンス
- サービス設計とリポジトリ構成
- serverless.yml の書き方
- ステージ・環境分離
- シークレットと設定値の管理
- セキュリティ / IAM
- パッケージングとビルド
- パフォーマンスとコールドスタート
- エラーハンドリングと信頼性
- オブザーバビリティ
- ステートフルリソースの扱い
- ローカル開発とテスト
- CI/CD
- コスト最適化
- アンチパターン集
- チェックリスト
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: truedefaultにフォールバック値を置き、ステージ固有の差分だけ書く。- 参照は
${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: 3600SQS / 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.yml | 500 リソース上限、デプロイ遅延、影響範囲拡大 | ドメインでサービス分割 + Compose |
| DB とアプリを同一スタック | remove やデプロイ失敗でデータ消失リスク | ステートフル層を別スタック + DeletionPolicy: Retain |
Resource: '*' の IAM | 侵害時の影響が全リソースに及ぶ | ARN とアクションを限定、関数ごとのロール |
| シークレットを環境変数に平文 | コンソール・テンプレートから漏洩 | Secrets Manager / SSM を実行時取得 |
| Lambda 同士の同期呼び出しチェーン | 二重課金、タイムアウト連鎖、密結合 | SQS / EventBridge / Step Functions |
| ハンドラ内で毎回クライアント初期化 | レイテンシ増、接続枯渇 | ハンドラ外で初期化 |
| ログ保持無期限 | ストレージコスト増大 | logRetentionInDays を設定 |
| 非同期処理に DLQ なし | 失敗イベントが消える | Destinations / DLQ + アラーム |
本番に deploy function | CloudFormation とのドリフト | 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 している