実践LESSON 19
この教材で学ぶこと Lambdaを中心に、非同期処理と障害対策を設計します。AWS サーバーレス — Lambda・API Gateway・Step Functions
対象読者: Lambdaの基本操作から、設計とトラブル対処へ理解を進めたい人。
1. Lambda の内部動作(コールドスタートを理解する)
リクエスト 1(コールドスタート):
[AWS インフラ確保] → [コンテナ起動] → [ランタイム初期化] → [ハンドラ実行]
↑ここが遅い(数百ms〜数秒)
リクエスト 2(ウォームスタート):
[既存コンテナ再利用] → [ハンドラ実行]
↑速い(ミリ秒単位)
対策:
1. Provisioned Concurrency(常に起動済みコンテナを確保)
→ コスト増。本番の重要エンドポイントに使う
2. ハンドラ外で初期化(DB接続など)
3. Lambda SnapStart(Java 向け。スナップショット起動)
2. Lambda ハンドラの書き方
import json
import boto3
import os
from typing import Any
# ✅ ハンドラ外で初期化(ウォームスタート時に再利用される)
s3 = boto3.client("s3")
TABLE_NAME = os.environ["TABLE_NAME"]
def handler(event: dict, context: Any) -> dict:
"""
event の構造はトリガーによって変わる:
- API Gateway: {"body": "...", "headers": {...}, "pathParameters": {...}}
- SQS: {"Records": [{"body": "...", "receiptHandle": "..."}]}
- EventBridge: {"source": "myapp", "detail-type": "OrderPlaced", "detail": {...}}
"""
# ログ(CloudWatch Logs に出力)
print(json.dumps({"event": event, "requestId": context.aws_request_id}))
try:
# メイン処理
result = process(event)
return {
"statusCode": 200,
"body": json.dumps(result),
"headers": {"Content-Type": "application/json"}
}
except ValueError as e:
return {"statusCode": 400, "body": json.dumps({"error": str(e)})}
except Exception as e:
# 予期しないエラーは再スロー → Lambda がリトライ
raise
def process(event: dict) -> dict:
body = json.loads(event.get("body", "{}"))
return {"message": "OK", "data": body}
3. 同時実行数とスロットリング
Lambda の同時実行制限:
- リージョンごとのデフォルト上限: 1000
- 1関数に予約枠を設定可能(Reserved Concurrency)
スロットリングが起こる条件:
同時実行数がリージョン上限 or 関数の予約枠を超えた場合
エラー: TooManyRequestsException (HTTP 429)
対策:
SQS → Lambda: SQS がバッファリング → スロットリングを自動リトライ
API Gateway → Lambda: クライアントに 429 を返す → クライアント側でリトライ実装が必要
# 予約済み同時実行数の設定
aws lambda put-function-concurrency \
--function-name myapp \
--reserved-concurrent-executions 50
# Provisioned Concurrency(コールドスタート対策)
aws lambda put-provisioned-concurrency-config \
--function-name myapp \
--qualifier production \
--provisioned-concurrent-executions 5
4. API Gateway + Lambda(REST API vs HTTP API)
REST API:
✅ 高度なリクエスト変換(マッピングテンプレート)
✅ API キー・使用量プラン
✅ キャッシュ
❌ 高コスト ($3.5/百万リクエスト)
→ 複雑な要件
HTTP API:
✅ 低コスト ($1.0/百万リクエスト)
✅ JWT 認証(Cognito/Auth0 との連携が容易)
✅ 低レイテンシ
→ 新規では基本こちらを選ぶ
# SAM (Serverless Application Model) でのシンプルな定義
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: production
Auth:
DefaultAuthorizer: CognitoAuthorizer
Authorizers:
CognitoAuthorizer:
UserPoolArn: !GetAtt UserPool.Arn
MyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src/
Handler: handler.handler
Runtime: python3.12
Environment:
Variables:
TABLE_NAME: !Ref DynamoTable
Events:
GetItem:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /items/{id}
Method: GET
Policies:
- DynamoDBReadPolicy:
TableName: !Ref DynamoTable
5. Step Functions — ワークフロー設計
用途: 複数のLambdaを組み合わせた複雑な処理フロー
例: 注文処理 → 在庫確認 → 決済 → 配送手配 → 通知
Express vs Standard:
Express: 高スループット・短時間(5分以内)・価格安
Standard: 長時間(1年まで)・待機中は課金なし・イベント履歴
{
"Comment": "注文処理ワークフロー",
"StartAt": "CheckInventory",
"States": {
"CheckInventory": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:function:check-inventory",
"Next": "ProcessPayment",
"Catch": [{
"ErrorEquals": ["InsufficientStock"],
"Next": "NotifyOutOfStock"
}]
},
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:function:process-payment",
"Retry": [{
"ErrorEquals": ["PaymentTimeout"],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2.0
}],
"Next": "ParallelShipping"
},
"ParallelShipping": {
"Type": "Parallel",
"Branches": [
{"StartAt": "ArrangeDelivery", "States": {"ArrangeDelivery": {"Type": "Task", "Resource": "...", "End": true}}},
{"StartAt": "SendConfirmEmail", "States": {"SendConfirmEmail": {"Type": "Task", "Resource": "...", "End": true}}}
],
"End": true
},
"NotifyOutOfStock": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:function:notify-user",
"End": true
}
}
}
6. Lambda の権限設計
2種類のロール:
1. 実行ロール(Execution Role): Lambda が他の AWS サービスを呼ぶための権限
2. リソースベースポリシー: 他のサービスが Lambda を呼び出す権限
例: API Gateway → Lambda を許可
# API Gateway が Lambda を実行できるようにする(自動設定されるが確認方法)
aws lambda get-policy --function-name myapp
# Lambdaが S3・DynamoDB・Secrets Manager を使えるように
# → IAM ロールに以下のポリシーをアタッチ
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}
7. 観測性(Observability)の設計
# Lambda Powertools(Python)で構造化ログ・トレース
from aws_lambda_powertools import Logger, Tracer, Metrics
from aws_lambda_powertools.metrics import MetricUnit
logger = Logger()
tracer = Tracer()
metrics = Metrics(namespace="MyApp")
@logger.inject_lambda_context
@tracer.capture_lambda_handler
@metrics.log_metrics
def handler(event, context):
metrics.add_metric(name="OrderProcessed", unit=MetricUnit.Count, value=1)
logger.info("Processing order", order_id=event["orderId"])
with tracer.capture_method:
result = heavy_computation()
return result
🔗 次のステップ
- 02_コンテナサービス.md — ECS/EKS との使い分け
- 04_データベースサービス.md — Lambda + DynamoDB パターン
- ../../06_DevOps/IaC/01_Terraform.md — Lambda を Terraform で管理