New RelicによるAWSの監視をTerraformで自動化する(第2回: アラート)

# 前回

https://qiita.com/neruneruo/items/3a1739d6726abd9432b4

# はじめに
New Relic記事第2弾。
前回は、AWSのメトリクスを、CloudWatchメトリクスストリームを使ってNew Relicに転送してサマリを監視するところまでをTerraformで自動化した。

今回は、メトリクスを監視してアラートを作成する部分を構築する。もちろん、Terraformでの自動化方法についても紹介する。

# アラート作成
前回作ったLambda関数は一定確率でエラー終了して、Lambda Function URLsで500応答を返すようにしている。

```python
import json
import random

def lambda_handler(event, context):
  random_value = random.random()

  if random_value < 0.5:
    return {
      'statusCode': '200',
      'body': json.dumps({'message': 'OK.'}),
    }
  else:
    return {
      'statusCode': '500',
      'body': json.dumps({'message': 'NG.'}),
    }
```

今回、一定間隔でこのLambda関数を実行し、1つでもエラーがあった場合にアラートを上げるような設定を行う。
実際の運用上、これが適切な方法かはしっかりと吟味を行っていただきたい。

:::note info
New Relicには３種類のアラートの発報方法がある。

- ケイデンス(Cadence)
- イベントタイマー(Evnet Timer)
- イベントフロー(Event Flow)  ※現在のデフォルトはこれ

ワークロードの特性に応じて使い分ける必要があるが、公式のドキュメントが難解なので、時系列での図解がある記事へのリンクも張っておく。
:::

https://newrelic.com/jp/blog/nerdlog/new-aggregation-methods-for-nrql-alert-conditions

https://newrelic.com/jp/blog/how-to-relic/understand-nrql-alert-condition

https://tech.yappli.io/entry/new-relic-streaming-method

リンク内でも言及があるが、CloudWatch メトリクスストリームの場合はEvent Flowを選択するのが適切なようなので、今回はEvent Flowで作成をした。

## アラートの構成要素
アラートは以下の3つの構成要素からなっている(括弧内はTerraformのリソース名)。

- アラートポリシー(newrelic_alert_policy)
- アラート条件(newrelic_nrql_alert_condition)
- アラート通知

アラート通知は、さらに以下の構成要素に分解される。

- ワークフロー(newrelic_workflow)
- チャネル(newrelic_notification_channel)
- 目的地(newrelic_notification_destination)

それぞれの概要は、以下の公式ドキュメントを参照していただきたい。

https://docs.newrelic.com/jp/docs/alerts/get-notified/intro-notifications/

## アラートポリシー

アラートは一連のアラート条件をまとめて、どのように通知をするかをまとめる概念を指す。

https://docs.newrelic.com/jp/docs/alerts/organize-alerts/create-edit-or-find-alert-policy/

Terraform では以下のリソース定義を行う。

```HCL
resource "newrelic_alert_policy" "example" {
  name = "Lambda Alert Example"

  incident_preference = "PER_CONDITION_AND_TARGET"
}
```

```incident_preference```については、以下のNew Relicの公式ドキュメントと、TerraformのNew Relic Providerの公式ドキュメントを参照。
これは、アラート通知の粒度を決定するものだ。
今回はもっとも細かい粒度で通知をする設定を行う。

https://docs.newrelic.com/jp/docs/alerts/organize-alerts/specify-when-alerts-create-incidents/#preference-options

https://registry.terraform.io/providers/newrelic/newrelic/latest/docs/resources/alert_policy#incident_preference

## アラート条件
アラート条件は、NRQLというNew Relic用のクエリ言語で取得できる情報に対してどのような閾値を設定するかを設定する。
また、```policy_id```でアラートポリシーとの紐付けも行う。

前述した通り、今回はLambda関数の実行で一つでもエラーがあったらアラートを上げる設定を行う。
メトリクスストリームから```aws.lambda.Url5xxCount```のメトリクスを拾って閾値を設定しよう。
他のLambda関数の情報を拾って誤検知しないように、WHERE句で``` `aws.lambda.functionName` = '${aws_lambda_function.example1.function_name}'```で絞り込みを行うのも忘れないように。


```HCL
resource "newrelic_nrql_alert_condition" "example" {
  enabled = true

  name                           = "P1 | 5xx HTTP Response | Lambda Function Example"
  description                    = "Alert when AWS Lambda Functions returned 5xx HTTP Response."
  type                           = "static"
  policy_id                      = newrelic_alert_policy.example.id
  title_template                 = "New Relic Alert Example: {{conditionName}}"
  fill_option                    = "static"
  fill_value                     = 0
  aggregation_method             = "event_flow"
  aggregation_window             = 60
  aggregation_delay              = 120
  open_violation_on_expiration   = false
  close_violations_on_expiration = false
  ignore_on_expected_termination = true

  nrql {
    query = <<EOQ
SELECT
  count(`aws.lambda.Url5xxCount`)
FROM 
  Metric
WHERE
  `collector.name` = 'cloudwatch-metric-streams' AND
  `aws.accountId` = '${data.aws_caller_identity.self.account_id}' AND
  `aws.lambda.functionName` = '${aws_lambda_function.example1.function_name}'
EOQ
}

  critical {
    operator              = "above"
    threshold             = 1
    threshold_duration    = 60
    threshold_occurrences = "at_least_once"
  }
}
```

