Docker＋Prometheusで外形（URL）監視を作る

初のAdvent calendarは最近触ってみたPrometheusという監視ツールの構築について書きます
みなさん、Webサイトの死活監視はされていますでしょうか？
「自分で構築できるの？」「SaaSと契約するほど大きなシステムじゃないんだけど・・・」「サーバを1から立てるのは辛い」などなど、私もハードルが高い機能の1つだと思っていました
が、DockerとPrometheusを組み合わせることで意外と簡単に外形監視の仕組みを作ることができるので、書いていこうと思います

# 外形（URL）監視とは何か？
外形監視とは、WebサイトやAPIサーバが正常に動いているか？をHTTPアクセスで確認する監視方法です
「レスポンスが正常か？」「規定の秒数以内にレスポンスが返却されるか？」などを確認します
良いところとしては、ユーザと同じ方法でアクセスするため、ネットワークを含めてシステムまたはAPI全体の正常/異常を確認することが可能です
サーバのリソース（CPUやメモリなど）を内部から監視する方法と組み合わせて使うことが多いと思います
AWSのサービスを使っていますが、簡単に説明した図になります

![スクリーンショット 2020-12-18 8.07.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198158/c4e490aa-85ec-047c-3fa6-af8a6789f0a5.png)

# 背景

* Webシステムに対して外形監視をしたい
* SaaSもあるがシステムが大規模じゃない場合はオーバースペックだと感じた（大規模ならお金かけて使った方が良いのかもしれないけれど）
* 柔軟な選択ができるよう環境（開発端末・クラウドなど）に関係なく構築できるようにしたい

# 今回やること

* 監視ツールであるPrometheusを使って外形監視をする
* OSに寄らずに環境構築できるDocker＋Prometheusで構築する
* 開発端末で起動する
* 異常を検知した場合、Slackに通知する

# 説明しないこと

ここでは書かないので、調べてみてください...

* ターミナルの操作方法
* Docker, docker-composeについて

# 環境

今回はMacでやりますが、Dockerを使っているので、windowsでもlinuxでも起動できます

* Mac ： 10.15.5
* Docker ： 19.03.8
* docker-compose ： 1.25.5

# 使用したDockerイメージ

* prom/prometheus ： 2.19.1
* prom/blackbox-exporter ： 0.17.0
* prom/alertmanager ： 0.21.0

# フォルダ・ファイル構成

```
━ docker-compose.yml
┣ prometheus
┃  ┗ alert_roles.yml
┃  ┗ prometheus.yml
┣ blackbox_exporter
┃  ┗ config.yml
┗ alertmanager
  ┗ config.yml
```

# docker-compose.ymlの作成

* prometheus:本体のイメージ
* blackbox_exporter:URL監視してくれるexporter（外部ライブラリ的なもの）
* alertmanager:アラートが発生した時にメッセージ通知をしてくれるexporter(今回はslack通知で使用する)
* 各々のポート番号とconfigファイルを設定していきます

``` yml:docker-compose.yml
version: '3'
services:
  prometheus:
    image: prom/prometheus
    container_name: prometheus
    volumes:
      - ./prometheus:/etc/prometheus
    command: "--config.file=/etc/prometheus/prometheus.yaml"
    ports:
      - 9090:9090
    restart: always
  blackbox_exporter:
    image: prom/blackbox-exporter:latest
    volumes:
      - ./blackbox_exporter/config.yml:/etc/blackbox_exporter/config.yml
  alertmanager:
    image: prom/alertmanager
    container_name: alertmanager
    volumes:
      - ./alertmanager:/etc/alertmanager
    command: "--config.file=/etc/alertmanager/config.yaml"
    ports:
      - 9093:9093
    restart: always
```

# blackbox_exporter

