Pushgateway->Prometheus->Alertmanager->Webhook

## 目的
Pushgateway, Prometheus, Alertmanagerの連携動作を確認しよう

## やること

Pushgatewayにアラートルールを満たすメトリクスをPOSTすると外部サービスでアラートを受信することを確認する。

図をクリックするとPlantUMLを確認できます([gist](https://gist.github.com/loftkun/be133823f9957493c0580c637eca2905))。

[![シーケンス](http://www.plantuml.com/plantuml/png/lP2nJaCn34NtV8L76Fe33AWCp3ofZ4Z8IftKKc8Zntd0t_C21Oad9DGEdNtFFRhUDaVpNak8tGfiDhSmzNR8wFIAxt03qyFk4IgvSMnF0bUGr41K00kPmtK8a-boFr1lV-0j6hxGTq4unNAcOHM-6oKL1JFPI9FiZyKiIRzSR2nvY7w51tt_vcI2PKSsSwH_2awRPzIqp7IncKVVVgX4dK1diZN8wPDz_WalQ5H9d84Rk7Kwvqah_NOvzre-0000)](http://www.plantuml.com/plantuml/uml/lP2nJaCn34NtV8L76Fe33AWCp3ofZ4Z8IftKKc8Zntd0t_C21Oad9DGEdNtFFRhUDaVpNak8tGfiDhSmzNR8wFIAxt03qyFk4IgvSMnF0bUGr41K00kPmtK8a-boFr1lV-0j6hxGTq4unNAcOHM-6oKL1JFPI9FiZyKiIRzSR2nvY7w51tt_vcI2PKSsSwH_2awRPzIqp7IncKVVVgX4dK1diZN8wPDz_WalQ5H9d84Rk7Kwvqah_NOvzre-0000)

アプリケーション | 機能
 --- | --- 
[Pushgateway](https://prometheus.io/docs/instrumenting/pushing/) | ・PUSHされたメトリクスを公開する
[Prometheus](https://prometheus.io/docs/introduction/overview/) | ・メトリクスを収集する<br>・アラート条件を満たした場合アラートをAlertmanagerに送信する
[Alertmanager](https://prometheus.io/docs/alerting/alertmanager/) | ・アラートを受信しreceiver設定に従い外部サービスに送信する<br>・receiverにはemailやslack等が用意されている。<br>・今回はreceiverとしてWebhookを使用し、自前のサーバに送信するようにした。
ExternalService | ・SMTPサーバやslack APIエンドポイント等。<br>・今回はnode.jsで簡易なWebサーバを準備した。 |

## 環境
- クライアント(Ubuntu 17.10)
  - `curl`でPushgatewayにメトリクスをPOSTする用
- k8sクラスタ(GKE)
  - Pushgatewary, Prometheus, Alertmanagerをデプロイ済み
  - デプロイツールは`helm`
  - デプロイ手順は[Helm v2のすゝめ](https://qiita.com/loftkun/private/853bbaabd4bf0fa96e9c)に記載しています。
- Webサーバー(EC2(Ubuntu 16.04))
  - リクエスト内容をログに残す簡易なHTTPサーバーを稼働済み(node.js(Express))。


## Prometheus
`prometheus.yaml`に以下設定が必要です。

1. `targets` に PrometheusがscrapeすべきPushgatewayを設定する
1. `alertmanagers` に Prometheusがアラートを送信すべきAlertmanagerを設定する
1. `rule_files`にアラートルールファイルのパスを設定する。

今回はHelmでデプロイしたので1と2はService Discoveryが設定済みです。

### Service Discoveryできていることを確認する

`Status` -> `Configration` を確認します。
Alertmanager や Pushgateway は Service Discovery設定がされています。
![Prometheus3.png](https://qiita-image-store.s3.amazonaws.com/0/21106/aab1f991-14d7-8e0a-8374-d1092251ab85.png)

`Status` -> `Runtime & Build Information` を確認します。
AlertmanagerがDiscoveryされています。
![Prometheus2.png](https://qiita-image-store.s3.amazonaws.com/0/21106/ee5eec80-e7d4-dbd7-109d-3ce18466c506.png)

`Status` -> `Targets` を確認します。
PushgatewayがDiscoveryされています。
![Prometheus9.png](https://qiita-image-store.s3.amazonaws.com/0/21106/7886a0e1-d470-b9fc-9251-9c2727ababfb.png)

### アラートルールを設定する

[公式ドキュメント](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)のサンプルルールを設定してみましょう。

Configmapをeditします。

```
$ kubectl edit configmap test-prometheus-server -n helm-test
configmap "test-prometheus-server" edited
$ kubectl get configmap test-prometheus-server -n helm-test -o yaml
( 略 : )
  rules: |
    groups:
    - name: example
      rules:

      # Alert for any instance that is unreachable for >5 minutes.
      - alert: InstanceDown
        expr: up == 0
        for: 5m
        labels:
          severity: page
        annotations:
          summary: "Instance {{ $labels.instance }} down"
          description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."

      # Alert for any instance that has a median request latency >1s.
      - alert: APIHighRequestLatency
        expr: api_http_request_latencies_second{quantile="0.5"} > 1
        for: 10m
        annotations:
          summary: "High request latency on {{ $labels.instance }}"
          description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"
( 略 : )
```

edit後、しばらく待つと設定がリロードされます( 設定をリロードするコンテナがPrometheus Pod内にいます )。

WebUIの`Alerts`を確認します。確かにルールを設定できています。
![Prometheus4.png](https://qiita-image-store.s3.amazonaws.com/0/21106/60ceff98-8cdf-14dc-06cd-03af45db22f8.png)

## Alertmanager 
Prometheusから受信したアラートの送信先(`receiver`)の設定が必要です。
今回は外部サーバに送信するため、webhookの設定を行います。

### receiverを設定する。

WebUIの`Status`を開いて`Config`を確認します。
ここにreceiverを設定してあげれば様々な外部サービスにアラートを飛ばせます。
![Alertmanager2.png](https://qiita-image-store.s3.amazonaws.com/0/21106/7c524a3d-6365-2b62-755f-0231d712c067.png)

今回はwebhook_configsを追加し外部サーバにアラートをPOSTするようにしてみます。
ConfigMapをeditします。

``` bash
$ kubectl edit configmap test-prometheus-alertmanager -n helm-test
configmap "test-prometheus-alertmanager" edited
$ kubectl get configmap test-prometheus-alertmanager -n helm-test -o yaml
( 略 : )
  alertmanager.yml: |
    global: {}
    receivers:
    - name: default-receiver
      webhook_configs:
        - url: "http://外部サーバのエンドポイント/"
( 略 : )
```
しばらく待つと設定がリロードされます。

## Pushgateway
### メトリクスをPOSTする

Pushgateway に[公式ドキュメント](https://github.com/prometheus/pushgateway)のサンプルメトリクスをPOSTしてみます。

```bash
$ # POSTする
$ cat <<EOF | curl --data-binary @- http://localhost:9091/metrics/job/some_job/instance/some_instance
# TYPE some_metric counter
some_metric{label="val1"} 42
# TYPE another_metric gauge
# HELP another_metric Just an example.
another_metric 2398.283
EOF
$
```

### メトリクスを確認する

WebUI をブラウザで開きます。
POSTしたメトリクスが確かに公開されています。
![Pushgateway2.png](https://qiita-image-store.s3.amazonaws.com/0/21106/954ffbf1-88ec-20c8-eca8-d3c67ea66b2d.png)

PrometheusのWebUIで[some_metricで検索](http://localhost:9090/graph?g0.range_input=1h&g0.expr=some_metric&g0.tab=0)します。
確かにPrometheusがメトリクスを収集しています。
![Prometheus5.png](https://qiita-image-store.s3.amazonaws.com/0/21106/8c4ac2f9-120f-e418-04e2-ea19b6eacaf2.png)

## アラートを発火させる
ここからが本題です。以下の連携を確認しましょう。

```
・アラートが発火するようなメトリクスをPushgatewayにPOSTする
・Pushgatewayがメトリクスを公開する
・PrometheusがPushgatewayのメトリクスを収集する
・アラートルールを満たしたらPrometheusがAlertmanagerにアラートを送信する
・Alertmanagerがアラート受信する
・Alertmanagerがreceiver(webhook)の機能で外部サーバにPOSTする
・外部サーバがアラートを受信する
```

### Pushgateway
アラートルール `up == 0` を満たすメトリクスをPOSTしてみましょう。

```
$ cat <<EOF | curl --data-binary @- http://localhost:9091/metrics/job/some_job/instance/some_instance
  # TYPE up counter
  up 0
EOF
$
```
確かにメトリクスが公開されました。
![Pushgateway3.png](https://qiita-image-store.s3.amazonaws.com/0/21106/82ed5166-a1be-670e-c18e-8e16c5822f31.png)


### Prometheus
Pushgatewayのメトリクスを収集した結果、アラートが`PENDING`状態となっています。
5分待ちます。
![Prometheus6.png](https://qiita-image-store.s3.amazonaws.com/0/21106/e31e58b5-c516-523e-df61-064e6edfc0ad.png)

5分経過しました、アラートが`FIRING`状態になり、発火しました！
![Prometheus7.png](https://qiita-image-store.s3.amazonaws.com/0/21106/4c991a6d-c5e6-4e15-4452-37af9b3efdc4.png)

### Alertmanager
Prometheusから送信されたアラートを確かに受信しています。
このアラートをreceiver(Webhook)の機能で外部サーバに送信しているでしょうか？
![Alertmanager3.png](https://qiita-image-store.s3.amazonaws.com/0/21106/97aa2aa3-4b14-c886-a875-aa07c8d2de00.png)


### 外部サーバ
Webサーバのログを見ると確かにアラートがPOSTされていました。
アラート情報は以下のようなjsonになっていました。

```json
{ receiver: 'default-receiver',
   status: 'firing',
   alerts:
    [ { status: 'firing',
        labels: [Object],
        annotations: [Object],
        startsAt: '2018-11-29T15:14:18.501662728Z',
        endsAt: '2018-11-29T15:17:18.501662728Z',
        generatorURL:
         'http://test-prometheus-server-7bd886b769-glcgt:9090/graph?g0.expr=up+%3D%3D+0&g0.tab=1' } ],
   groupLabels: {},
   commonLabels:
    { alertname: 'InstanceDown',
      instance: 'some_instance',
      job: 'some_job',
      severity: 'page' },
   commonAnnotations:
    { description:
       'some_instance of job some_job has been down for more than 5 minutes.',
      summary: 'Instance some_instance down' },
   externalURL: '',
   version: '4',
   groupKey: '{}:{}' }
```

## アラートを鎮火する
アラートを止めるために、up == 1 となるメトリクスをPushgatewayにPOSTしておきましょう。

```bash
cat <<EOF | curl --data-binary @- http://localhost:9091/metrics/job/some_job/instance/some_instance
  # TYPE up counter
  up 1
EOF
```

無事にアラートが鎮火しました。
![Prometheus8.png](https://qiita-image-store.s3.amazonaws.com/0/21106/d8185663-b9fc-46af-7258-eb23c6e42477.png)


## まとめ

Pushgateway, Prometheus, Alertmanagerの連携動作を確認できました。

ポイント

- Pushgatewayを使うと手軽にメトリクスを生成できるぞ
- Pushgateway, Prometheus, AlertmanagerをHelmでデプロイした恩恵により、変更する設定項目はアラートルールだけで済んだ(service discovery最高！)
- Webhookを活用するとemailやslackだけでなくいろいろなサービスとも連携できそうですね！