DatadogのAWS integrationとAlertの設定をTerraformで行い、バージョン管理やレビューできるようにする。 全体のコードはGitHubに置いてある。
AWS Integration
まずdatadog_integration_awsでAWS integrationの設定を作成してExternalIDを取得し、Policy/Roleを作成する。必要な権限はドキュメントを参照。
resource "datadog_integration_aws" "test" {
account_id = "${var.aws_account_id}"
role_name = "${var.aws_integration_role_name}"
filter_tags = ["datadog:1"]
}
data "aws_iam_policy_document" "datadog_aws_integration_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::464622532012:root"]
}
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = [
"${datadog_integration_aws.test.external_id}",
]
}
}
}
Datadog providerにはないSlackなどその他のintegrationは手動で設定する必要がある。 また、ログを集める場合Serverless Application Repositoryから公式のDatadog-Log-Forwarderを入れて AWS IntegrationのところにLambdaのARNを入れるのも手動。
追記 (2020-12-07): 今はDatadog Forwaderとなり、Serverless Application Repositoryからではなく、直接CloudFormationのスタックを上げるようになっているのでaws_cloudformation_stackでデプロイできる。AWS IntegrationのRoleに必要なPolicyを与えて
Collect LogsタブのLambda Cloudwatch Logsにチェックを入れると、Optionally limit resource collectionを設定しているならそのtagを持つ、全てのFunctionのStreamに自動でForwarderへのSubscription Filterが作成され転送が始まる。チェックを外すと削除されるが、この際もtagを見ているようで、条件を変更するとSubscription Filterが一部残ることがあった。ログだけではなくlambdaからのカスタムメトリクスの中継や、estimated_costなどの拡張メトリクスの送信も行う。
Alert
datadog_monitorでAlertを作成する。 今回はLambdaの実行が失敗したときと、WARNという文字列が含まれるログが一定数出力されたときにAlertを飛ばすようにしてみた。 なおinfoやwarnといったログレベルはjsonのlevelフィールドに入れればデフォルトのpipelineでマップされるので通常は文字列比較などする必要はない。
初めに作るときは一度画面で意図通りアラートが飛ぶものを作成し、クエリなどをコピーするのが確実。
resource "datadog_monitor" "lambda-error-alert" {
name = "lambda-error-alert"
type = "metric alert"
message = "Error occurred. @slack-alert"
# Note: We highly recommend a delay of at least 900s for AWS metrics.
evaluation_delay = 900
query = "sum(last_1m):avg:aws.lambda.errors{*} by {functionname}.as_count() >= 3"
thresholds {
ok = 0
critical = 3
critical_recovery = 2
}
renotify_interval = 10
}
resource "datadog_monitor" "lambda-warn-log-alert" {
name = "lambda-warn-log-alert"
type = "log alert"
message = "Many warn logs are being outputted. @slack-alert"
query = "logs(\"service:lambda WARN\").index(\"main\").rollup(\"count\").by(\"functionname\").last(\"5m\") > 4"
thresholds {
ok = 0
warning = 2
critical = 4
}
renotify_interval = 10
}
一定確率でログやエラーを出すLambdaを動かしてAlertが飛ぶことを確認した。
func Handler(ctx context.Context) (string, error) {
if rand.Intn(10) < 7 {
fmt.Println("[WARN] some problem")
}
if rand.Intn(100) > 10 {
return "", errors.New("error")
}
return "done", nil
}