対象: Lambda で API・イベント処理・バッチ・ワークフローを構築する開発者 / アーキテクト


目次

  1. 関数コードの設計
  2. パフォーマンスとコールドスタート
  3. メモリ・アーキテクチャ・コスト最適化
  4. 同時実行数とスケーリング
  5. イベントソースとエラーハンドリング
  6. セキュリティ
  7. ネットワーク (VPC)
  8. 監視・オブザーバビリティ
  9. デプロイ・運用
  10. 長時間処理・ワークフロー
  11. Lambda を選ぶべきでないケース
  12. チェックリスト

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, EventBridgeLambda が最大 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 Batch15 分を超える単一の重い処理

関数から別の関数を同期呼び出しで連鎖させる(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 で管理している
  • エイリアス + 段階的デプロイ + 自動ロールバック
  • エラー・スロットル・遅延のアラームを設定した
  • 構造化ログとトレーシングを導入した

参考資料

0件のページ