URL監視の設定をyml形式で記載していきます
``modules:``には確認したいプロトコルごとに設定を作っていきます（tcp、pop3、sshもデフォルトで用意されています）
今回はPOSTメソッドの設定として``http_post_2xx:``を追加しています
``headers:``にはHTTPヘッダを設定することができます
設定できる内容についてはblackbox_exporterの[Configuration](https://github.com/prometheus/blackbox_exporter/blob/master/CONFIGURATION.md)を参照してください

``` yml:blackbox_exporter/config.yml
modules:
  http_2xx:
    prober: http
    http:
  http_post_2xx:
    prober: http
    http:
      method: POST
      headers:
        xxx: yyyy
```

# prometheus

prometheus本体で外形監視の設定を記載します
評価内容は``alert_roles.yml''に定義しています

``` yml:prometheus/prometheus.yml
global:
  # prometheusがexporter等に情報を取りに行く間隔(今回はblackbox_exporter)
  scrape_interval:     15s 
  # ruleの評価を行う間隔(今回はrule_filesで指定されているalert_roles.ymlが評価内容)
  evaluation_interval: 15s 
  external_labels:
      monitor: 'codelab-monitor'

rule_files:
  - /etc/prometheus/alert_roles.yml

alerting:
  alertmanagers:
    - scheme: http
      static_configs:
      - targets:
        - alertmanager:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets:
        - prometheus:9090
  # １つの監視条件をjobという単位で扱う
  - job_name: 'blackbox_http'
    metrics_path: /probe
    # blackbox_exporterのconfig.ymlで定義しているmoduleを指定
    params:
      module: [http_post_2xx]
    static_configs:
      # 監視対象のURLを指定
      - targets:
        - '[target_url]'
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: blackbox_exporter:9115
```

``` yml:alert_roles.yml
# alertの定義
groups:
- name: blackbox_exporter
  rules:
  - alert: http_success
    # 評価するメトリクスと条件
    # probe_successの条件を"blackbox_http"のjobに適用しています
    expr: probe_success{job='blackbox_http'} != 1
    # 評価NGとするまでの継続時間(10秒間alert状態の場合NG)
    for: 10s
    labels:
      severity: critical
    annotations:
      summary: "{{ $labels.instance }}: http request not return 200"
      description: "{{ $labels.instance }} http request not return 200 for more than 10 seconds."
```

# alertmanager

alertmaneagerは通知を行います
今回はslackへの通知を設定しています
slackのwebhookは[公式](https://slack.com/intl/ja-jp/help/articles/115005265063-Slack-%E3%81%A7%E3%81%AE-Incoming-Webhook-%E3%81%AE%E5%88%A9%E7%94%A8)を参照ください

``` yml:config.tml
global:
  # slackのwebhookURLを指定
  slack_api_url: '[slack webhook url]'
  smtp_smarthost: 'localhost:25'
  smtp_require_tls: false
  smtp_from: 'Alertmanager'

route:
  receiver: 'test-route'
  # グループ化の設定(アラート名）
  group_by: '[alertname]'
  # alertグループの通知の送信を最初に待機する時間
  group_wait: 10s
  # alertグループでの最小送信間隔(新しいアラート送信まで待機する時間)
  group_interval: 5m
  # 通知を再度送信するまで待機する時間
  repeat_interval: 1h

receivers:
- name: 'test-route'
  # slackのチャンネル名
  slack_configs:
  - channel: '#general'
  # email(※slackには表示されなかった)
  email_configs:
  - to: "zzz@gmail.com"
```

# dockerコンテナ起動

dockerコンテナを起動します
今回は複数コンテナを一度で上げるのに``docker-compose``を使用します

``` sh:
$ docker-compose build
# "-d"オプションをつけることで、バックグラウンドで実行します
# オプションがないとターミナルを閉じた時にコンテナが停止してしまいます
$ docker-compose up -d

# コンテナの起動を確認
$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                    NAMES
ab4455256cff        prom/prometheus                 "/bin/prometheus --c…"   5 days ago          Up 5 days           0.0.0.0:9090->9090/tcp   prometheus
830bf6475888        prom/alertmanager               "/bin/alertmanager -…"   5 days ago          Up 5 days           0.0.0.0:9093->9093/tcp   alertmanager
b6fcd4f26f57        prom/blackbox-exporter:latest   "/bin/blackbox_expor…"   5 days ago          Up 5 days           9115/tcp                 prometheus_sample_blackbox_exporter_1
```

# Prometheusのダッシュボードを確認

``http://localhost:9090``にアクセスする
実際の運用ではhttps、ポートフォワーディングなどに対応しますが、今回は開発端末なのでそのままにします
``alert_roles.yml``で指定した評価メトリクスを入力し、``execute``をクリックすると評価結果をグラフで表示できます
今回はhttpリクエストが成功(200 OK)を評価していて、1.0であれば正常となります

![スクリーンショット 2020-12-18 20.36.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198158/69276426-7aa7-fec3-c735-0e9eb83aa167.png)

メニューからAlertを選択するとアラート（通知）の状態を確認することができます
状況が色分けされていて、一目でどの評価がエラーとなっているか確認ができます

![スクリーンショット 2020-12-18 20.53.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198158/6bead1d5-1558-c050-06a7-cb301b0e7999.png)

# slack通知を確認する

監視対象のURLを存在しないURLに変更して、httpレスポンスが500系を返却するようにしてAlertの確認をします
httpリクエストが異常を返却するとAlertメニューの状況が赤く変化します

![スクリーンショット 2020-12-18 21.03.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198158/679a2ccb-3060-de80-f725-9392f30d95b1.png)

そして、slackにエラー発生していることが通知されます

![スクリーンショット 2020-12-18 21.10.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198158/d0799860-3637-836c-f0c8-189282aa1082.png)

今回はSlackというチャットサービスでしたが、チャットの通知に反応する習慣がない人などから「Slackに通知すると気づきにくい!もっと早く確実に気づきたい」という要望があれば、SaaSの電話コールサービスと繋げることでAlert発生時に電話をコールすることも可能です
通知することが大事ではなく、人間が認識する方法・可能性を考慮した設計にすることが大事だと思っています

# おまけ

Prometheusの見た目は正直質素・・・だと思います（必要な機能は揃っているので十分な内容です）が、
「デザインがイケてるダッシュボードにしたい！」という方は、[Grafana](https://grafana.com/)と連携させるとかっこよくなると思います
Prometheusと同様にDockerイメージがあるので、docker-compose.ymlに追加することでGrafanaが起動します
興味がある方はぜひチャレンジしてみてはいかがでしょうか

![スクリーンショット 2020-12-18 21.23.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198158/0f283bb3-0d1d-6a27-d738-e7f084a98014.png)
（画像は公式のデモになります）


「監視を構築するのはかなり辛いなぁ・・・」という方は、
簡単に構築して設定をコード管理できるDocker+Prometheusで監視機能を構築してクリスマスを迎えてはいかがでしょうか？