created: 2023-10-22T01:58:05.462Z
terraform でGCPのalert_policyをfor_eachでまとめて定義する
CloudFunction から API を叩いて処理するようなコードで、たたく API があんまり安定しないという場合がある。 ただ、API エラーの種類ごとにアラートを設定したい場合もある。今回はタイムアウトはけっこう発生するけど TLS コネクションエラーも少数発生するので、それぞれ通知する閾値を変えたい。
前提として CloudFunctionFailure
というログベースのメトリクスを生成しておいて、それに対してのフィルタリングと閾値を変化させて alert_policy のリソースをたくさん生成するということをする。terraform の for_each
を使う。
なお、ほとんどの定義は terraform import
で生成するもので、下記のコードは完全な定義ではない(いろいろ省いたもの)。完全な定義を取得する方法はこちらで書いた。
コード
locals {
failure_alert_definitions = [
{
failure_type = "HttpReadTimeout",
function_name = "Collector",
threshold = 10,
duration = "1800s"
},
{
failure_type = "HttpConnTimeout",
function_name = "Collector",
threshold = 10,
duration = "1800s"
},
{
failure_type = "HttpsTlsConnReset",
function_name = "Collector",
threshold = 3,
duration = "1800s"
},
{
failure_type = "HttpUnexpected",
function_name = "Collector",
threshold = 1,
duration = "1800s"
}
]
}
resource "google_monitoring_alert_policy" "Failure" {
for_each = {
for i in local.failure_alert_definitions :
"${i.function_name}_${i.failure_type}" => i
}
display_name = "${each.value.function_name}-${each.value.failure_type}"
conditions {
display_name = "${each.value.function_name}-${each.value.failure_type}"
condition_threshold {
threshold_value = each.value.threshold
duration = each.value.duration
comparison = "COMPARISON_GT"
denominator_filter = null
filter = <<-EOS
resource.type="cloud_function"
AND metric.type="logging.googleapis.com/user/CloudFunctionFailure"
AND metric.labels.function_name="${each.value.function_name}"
AND metric.labels.failure_type="${each.value.failure_type}"
EOS
aggregations {
alignment_period = "600s"
cross_series_reducer = "REDUCE_SUM"
group_by_fields = []
per_series_aligner = "ALIGN_DELTA"
}
trigger {
count = 1
percent = 0
}
}
}
documentation {
content = <<-EOS
# ${each.value.function_name}/${each.value.failure_type}
- 期間 ${each.value.duration}
- 閾値 ${each.value.threshold}
EOS
mime_type = "text/markdown"
}
}
Array => Map
terraform では、Array を for_each させることはできない。これは生成されゆくリソースを一意に識別できる必要があるためで、これはたぶん React でループから要素を生成するときに key 属性が必要なのと同じような理由なのだろう。
Array はダメだけど、要素が重複せず一意になるデータ構造である Set や Map なら for_each することができるので、配列を Map に変換してリソース定義に使う必要がある。
Python のリスト内包表記のようにして配列から Map を生成するコードはこのようになる。 i
が配列の要素であり、"${i.function_name}_${i.failure_type}"
と長い部分が Map のキーになる。
for_each = {
for i in local.failure_alert_definitions :
"${i.function_name}_${i.failure_type}" => i
}