目次
17 章

Lambda 基礎

AWS サーバーレスの最初のボタンを整理します。Lambda の役割(vs ECS / EC2)、runtime / handler / event / context モデル、同期 / 非同期 / ストリーム呼び出し、同時実行とコールドスタート、Reserved / Provisioned Concurrency、メモリ · 時間の上限、ロギングと Layers、コストまでを扱います。

第15章 ECS と Fargate第16章 ECR はコンテナが常に立ち上がっているモデルでした。トラフィックが 0 のときもコンテナ1個は生きています。トラフィックの変動が大きかったり、短い処理だけが必要だったり、運用負担をさらに減らしたいときには別の選択肢が合います — Lambda です。

本章は Lambda の動作方式、モデル(runtime / handler / event)、呼び出し方式、コールドスタート、同時実行と上限、ロギングまでを一度に整理します。ここで固めるモデルは 第18章 API Gateway + Lambda の HTTP 露出、第19章 EventBridge / SQS / SNS のイベント処理、そして5部 第31章 Lambda 深掘り の運用パターンへとつながります。

Lambda がすること #

AWS Lambda はサーバーレス関数の実行プラットフォームです。イベントが入ってきたらそこで初めて関数が目覚め、終わると再び消えます。トラフィックが 0 ならコストも 0 です。

Lambda の絵
イベント (HTTP / S3 アップロード / SQS メッセージ / Cron)
Lambda がコンテナを hot または cold で立ち上げる
自分のハンドラ関数を実行 (数 ms ~ 15 分)
レスポンス / 結果 → 呼び出し元
一定時間 idle のあとコンテナ終了

いつ Lambda が合うのか #

Lambda が合う場合 #

  • イベント駆動 — S3 アップロード → サムネイル生成、SQS メッセージ → 処理、EventBridge スケジュール → バッチ
  • 変動が大きいトラフィック — 普段は 0、たまに急増。0 のときはコストがかからない
  • 短い処理 — 通常は数秒 ~ 数分
  • サイド / 補助ワークロード — メインシステムの横の補助関数
  • API の一部だけ — すべての API が Lambda である必要はない

Lambda が合わない場合 #

場合理由
常にトラフィックが回る大きな API同時実行 / コールドスタート / コストで ECS が有利
15分を超える処理Lambda の1呼び出しは最大 15分
非常に大きなメモリ / GPULambda は最大 10 GB メモリ、GPU なし
Stateful な接続 (WebSocket のバックエンドなど)可能だが設計が複雑
常に立ち上がっている DB connection pool呼び出しごとに新しく接続される方式

比較 #

EC2ECS / FargateLambda
立ち上がっている時間常時常時 (Service)呼び出しごと
運用負担中 (Fargate は小)
コールドスタートなしあり (数十 ms ~ 数秒)
時間の上限無限無限15 分
トラフィック 0 のコスト0
同時処理OS レベルコンテナ1個に多重関数インスタンス1個あたり同時1個

最後の行が重要です。Lambda は関数インスタンス1個が同時に1個の呼び出しだけを処理します。同時呼び出しが N 個ならインスタンスも N 個に自動拡張されます。

最初の Lambda — Hello, World #

関数を作る (コンソール) #

コンソール → Lambda → 「関数の作成」→ 直接作成 → Python 3.13 で進めます。

lambda_function.py (コンソールのデフォルト)
def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": "Hello, Lambda"
    }

保存して「テスト」を押すと空のイベントで呼び出されます。CloudWatch Logs に自動でロググループ (/aws/lambda/<関数名>) が作成されます。

CLI で作る #

コードを zip でまとめます。

zip + 作成
zip function.zip lambda_function.py

aws lambda create-function \
  --function-name hello \
  --runtime python3.13 \
  --role arn:aws:iam::123456789012:role/lambda-basic-role \
  --handler lambda_function.lambda_handler \
  --zip-file fileb://function.zip

IaC (Terraform) #

terraform
resource "aws_lambda_function" "hello" {
  function_name = "hello"
  runtime       = "python3.13"
  handler       = "lambda_function.lambda_handler"
  role          = aws_iam_role.lambda.arn
  filename      = "function.zip"
  source_code_hash = filebase64sha256("function.zip")

  memory_size = 256
  timeout     = 10
}

Terraform 自体は 第25章 Terraform 入門 で本格的に扱います。

モデル — Runtime / Handler / Event / Context #

Lambda のモデルを4つのキーワードで整理します。

Runtime #

