Prometheus+Grafana+Alertmanager環境構築手順

# はじめに
* `Prometheus + Grafana + Alertmanager` 環境を構築したときのメモです。
* `OS: ubuntu18.04` で環境構築しました。

# 1. 構築手順
## Prometheus, Grafana, Alertmanagerのインストール
* パッケージインストール準備

```bash
$ apt-get update \
 && apt-get install -y --no-install-recommends \
    gnupg2 \
    curl \
    software-properties-common
$ curl -k https://packages.grafana.com/gpg.key | apt-key add -
$ add-apt-repository "deb https://packages.grafana.com/oss/deb stable main
```

* パッケージインストール

```bash
$ apt-get install -y --no-install-recommends \
    prometheus \
    prometheus-alertmanager \
    grafana
```

* Prometheusディレクトリ作成

```bash
$ mkdir /var/run/prometheus
$ chown -R prometheus /var/run/prometheus
```

## exporterのインストール、設定
※ Prometheusサーバを含む、監視対象ノードに対して

* ノード: node-exporter

```bash
$ apt-get install -y --no-install-recommends prometheus-node-exporter
```

* その他のexporter
    * プロセス: process exporter
    * JVM: jvm exporter
    * 外形: blackbox exporter
    * ログ: mtail, grok exporter
    * GPU: dcgm exporter
    * コンテナ: cadvisor
    * k8s: kube state metrics
    * カスタム
      * cronでPythonスクリプトなどを実行してmetricsファイルを更新し、それをflaskなどで読み込んで公開すれば良い（それをPrometheusがスクレイピングする）

* exporterのプロキシ設定
  * `exporter_proxy`
    * exporterのproxyとして動作するもので、1つのportで複数のexporterとの通信が可能になる

## Prometheus, AlertManagerの設定ファイル修正
* `/etc/prometheus/prometheus.yml`
    * scrape_interval
    * evaluation_interval
    * rule_filesのパス指定
    * scrape対象の指定
      * job_nameラベルを付与（監視対象をグルーピングするラベル。アラートなど後続で利用。）
        * 少なくともポートごとに作ることになる
      * static configs（Ansibleのinventoryファイルの展開も可能）やservice discovery(sd)
        * ec2_sd_configs
          * 対象ポートごとに作成する
          * job_nameでは、任意のプロジェクト名等を含めても良い
          * action: keep で、絞り込みを維持したまま下にいく（徐々に絞り込んでいく）
          * EC2のタグを取得して、target_tagに付け直す（アラートなど後続で利用）

```yml
global:
  scrape_interval:     15s # By default, scrape targets every 15 seconds.
  evaluation_interval: 15s # By default, scrape targets every 15 seconds.
  # scrape_timeout is set to the global default (10s).

  # Attach these labels to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
      monitor: 'example'

# Load and evaluate rules in this file every 'evaluation_interval' seconds.
rule_files:
  - rules.yml
  # - "first.rules"
  # - "second.rules"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s
    scrape_timeout: 5s

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ['localhost:9090']

  - job_name: node
    # If prometheus-node-exporter is installed, grab stats about the local
    # machine by default.
    static_configs:
      - targets: ['localhost:9100']

  - job_name: 'node_ec2'
    ec2_sd_configs:
      - region: ap-northeast-1
        access_key: xxxxxxxxxxxxxxxxxxxx
        secret_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        port: 9100
    relabel_configs:
      #　extract by ec2 tag
      - source_labels: [__meta_ec2_tag_Project]
        regex:  XXX
        action: keep
      - source_labels: [__meta_ec2_tag_Env]
        regex:  stg01
        action: keep
      # reattach the tag from EC2 to Prometheus
      - source_labels: [__meta_ec2_tag_Name]
        target_label: name
      - source_labels: [__meta_ec2_tag_Stage]
        target_label: env
```

* `/etc/prometheus/rules.yml`
  * ラベルとしては、他に `$labels.job` なども利用できる
  * for: 推奨は5分以上（入門Prometheus）

```yml
groups:
  - name: node
    rules:

    - alert: node up == 0
      expr: up{job="node_ec2"} == 0
      for: 5m
      labels:
        severity: error
      annotations:
        summary: "[{{ $labels.env }}][{{ $labels.severity }}]node up == 0. node: {{ $labels.instance }} name: {{ $labels.name }} current value: {{$value}}"
        description: ""
```

* `/etc/prometheus/alertmanager.yml`
  * 下記サンプルに加えて、以下も利用可能
    * `group_by`
    * `match`
    * `group_interval`
    * `group_interval`
    * `repeat_interval`

```yml
route:
  receiver: 'slack_notice'

receivers:
- name: 'slack_notice'
  slack_configs:
    - api_url: XXX
      channel: XXX
      text: "{{ .CommonAnnotations.summary }}"
      send_resolved: true
```

## Prometheus, Grafana, AlertManagerの起動
* 起動オプションの変更
  * 保存期間（デフォルトは15d）
    * `/usr/bin/prometheus --storage.tsdb.retention=1y`
  * 保存先（デフォルトは./data）
    * `-storage.tsdb.path="./data"`
  * WebAPI有効化
    * `--web.enable-admin-api`

* 起動
```bash
$ /usr/sbin/service prometheus restart
$ /usr/sbin/service prometheus-alertmanager restart
$ /usr/sbin/service grafana-server restart
```

# 2. 運用手順
## ブラウザ確認
* `http://localhost:9090/graph` : クエリを書いてメトリクスを確認できる
* `http://localhost:9090/alerts` : アラートを確認できる
* `http://localhost:9090/tsdb-status` : DBへの負荷などが確認できる
* `http://localhost:9090/targets` : exporterごとに、監視ができているか確認できる
* `http://localhost:9090/service-discovery` : サービスディスカバリの状況を確認できる
* `http://localhost:9093` : Alertmanager

## 非監視設定
* アラートマネージャーのWebコンソールの `Silence` 

## バックアップ・リストア
* バックアップ
    * `curl -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot`
        * → `data/snapshots/2020XXXXX-XXXXX` などに作成される
* リストア
    * 上記を一度よけてから、dataに戻す
    * `cp -R data-current/snapshots/2020XXXXX-XXXXX/* data`

## Prometheusサーバ切り替え
* クラスタリングも可能。Alertmanager同士はゴシップでやりとりする。
* Active-Standbyが運用はラク？Prometheusサービスの死活のみやる。

# 参考
* [入門Prometheus](https://www.oreilly.co.jp/books/9784873118772/)
* [Prometheusのec2 service discoveryを試す](http://clavier.hatenablog.com/entry/2017/02/12/103505)
* [1万台のサーバを監視できると話題のPrometheusをGrafanaと組み合わせて導入した話~vol2~](https://blog.nagisa-inc.jp/archives/1557)
* [Prometheus 使ってみたメモ](https://ngyuki.hatenablog.com/entry/2017/11/14/203554)
* [Prometheus 監視をまるっと設定](https://www.techscore.com/blog/2017/12/07/prometheus-monitoring-setting/)
  * アラートのグループ化の定義が分かりやすい
* [CONFIGURATION](https://prometheus.io/docs/alerting/latest/configuration/)