DatadogのAWS integrationとAlertの設定をTerraformで行う

(2019-05-04)

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は手動で設定する必要がある。 また、Logも集める場合Serverless Application Repositoryから公式のDatadog-Log-Forwarderを入れて AWS IntegrationのところにLambdaのARNを入れるのも手動。

Alert

datadog_monitorでAlertを作成する。 今回はLambdaの実行が失敗したときと、WARNという文字列が含まれるログが一定数出力されたときにAlertを飛ばすようにしてみた。 なおinfoやwarnといったログレベルはjsonのlevelフィールドに入れればデフォルトのpipelineでマップされるので通常は文字列比較などする必要はない。

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
}

Slackに流れるAlert