対象: Lambda で API・イベント処理・バッチ・ワークフローを構築する開発者 / アーキテクト
目次
- 関数コードの設計
- パフォーマンスとコールドスタート
- メモリ・アーキテクチャ・コスト最適化
- 同時実行数とスケーリング
- イベントソースとエラーハンドリング
- セキュリティ
- ネットワーク (VPC)
- 監視・オブザーバビリティ
- デプロイ・運用
- 長時間処理・ワークフロー
- Lambda を選ぶべきでないケース
- チェックリスト
1. 関数コードの設計
ハンドラとビジネスロジックを分離する
ハンドラはイベントの解析とレスポンス整形だけに留め、ロジックは別モジュールに切り出す。単体テストがしやすくなり、Lambda 以外への移植も容易になる。
# handler.py
from app.service import create_order
def lambda_handler(event, context):
body = parse(event)
result = create_order(body)
return {"statusCode": 201, "body": to_json(result)}初期化処理はハンドラの外で行う
SDK クライアント、DB 接続、設定値の読み込みなどはグローバルスコープで 1 回だけ実行し、実行環境の再利用(ウォームスタート)時に使い回す。
import boto3
dynamodb = boto3.resource("dynamodb") # 実行環境ごとに1回
table = dynamodb.Table(os.environ["TABLE_NAME"])
def lambda_handler(event, context):
table.put_item(Item=...) # 毎回の呼び出しで再利用状態を実行環境に持たせない
/tmpやグローバル変数はキャッシュ用途に限定し、消えても問題ない前提で使う。- 永続化が必要なデータは DynamoDB / S3 / ElastiCache などに保存する。
- ユーザー固有データをグローバル変数に残さない(別リクエストへの漏えい防止)。
冪等性を確保する
Lambda は「少なくとも 1 回」実行されるイベントソースが多い(SQS、EventBridge、非同期呼び出しなど)。同じイベントが複数回届いても結果が変わらないよう設計する。
- リクエスト ID / イベント ID をキーに DynamoDB の条件付き書き込みで重複排除
- Powertools for AWS Lambda の Idempotency ユーティリティを活用
1 関数 1 責務
巨大な「モノリス関数」は権限が広がり、コールドスタートも遅くなる。目的ごとに関数を分け、最小権限の IAM ロールを付与する。ただし細かく分けすぎると管理コストが増えるので、ドメイン単位でまとめるのが現実的。
再帰呼び出しを避ける
関数が自分自身をトリガーするイベント(同じ S3 バケットへの書き込みなど)を作らない。Lambda には再帰ループ検出機能があるが、設計段階で入力と出力のリソースを分ける。
2. パフォーマンスとコールドスタート
デプロイパッケージを小さくする
- 不要な依存関係を含めない(
aws-sdk全体ではなく必要なクライアントのみ、など) - Node.js は esbuild などでバンドル・ツリーシェイク
- Python はテストコードや不要ファイルを除外
- 共通ライブラリは Lambda レイヤーで共有(ただしレイヤー自体もサイズに含まれる)
初期化を遅延・最小化する
- 起動時に重いフレームワーク(DI コンテナ、ORM の全モデル読み込み等)を避ける
- 使わない可能性があるクライアントは遅延初期化する
SnapStart を検討する
Java・Python・.NET で利用可能。初期化済み実行環境のスナップショットから起動するため、初期化が重い関数のコールドスタートを大幅に短縮できる。
- 注意: スナップショット後に一意であるべき値(乱数シード、一時認証情報、接続など)はランタイムフックで再生成する
プロビジョニングされた同時実行 (Provisioned Concurrency)
レイテンシ要件が厳しい API など、コールドスタートを許容できない場合に使用。Application Auto Scaling でスケジュール・使用率ベースに調整し、コストを抑える。
レスポンスストリーミング
LLM の出力や大きなレスポンスは InvokeWithResponseStream / Function URL のストリーミングで逐次返すと、Time to First Byte を短縮できる。
3. メモリ・アーキテクチャ・コスト最適化
メモリは計測して決める
Lambda はメモリ量に比例して CPU も割り当てられる。メモリを増やすと実行時間が短くなり、結果的に安くなることも多い。
- AWS Lambda Power Tuning(Step Functions ベースの OSS)で最適値を計測
- AWS Compute Optimizer の推奨も参考にする
arm64 (Graviton) を優先する
多くのワークロードで x86_64 より価格性能比が良い。ネイティブバイナリ依存がなければまず arm64 を選ぶ。
タイムアウトを適切に設定する
デフォルト(3 秒)のまま、または上限(15 分)に雑に設定しない。想定実行時間 + 余裕で設定し、下流の API タイムアウトより Lambda タイムアウトを長く取る。
待ち時間に課金されない設計にする
- 関数内で
sleepやポーリングで待たない - 待機が必要なら Step Functions の Wait / コールバック、または Lambda durable functions を使う
Lambda Managed Instances の検討
常時一定のトラフィックがある、特殊なハードウェアが必要、EC2 の Savings Plans / RI を活かしたい場合は、EC2 上で Lambda を実行する Lambda Managed Instances(LMI)が選択肢になる。1 実行環境で並列リクエストを処理できるため、コードがスレッドセーフであることを確認する。
ログのコストに注意
CloudWatch Logs のコストは見落としがち。
- ログレベルを環境変数で制御(本番は INFO 以上)
- ロググループの保持期間を必ず設定
- 高頻度ログは Infrequent Access ログクラスも検討
4. 同時実行数とスケーリング
アカウントの同時実行上限を把握する
リージョン単位でアカウント全体の同時実行数が共有される。1 つの関数の暴走が他の関数を巻き込まないよう設計する。
予約同時実行 (Reserved Concurrency) を使う
- 重要な関数の容量を確保する
- 下流(RDS、外部 API など)を保護するための上限として使う
0に設定すれば緊急停止スイッチになる
下流リソースを保護する
- RDS へは RDS Proxy 経由で接続し、コネクション枯渇を防ぐ
- SQS イベントソースでは 最大同時実行数 (Maximum concurrency) を設定する
- 外部 API にはレート制限・リトライ(指数バックオフ + ジッター)を実装
5. イベントソースとエラーハンドリング
呼び出しタイプごとに戦略を変える
| 呼び出しタイプ | 例 | リトライ | 失敗時の受け皿 |
|---|---|---|---|
| 同期 | API Gateway, ALB, Function URL | 呼び出し元の責任 | クライアントにエラーを返す |
| 非同期 | S3, SNS, EventBridge | Lambda が最大 2 回 | 失敗時の送信先 (on-failure destination) / DLQ |
| ポーリング (ESM) | SQS, Kinesis, DynamoDB Streams, Kafka | 設定に依存 | DLQ / on-failure destination |
SQS
- 可視性タイムアウトを関数タイムアウトの 6 倍以上 に設定
- 部分バッチレスポンス (
ReportBatchItemFailures) を有効にし、失敗したメッセージだけを再処理 maxReceiveCount付きの DLQ(リドライブポリシー)を設定
Kinesis / DynamoDB Streams
- 1 件の不正レコードでシャード全体が止まる「ポイズンピル」に注意
BisectBatchOnFunctionError、MaximumRetryAttempts、MaximumRecordAgeInSecondsを設定- 部分バッチレスポンスを使用
非同期呼び出し
- DLQ よりも詳細な情報を持つ Destinations(成功/失敗の送信先)を優先
MaximumEventAgeInSecondsとMaximumRetryAttemptsを明示的に設定
イベントフィルタリング
ESM のフィルター条件で不要なイベントを除外すると、無駄な呼び出しとコストを削減できる。
6. セキュリティ
最小権限の IAM
- 関数ごとに専用の実行ロールを作る
Resource: "*"やdynamodb:*のようなワイルドカードを避ける- IAM Access Analyzer でポリシーを検証・未使用権限を検出
シークレットは環境変数に平文で置かない
- AWS Secrets Manager / SSM Parameter Store を使用
- Parameters and Secrets Lambda Extension でキャッシュし、API 呼び出し回数を削減
- 環境変数を使う場合も KMS カスタマーマネージドキーで暗号化
リソースベースポリシーを絞る
- 呼び出し元を
SourceArn/SourceAccountで限定(混乱した代理問題の防止) - Function URL を
AuthType: NONEで公開する場合は、関数内での認証を必ず実装
コードの完全性
- コード署名 (Code Signing) で信頼できるパッケージのみデプロイ
- 依存ライブラリの脆弱性スキャン(Amazon Inspector の Lambda スキャン、Dependabot など)
- ランタイムのサポート終了 (deprecation) スケジュールを追跡し、早めにアップグレード
入力値の検証
イベントは信頼できない入力として扱い、スキーマ検証(Powertools の Validation / Parser、Pydantic、Zod など)を行う。
マルチテナント
テナント間の分離が必要な場合は、テナント分離モードやテナントごとの関数・ロールの分離を検討する。
7. ネットワーク (VPC)
VPC は必要な場合だけ
RDS / ElastiCache などプライベートリソースにアクセスする場合のみ VPC に接続する。DynamoDB や S3 しか使わない関数を VPC に入れる必要はない。
VPC 接続時の注意
- インターネットへの通信には NAT Gateway が必要(パブリックサブネットに置いてもパブリック IP は付与されない)
- AWS サービスへは VPC エンドポイント(Gateway / Interface)を使うと NAT のコストとレイテンシを削減できる
- 複数 AZ のサブネットを指定して可用性を確保
- サブネットの IP アドレスに十分な余裕を持たせる
8. 監視・オブザーバビリティ
必ず監視するメトリクス
| メトリクス | 見るべき点 |
|---|---|
Errors / エラー率 | 急増の検知 |
Throttles | 同時実行上限に当たっていないか |
Duration (p99) | タイムアウトに近づいていないか |
ConcurrentExecutions | 上限に対する余裕 |
IteratorAge | ストリーム処理の遅延 |
AsyncEventAge / DeadLetterErrors | 非同期処理の滞留・失敗 |
構造化ログ
- JSON 形式でログを出力(Lambda の高度なログ制御で JSON 出力 / ログレベル設定が可能)
- リクエスト ID・相関 ID を全ログに含める
- 個人情報・シークレットをログに出さない
分散トレーシング
AWS X-Ray または OpenTelemetry (ADOT) でサービス間の処理を追跡する。
Powertools for AWS Lambda を使う
Python / TypeScript / Java / .NET 向けに Logger・Tracer・Metrics・Idempotency・Batch 処理などのベストプラクティスがまとめて実装されている。
アラーム
CloudWatch アラームをエラー率、スロットル、DLQ のメッセージ数、IteratorAge に設定し、通知先を決めておく。
9. デプロイ・運用
Infrastructure as Code
AWS SAM、AWS CDK、Terraform、Serverless Framework などで関数とその周辺リソースをコード管理する。コンソールでの手動変更は避ける。
バージョンとエイリアス
- デプロイごとにバージョンを発行し、
prodなどのエイリアス経由で呼び出す $LATESTを本番トラフィックに直接向けない
段階的デプロイ
CodeDeploy(SAM の DeploymentPreference)で Canary / Linear デプロイを行い、CloudWatch アラームと連動した自動ロールバックを設定する。
環境の分離
dev / staging / prod は可能であれば AWS アカウント単位で分離する。
テスト
- ロジックは単体テストでローカル実行
- AWS サービスとの結合部分は実際のクラウド環境でテスト(エミュレータへの過信は禁物)
sam local invoke/sam syncなどで開発ループを短縮
ランタイムの更新
ランタイム管理設定(自動更新 / 関数更新時 / 手動)を理解し、サポート終了前に計画的にアップグレードする。
10. 長時間処理・ワークフロー
Lambda の最大実行時間は 15 分。それを超える、または複数ステップを確実に連携させたい場合は以下を検討する。
| 手段 | 向いているケース |
|---|---|
| Step Functions | 可視化されたワークフロー、多数の AWS サービス連携、人手による承認 |
| Lambda durable functions | コード内で複数ステップをチェックポイント付きで記述、待機中は課金なし、最長 1 年 |
| SQS + Lambda で分割 | 大量データをチャンクに分けて並列処理 |
| ECS / Fargate / AWS Batch | 15 分を超える単一の重い処理 |
関数から別の関数を同期呼び出しで連鎖させる(Lambda → Lambda)構成は、待機時間の二重課金やエラー処理の複雑化を招くため避ける。
11. Lambda を選ぶべきでないケース
- 15 分を超える単一処理が必要
- 常時高負荷で安定したトラフィック(コンテナや LMI の方が安い場合がある)
- 長時間の WebSocket / TCP 接続を自分で保持する必要がある
- GPU が必要な処理
- 極めて低いレイテンシ(1 桁ミリ秒)が常に要求され、コールドスタートを一切許容できない
12. チェックリスト
コード
- 初期化処理をハンドラの外に置いている
- 冪等性を確保している
- 入力を検証している
- 実行環境に状態を依存させていない
パフォーマンス / コスト
- Power Tuning などでメモリを計測して決めた
- arm64 を検討した
- タイムアウトを適切に設定した
- ログの保持期間を設定した
信頼性
- DLQ / on-failure destination を設定した
- SQS で部分バッチレスポンスを有効にした
- 可視性タイムアウト ≥ 関数タイムアウト × 6
- 下流保護のため同時実行数を制御している
セキュリティ
- 関数ごとに最小権限ロールを付与した
- シークレットを Secrets Manager / Parameter Store で管理している
- サポートされたランタイムを使っている
- 依存ライブラリをスキャンしている
運用
- IaC で管理している
- エイリアス + 段階的デプロイ + 自動ロールバック
- エラー・スロットル・遅延のアラームを設定した
- 構造化ログとトレーシングを導入した