言語とバージョンです。マネージド runtime は次のとおりです。

  • Python (3.10 ~ 3.13)
  • Node.js (18, 20, 22)
  • Java (8, 11, 17, 21)
  • .NET, Ruby, Go (provided.al2023 の上)
  • Custom Runtime — Rust / Zig / Swift など直接サポート

または コンテナイメージ (ECR) でデプロイします — 第16章 ECR と自然につながります。大きな依存性があるときに有利です (zip の上限 250MB、コンテナは 10GB)。

Handler #

Lambda が呼び出す関数の名前です。<ファイル名>.<関数名> 形式です。

myapp/handler.py
def main(event, context):
    ...

→ Handler 設定: myapp.handler.main

Event #

呼び出し元が送ったデータです。JSON オブジェクトで、呼び出しソースごとに形が異なります。

API Gateway の event
{
  "version": "2.0",
  "routeKey": "GET /hello",
  "headers": {...},
  "queryStringParameters": {...},
  "body": "..."
}
S3 ObjectCreated の event
{
  "Records": [
    {
      "eventSource": "aws:s3",
      "s3": {
        "bucket": {"name": "my-bucket"},
        "object": {"key": "uploads/photo.jpg"}
      }
    }
  ]
}

Lambda Powertools (Python / TypeScript / Java) のようなライブラリが、これらの event を型安全に扱うのに大きく役立ちます — 実戦では積極的に活用します。

Context #

ランタイム情報です。関数の実行時間の上限 (get_remaining_time_in_millis())、request id、関数名などが入っています。

context の役割
def handler(event, context):
    print(context.aws_request_id)
    print(context.function_name)
    print(context.get_remaining_time_in_millis())  # 残り時間 ms

呼び出し方式 — 同期 vs 非同期 vs ストリーム #

呼び出しソースによって Lambda の動作が異なります。

1) 同期 (Synchronous) #

呼び出し元が結果を待ちます。レスポンスを受け取るまでブロックします。

呼び出しソース
API Gateway
ALB
Cognito
直接 Invoke API
aws lambda invoke \
  --function-name hello \
  --payload '{"name": "world"}' \
  --cli-binary-format raw-in-base64-out \
  out.json

2) 非同期 (Asynchronous) #

呼び出し元がキューに入れて終わります。Lambda がバックグラウンドで処理します。失敗時に自動リトライ (デフォルト2回) と DLQ (Dead Letter Queue) への移送をします。

呼び出しソース
S3 ObjectCreated
SNS
EventBridge
InvocationType=Event の Invoke
aws lambda invoke \
  --function-name hello \
  --invocation-type Event \
  --payload '{"key": "value"}' \
  --cli-binary-format raw-in-base64-out \
  out.json

3) ストリーム / ポーリング #

Lambda がキュー / ストリームを自動でポーリングします。

呼び出しソース
SQS
DynamoDB Streams
Kinesis
MSK (Managed Kafka)

設定さえしておけば、新しいメッセージが来るたびに Lambda が batch で受け取って処理します。失敗すると retry と DLQ に行きます。SQS との連携は 第19章 EventBridge / SQS / SNS で詳しく扱います。

同時実行 (Concurrency) の意味 #

Lambda の最も重要な1つです。関数インスタンス1個が同時に1呼び出しだけを処理するので、同時に入ってきた呼び出し数 = 立ち上がったインスタンス数です。

同時呼び出しの流れ
秒あたり 10 個の呼び出し、各呼び出し 1 秒 → 同時 ~10 個のインスタンス
秒あたり 100 個の呼び出し、各呼び出し 100ms → 同時 ~10 個のインスタンス

アカウントの上限 (Account Concurrency) #

リージョンごとにデフォルト 1,000 です。運用ワークロードはしばしば不足します — Service Quotas コンソールで増額を申請します。

Reserved Concurrency #

特定の関数に「最大 N 個」を保証し、かつ制限します。

この関数は最大 100 個
aws lambda put-function-concurrency \
  --function-name hello \
  --reserved-concurrent-executions 100

用途は次のとおりです。

  • 危険な関数の暴走を遮断 (例: 有料の外部 API を呼び出す関数)
  • 他の重要な関数に同時実行の余裕を残す (この関数が 1,000 を全部占めないように)
  • DB connection の暴走防止 (RDS connection pool の保護)

Provisioned Concurrency — コールドスタートの回避 #

あらかじめ N 個のインスタンスを温めておきます。同時呼び出しが N 以下ならコールドスタートが 0 です。

aws lambda put-provisioned-concurrency-config \
  --function-name hello \
  --qualifier prod \
  --provisioned-concurrent-executions 10

