エラーの内容
Lambdaファンクションのログに以下が表示されます:
Task timed out after 3.00 seconds
Lambdaが処理完了前にファンクションを強制終了しました。デフォルトの3秒タイムアウトはhello-world程度のファンクションなら問題ありません。しかし、サードパーティAPIへのHTTPリクエストや負荷時のデータベースクエリを1つ追加するだけで、3秒はあっという間に過ぎてしまいます。
根本原因
- デフォルトタイムアウトが短すぎる — Lambdaのデフォルトは3秒です。遅いAPIコールが1回あるだけで、気づかないうちに超過してしまいます。
- 外部ネットワーク呼び出し — サードパーティAPI、RDS、DynamoDBへのHTTPリクエストは、負荷時に1〜5秒かかることがあり、それ以上になる場合もあります。
- コールドスタートのレイテンシ — JVMベースのランタイム(Java、Kotlin)は、コードが実行される前に通常800ms〜2秒追加されます。大きなPythonパッケージはさらに200〜400msを加えることがあります。
- 無限ループやブロッキング待機 — 終了しないポーリングループや、コールスタックのどこかに埋まっている
time.sleep(10)など。 - VPCネットワーキング — VPC内のファンクションはコールドスタートが遅く、NATゲートウェイが設定されていない場合、外部呼び出しでハングアップします。
ステップ1 — まずログを確認する
設定はまだ変更しないでください。CloudWatch Logsを開いて、時間がどこで消費されているかを確認しましょう。
# AWS CLIでログをリアルタイム確認
aws logs tail /aws/lambda/your-function-name --follow
# または実行時間を示すREPORTラインのみフィルタリング
aws logs filter-log-events \
--log-group-name /aws/lambda/your-function-name \
--filter-pattern "REPORT" \
--query 'events[*].message' \
--output text
REPORTラインに必要な情報がすべて含まれています:
REPORT RequestId: abc-123 Duration: 2987.43 ms Billed Duration: 2988 ms Memory Size: 128 MB Max Memory Used: 67 MB Init Duration: 412.31 ms
Init Durationが400msを超えている場合はコールドスタートの問題を示しています。Durationがタイムアウト上限に近い場合は、ファンクション自体が本当に遅いため、上限を引き上げるのが正しい対処法です。
ステップ2 — タイムアウトを増やす
まずタイムアウトを増やしましょう。最大値は15分(900秒)です。
AWSコンソールから設定する
- Lambda → 対象のファンクション → 設定 → 一般設定に移動する
- 編集をクリックする
- タイムアウトを現実的な値に設定する — 外部呼び出しを行うファンクションには30秒が適切な出発点です
- 保存をクリックする
AWS CLIから設定する
aws lambda update-function-configuration \
--function-name your-function-name \
--timeout 30
Serverless Framework(serverless.yml)から設定する
functions:
myFunction:
handler: handler.main
timeout: 30 # 秒
Terraformから設定する
resource "aws_lambda_function" "my_function" {
function_name = "my-function"
timeout = 30
# ... その他の設定
}
おおよその目安として、タイムアウトは予想される最悪実行時間の2〜3倍に設定しましょう。900に設定したくなる気持ちはわかりますが、それでは実際のパフォーマンス低下を見逃してしまいます。
ステップ3 — タイミングログを追加してボトルネックを特定する
30秒でもタイムアウトする場合は、コードに計測処理を追加して、どのステップが時間を消費しているかをログで正確に把握しましょう。
Python
import time
import logging
logger = logging.getLogger()
def handler(event, context):
t0 = time.time()
result = fetch_from_database()
logger.info(f"DBクエリの所要時間: {time.time() - t0:.2f}秒")
t1 = time.time()
response = call_external_api(result)
logger.info(f"API呼び出しの所要時間: {time.time() - t1:.2f}秒")
return response
Node.js
exports.handler = async (event) => {
console.time('db-query');
const result = await fetchFromDatabase();
console.timeEnd('db-query');
console.time('api-call');
const response = await callExternalAPI(result);
console.timeEnd('api-call');
return response;
};
デプロイして一度実行し、CloudWatchを確認してください。遅いステップがすぐに特定できます。
ステップ4 — 遅い処理を最適化する
すべてのHTTPクライアントに明示的なタイムアウトを設定する
タイムアウトが設定されていないHTTPクライアントは無限に待ち続けます。例外なく、すべての外部呼び出しにタイムアウトを設定してください。
# Pythonのrequestsライブラリ
import requests
response = requests.get('https://api.example.com/data', timeout=5) # 最大5秒
# Pythonのhttpx(非同期)
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get('https://api.example.com/data')
// Node.jsのaxios
const axios = require('axios');
const response = await axios.get('https://api.example.com/data', {
timeout: 5000 // 5秒(ミリ秒単位)
});
SDKクライアントの初期化をハンドラの外に移す
ハンドラ内で作成されたAWS SDKクライアントは、呼び出しのたびに再構築されます。代わりにモジュールレベルに移動しましょう。Lambdaはウォームスタート時に実行環境を再利用するため、セットアップコストは一度だけで済みます。
# 悪い例 — 呼び出しのたびに新しいクライアントを作成
def handler(event, context):
dynamodb = boto3.client('dynamodb')
...
# 良い例 — クライアントを一度初期化し、ウォームスタート時に再利用
import boto3
dynamodb = boto3.client('dynamodb')
def handler(event, context):
...
メモリを増やしてCPUを強化する
LambdaのCPUはメモリに比例してスケールします。128MBから512MBにすると、CPUが4倍になります。CPU負荷の高いワークロードでは、実行時間を半分以下に削減できます。
aws lambda update-function-configuration \
--function-name your-function-name \
--memory-size 512
Lambda Power Tuningを実行して、自分のファンクションに合った速度とコストのバランスが取れるメモリ設定を見つけましょう。
VPC内のLambda
NATゲートウェイがない場合、ファンクションはインターネットに接続できません。外部HTTPコールはLambdaが強制終了するまでハングアップします。サブネットのルートテーブルを確認してください — NATゲートウェイを指す0.0.0.0/0のルートが必要です(NATインスタンスでは不十分です)。
# ファンクションがVPCにアタッチされているか確認
aws lambda get-function-configuration \
--function-name your-function-name \
--query 'VpcConfig'
ステップ5 — 修正を確認する
ファンクションを直接呼び出してレスポンスを確認します:
# 同期テスト呼び出し
aws lambda invoke \
--function-name your-function-name \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
output.json
cat output.json
CloudWatchのREPORTラインを確認します。Durationがタイムアウト設定を大幅に下回っていれば完了です:
REPORT RequestId: xyz-456 Duration: 1243.12 ms Billed Duration: 1244 ms
ログにTask timed out after 3.00 secondsが表示されなければ修正完了です。
まとめと教訓
- 3秒はLambdaのデフォルト値であり、推奨値ではありません。多くの本番ファンクションは15〜60秒が必要です。
- 外部呼び出しを行うすべてのファンクションにタイムアウトを設定してください — そしてその値はデフォルトのままにせず、意図的に決定してください。
- JavaとKotlinのコールドスタートは、ハンドラが実行される前に通常1〜2秒消費します。それが許容できない場合は、Provisioned ConcurrencyまたはSnapStart(Java 11以降)を検討してください。
- HTTPクライアントのタイムアウトはLambdaのタイムアウトより短く設定してください。そうすれば、Lambdaがファンクション全体を強制終了するのを待つのではなく、適切な例外を取得して処理できます。