```title_template```では、通知のタイトルを設定する。
ここには、HandleBarsの記法で変数置換が可能になっている。通知の視認性を上げるためにも分かりやすいタイトルの設定をしておこう。今回の例で言えば、```{{conditionName}}```は、このアラートの名前(```name```プロパティ)と同じ値が設定される。
詳細はNew Relicの以下の公式ドキュメントを参照。

https://docs.newrelic.com/jp/docs/alerts/create-alert/condition-details/title-template/


その他、様々な設定が行えるが、詳細は以下の公式ドキュメントを参照。

https://docs.newrelic.com/jp/docs/alerts/create-alert/create-alert-condition/alert-conditions/

:::note warn
NRQLを使用しない```newrelic_alert_condition```というリソースも存在するが、TerraformのNew Relic Providerの公式ドキュメントでdeprecatedである旨が記載されているため、本記事では取り扱わない。
:::

## アラート通知
アラート通知は以下の通り3つのリソースを設定する。

### ワークフロー
ワークフローでは、通知チャネルの設定と、通知のフィルタ条件を設定する。
今回は```newrelic_alert_policy.example.id```に合致するもののみ通知する設定だ。

```HCL
resource "newrelic_workflow" "example" {
  name                  = "example"
  muting_rules_handling = "NOTIFY_ALL_ISSUES"

  issues_filter {
    name = "Issue Filter"
    type = "FILTER"
    predicate {
      attribute = "labels.policyIds"
      operator  = "EXACTLY_MATCHES"
      values    = [newrelic_alert_policy.example.id]
    }
  }

  destination {
    channel_id = newrelic_notification_channel.example.id
  }
}
```

### チャネル
チャネルでは、何のチャネルを使用するかを選択する。
```EMAIL```以外にも、```SERVICE_NOW_APP```, ```WEBHOOK```, ```JIRA_CLASSIC```, ```MOBILE_PUSH```, ```EVENT_BRIDGE```, ```SLACK```, ```SLACK_COLLABORATION```, ```PAGERDUTY_ACCOUNT_INTEGRATION```, ```PAGERDUTY_SERVICE_INTEGRATION```と様々な通知先を設定可能だ。

```property```項目は通知先によって異なるため、詳細はTerraformのNew Relic Providerの公式ドキュメントを参照。

https://registry.terraform.io/providers/newrelic/newrelic/latest/docs/resources/notification_channel#nested-property-blocks

```HCL
resource "newrelic_notification_channel" "example" {
  name           = "Email Example"
  type           = "EMAIL"
  destination_id = newrelic_notification_destination.example.id
  product        = "IINT"

  property {
    key   = "subject"
    value = "NewRelic Nortification Test"
  }

  property {
    key   = "customDetailsEmail"
    value = "issue id - {{issueId}}"
  }
}
```

### 目的地(デスティネーション)
デスティネーションでは、実際の通知先を設定する。
要は、Emailの場合はメールアドレスだ。

これも、```type```によって設定する値が異なるため、詳細はTerraformのNew Relic Providerの公式ドキュメントを参照。

https://registry.terraform.io/providers/newrelic/newrelic/latest/docs/resources/notification_destination#nested-property-blocks

```HCL
resource "newrelic_notification_destination" "example" {
  name = "Email Example"
  type = "EMAIL"

  property {
    key   = "email"
    value = local.newrelic_nortice_email_address
  }
}
```

ここまで設定したら、```terraform apply```で設定を反映しよう。

# いざ、動かす！
## メール内容の確認
前回作ったLambda Function URLsのエンドポイントに向けてcurlでリクエストを送り続けてみよう。
すると、```local.newrelic_nortice_email_address```で通知先に設定したメールアドレスに対して、以下のようなメールが届くはずだ。

![キャプチャ1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/11a4184a-346e-122e-993a-d450ad8ee854.png)

この通り、```newrelic_nrql_alert_condition.example.title_template```で設定した```{{conditionName}}}```が正しく置換されていることが確認できる。

## コンソールの確認
コンソールの左メニューから、「Alerts」→「Issues & Activity」を選択すると、上記のメールで送信されたIssueの情報が表示される。

![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/19a1bf09-b1ed-4669-31d0-10c4f7fc080f.png)

この行をクリックすると、メールで通知されたのと同等の情報が参照可能だ。

![キャプチャ3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/573dfdc3-d4b0-05c2-33b4-4fd6fcac85ed.png)

これで、AWSのメトリクスの自動監視が可能になった！

:::note info
なお、発行されたIssueについては、AcknowledgeやCloseの記録が残る。
単にシステムを監視するだけでなく、このような機能を活用することで、DORAメトリクスの1つであるMTTRであったり、最近話題であるMTTAを算出してチームのDevOpsの習熟度を考察することが可能になる。

最近はポストモーテムを記録することもできるようになったので、既存でインシデント管理の仕組みが準備で来ていない場合は、まずはここから使ってみるというのも良いだろう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/4867b023-db44-8080-c9b2-357b796dbd8d.png)
:::

# 次回

https://qiita.com/neruneruo/items/1792ee55d3d22ff03045