コストは温めておいたインスタンスの時間分だけ課金されます (やや安い単価)。API の入り口でコールドスタートがユーザー体験に直接影響するなら検討します。

コールドスタート — 最もよく出会う落とし穴 #

関数インスタンスが新しく作られるときの初期コストです。2段階に分かれます。

コールドスタートの分解
[INIT 段階] — 一度だけ
  ├─ コンテナ環境の準備
  ├─ runtime の起動
  ├─ ハンドラモジュールの import
  └─ ハンドラ関数の外側のコードの実行 (グローバル)
[INVOKE 段階] — 各呼び出しごと
  └─ ハンドラ関数の実行

INIT はインスタンスの最初の呼び出しにだけコストがかかります。同じインスタンスが次の呼び出しを受けると INIT を飛ばします (warm)。

コールドスタート時間 #

おおよその区間です。

言語 / 形態INIT 時間
Python (小さな依存性)~150 ms
Python (大きな依存性、例: boto3 + pandas)~1 ~ 2 秒
Node.js (小さい)~100 ms
Java~500 ms ~ 数秒
コンテナイメージ (大きい)~数秒

コールドスタートを減らす #

  1. メモリを増やす — Lambda はメモリに比例して vCPU も増えます。256MB → 1024MB にするだけでも INIT が速くなります。
  2. 依存性のスリミング — 使わないパッケージを外す、tree-shaking
  3. グローバル変数の活用 — ハンドラの外側で一度作ったオブジェクト (boto3 client、DB connection など) を warm なインスタンスが再利用
  4. Provisioned Concurrency — 上で見たとおり
  5. Lambda SnapStart — INIT 結果のスナップショットを取って速く復元。現在 Java / Python / .NET でサポート

グローバル変数パターン #

良いパターン — グローバルで client を作る
import boto3

# Lambda インスタンスで一度だけ — INIT 段階
s3 = boto3.client("s3")

def handler(event, context):
    # ハンドラはきれいに — 各呼び出しごとに client を作らない
    return s3.list_buckets()
悪いパターン
def handler(event, context):
    # 各呼び出しごとに boto3 client — 遅い
    s3 = boto3.client("s3")
    return s3.list_buckets()

メモリと時間の上限 #

項目上限
メモリ128 MB ~ 10,240 MB (1 MB 単位)
時間1 秒 ~ 900 秒 (15 分)
一時ディスク (/tmp)512 MB ~ 10 GB
環境変数4 KB
Payload (同期)6 MB
Payload (非同期)256 KB
zip (ソース)50 MB (圧縮)
zip (展開後)250 MB
コンテナイメージ10 GB

メモリは vCPU と紐づいています。1,769 MB で vCPU 1個分です。メモリを増やすと CPU も一緒に増えて関数が速くなることがよくあります — メモリを増やすだけでコストが減ることがあります (Lambda Power Tuning で最適値を探します)。

ロギング — CloudWatch Logs #

Lambda のすべての stdout / stderr は自動で CloudWatch Logs/aws/lambda/<関数名> ロググループに入ります。

ロギング
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
    logger.info("Hello %s", event.get("name"))
    return "ok"

運用では構造化ログをおすすめします — JSON で出力すると CloudWatch Logs Insights でフィールドごとのクエリが可能です。

JSON ログ
import json, logging

logger = logging.getLogger()

def handler(event, context):
    logger.info(json.dumps({
        "request_id": context.aws_request_id,
        "user_id": event.get("user_id"),
        "action": "process",
        "duration_ms": 42
    }))

PowertoolsLogger がこれを1行できれいに処理します。

Layers — コードの再利用 #

複数の関数が同じ依存性 / ユーティリティを使うとき、Lambda Layer で分離します。

Layer を作る
# python の依存性
mkdir -p python
pip install requests -t python/
zip -r layer.zip python

aws lambda publish-layer-version \
  --layer-name my-utils \
  --zip-file fileb://layer.zip \
  --compatible-runtimes python3.13
関数に付ける
aws lambda update-function-configuration \
  --function-name hello \
  --layers arn:aws:lambda:ap-northeast-2:123456789012:layer:my-utils:1

長所は関数の zip が小さくなり、依存性を一度だけ更新するという点です。短所は多くなりすぎると追跡が難しくなるという点です (5個の上限)。

Lambda コスト #

Lambda コスト
呼び出しあたりのコスト = (呼び出し数 × $0.0000002)
            + (実行時間 GB-秒 × $0.0000166667)

