概要
-
AWS Lambda
のタイムアウトを検知する実装を解説します - 実装の
Terraform
コードサンプルを提供します -
CloudWatchLogs MetricFilter
におけるハックをお伝えします
AWS Lambdaのタイムアウトを検知する実装
想定ケース
以下のように、EventBridge経由でLabmdaを実行するケースを考えます
引用:
Lambdaの仕様
Lambdaは以下の仕様を持っています
- タイムアウト設定のデフォルト値は 3 秒です
- 最大値の 900 秒 (15 分) まで 1 秒単位で調整できます
- タイムアウトエラーとなった場合、2回までリトライされます
望ましくないシナリオ
- Lambdaの処理時間は、ユーザー数に比例する
- ユーザー数が漸増する
- あるタイミングからLambdaのタイムアウト+リトライが発生し始める
- 上記を検知する機構がない
発生する問題
このシナリオにより、以下のような問題が生じます
- Lambdaの処理が完了できていないことに気づけない
- 意味のないリトライにより、コストが増加する
解決方法
本質的にはタイムアウトが起こらないように処理の並行化などを検討すべきでしょうが、
まずは最低限タイムアウトが発生しないように、タイムアウトの時間を伸ばします
その上で、将来的なタイムアウトを検知できる機構を実装しておきましょう
実装の概要
実装の概要は以下の通りです
なお、最後の通知先はビジネスニーズによると思いますので、本記事では解説しません
- タイムアウト時、CloudWatchLogsに
"Task timed out"
というログが出力される - 上記を検出するCloudWatchLogs MetricFilterを作成する
- MetricFilterを検知してSNSトピックに通知するMetricAlarmを設定する
- (Option) SNSの送信先をChatBotにすることでSlackに通知する
概要図
実装のTerraformコードサンプル
それでは、実装のTerraform
コードサンプルを提供します
CloudWatchLogs MetricFilter
"Task timed out"
を検知するMetricFilterを作成します
// NOTE: Lambdaのタイムアウトを検知。以下のAWS re:Postを参考に作成
// https://repost.aws/ja/knowledge-center/lambda-verify-invocation-timeouts
resource "aws_cloudwatch_log_metric_filter" "sample_task_timed_out" {
log_group_name = "/aws/lambda/${local.lambda_name}"
name = "${local.lambda_name}-task-timed-out"
pattern = "Task timed out"
// 任意で設定可。CloudWatchLogs MetricAlarmで利用する
metric_transformation {
name = "TaskTimedOut"
namespace = ${local.lambda_name}
value = 1
}
}
CloudWatchLogs MetricAlarm & SNS Topic
作成したMetricFilterを検知するMetricAlarmを作成します
resource "aws_cloudwatch_metric_alarm" "sample_task_timed_out" {
alarm_name = "${local.lambda_name}-task-timed-out"
namespace = local.lambda_name
metric_name = "TaskTimedOut"
statistic = "Sum"
threshold = 1
period = 60
evaluation_periods = 1
datapoints_to_alarm = 1
comparison_operator = "GreaterThanOrEqualToThreshold"
alarm_actions = [aws_sns_topic.sample_task_timed_out.arn]
ok_actions = [aws_sns_topic.sample_task_timed_out.arn]
insufficient_data_actions = [aws_sns_topic.sample_task_timed_out.arn]
alarm_description = <<-EOT
## Lambdaがタイムアウトしました
Lambdaがタイムアウトし、処理は完了しませんでした
開発者までお知らせください
EOT
}
resource "aws_sns_topic" "sample_task_timed_out" {
name = "alert-${local.lambda_name}-task-timed-out"
}
CloudWatchLogs MetricFilterにおけるハック
ところで、この"Task timed out"
では検知できずにタイムアウトが発生する場合がありました
原因は正直よくわからないのですが、その場合は"status": "timeout"
がログに出力されるようです
仮に偽陽性になっても運用開始後に調整できるので、一旦このパターンも検出できるようにしました
同じ問題にお困りの方は参考にしてみてください
以下はterrafromのコード例です
// NOTE: 上記のフィルターでは検出できないLambdaのタイムアウトのパターンがあるため、"status": "timeout"の場合も検知
resource "aws_cloudwatch_log_metric_filter" "sample_status_timeout" {
log_group_name = "/aws/lambda/${local.lambda_name}"
name = "${local.lambda_name}-status-timeout"
pattern = "\"\\\"status\\\":\\\"timeout\\\"\""
metric_transformation {
name = "StatusTimeout"
namespace = local.lambda_name
value = 1
}
}
resource "aws_cloudwatch_metric_alarm" "sample_status_timeout" {
alarm_name = "${local.lambda_name}-status-timeout"
namespace = local.lambda_name
metric_name = "StatusTimeout"
statistic = "Sum"
threshold = 1
period = 60
evaluation_periods = 1
datapoints_to_alarm = 1
comparison_operator = "GreaterThanOrEqualToThreshold"
alarm_actions = [aws_sns_topic.sample_task_timed_out.arn]
ok_actions = [aws_sns_topic.sample_task_timed_out.arn]
insufficient_data_actions = [aws_sns_topic.sample_task_timed_out.arn]
alarm_description = <<-EOT
## Lambdaがタイムアウトしました
Lambdaがタイムアウトし、処理は完了しませんでした
開発者までお知らせください
EOT
}
参考
以下の記事を参考にしました