月 100万呼び出し + 呼び出しあたり 100ms + 256MB の例です。

  • 呼び出しコスト: 100万 × $0.0000002 = $0.20
  • 時間コスト: 100万 × 0.1秒 × 0.25GB × $0.0000166667 = $0.42
  • 合計: ~$0.62 / 月

非常に安いです。無料枠で月 100万呼び出し + 400,000 GB-秒が無料です。小さなワークロードは事実上 0 です。

コストが大きくなる場合は次のとおりです。

  • 呼び出しあたりの時間が長くメモリが大きい関数 (例: 5分 × 3GB)
  • 同時実行が常に高い関数 — ECS / Fargate のほうが安いことがある

よく出会う落とし穴 #

1) コールドスタートで最初の呼び出しが遅い #

API 入り口の Lambda が 0.5 ~ 2秒ずつかかります。ユーザーが耐えられません。上の「コールドスタートを減らす」5つと Provisioned Concurrency を検討します。

2) ハンドラの中で毎回オブジェクト生成 #

悪い例
def handler(event, context):
    db = create_db_connection()
    boto = boto3.client("s3")
    ...

各呼び出しごとに 100ms の無駄です。グローバルに引き出します。

3) RDS connection pool の暴走 #

Lambda 同時 100個 → DB 接続 100個 → DB が connection の上限を超過します。代替は次のとおりです。

  • RDS Proxy — Lambda たちが共有する connection pool (第11章 RDS)
  • Reserved Concurrency で関数の同時実行を制限
  • DynamoDB のようなサーバーレス DB を使う

4) 非同期呼び出しの失敗が静かに消える #

S3 → Lambda が失敗しても呼び出し元は分かりません。DLQ (SQS) または Lambda Destination で失敗をキャプチャします。

非同期の失敗を SQS DLQ に
aws lambda put-function-event-invoke-config \
  --function-name hello \
  --maximum-retry-attempts 2 \
  --destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:...:dlq"}}'

5) 時間超過で途中で切られる #

15分の上限を知らずに大きな処理を1つの関数に入れると、14分59秒で強制終了されます。長い処理は 第21章 Step Functions 入門 で分割するか、ECS / Fargate に移します。

6) Payload が 6MB を超える #

API Gateway → Lambda の同期呼び出しの上限です。大きなファイルは S3 presigned URL パターンで回避します — Lambda が presigned URL だけを発行し、クライアントが直接 S3 にアップロードします。

7) 環境変数に秘密 #

平文の環境変数に DB パスワードを置くとログ / コンソールで露出します。第20章 Secrets Manager / Parameter Store に移します。

練習問題 #

  1. 自分が作ろうとする処理が §「いつ Lambda が合うのか」の合う場合と合わない場合のどちらに属するかを1行で判断し、もし合わないなら 第15章 ECS と Fargate のどのモデルに行くべきかを書いてみましょう。
  2. §「コールドスタートを減らす」の5つの方法のうち、コードを1行も直さずに適用できるものとコード構造を変えなければならないものを分けてみましょう。そして §「グローバル変数パターン」の良い例と悪い例がコールドスタートの INIT / INVOKE 段階のどこで差を作るかを結びつけておきましょう。
  3. §「Lambda コスト」の計算に沿って、月 500万呼び出し + 呼び出しあたり 200ms + 512MB 関数の1ヶ月のコストを自分で計算してみましょう。同じワークロードを 第15章 ECS と Fargate の Fargate で常に立ち上げるなら、どちらが安いかを1行で判断しておきましょう。

一行まとめ: Lambda はイベントが来たときだけ目覚めるサーバーレス関数で、トラフィックが 0 ならコストも 0 で、イベント駆動 · 変動が大きいトラフィック · 短い処理に合う。モデルは runtime · handler · event · context の4つで、呼び出しは同期 · 非同期 · ストリームに分かれ、関数インスタンス1個が同時1呼び出しだけを処理するので呼び出しが増えるとインスタンスが自動拡張される。最もよく出会う落とし穴はコールドスタートで、メモリを増やす · グローバル変数 · Provisioned Concurrency で緩和し、15分の上限 · 6MB payload · 環境変数の秘密のような限界は Step Functions · S3 presigned URL · Secrets Manager で回避する。

次の章 #

関数だけあると呼び出す入り口が足りません。次の 第18章 API Gateway + Lambda では、HTTP リクエストで Lambda を呼ぶ最もよくあるパターンを扱います。REST API と HTTP API の違い、Lambda 統合、ルート / メソッド、権限(IAM / Cognito / Lambda authorizer)、ステージ / デプロイまでサーバーレス API の入り口を整理します。

X