UbuntuにPrometheus+Alertmanager+Grafanaをデプロイして自前k8s環境をモニターしてみた

# はじめに

SynologyのNASは常時稼動しているので、この上のDockerにPrometheus、AlertmanagerにGrafanaを導入しています。K8sクラスター側では、node-exporterやnginx-prometheus-exporterを導入してWebサーバーの状況も監視するようにしています。

今回は別の環境で構築したk8sクラスターをモニターするために、Ubuntu 18.04上のDocker上にPrometheus+Alertmanager+Grafanaを導入したので、その際のメモを残しておきます。

> 【2023/07/06追記】
現在はほとんどのクラスターでkube-prometheusを利用してk8sクラスター上でPrometheus等を稼動させています。この記事を書いてからの現在までの経験からは、k8sクラスターの外でWebサーバー等の稼動状況をモニターしてクラスターダウンだけ監視しておけば、細かいデータの収集はk8sクラスター上でPrometheusを稼動させた方が良いと考えています。

# References

* https://tech-lab.sios.jp/archives/16604
* https://github.com/kubernetes/kube-state-metrics

# 環境

PrometheusをK8sのPodとして稼動させる例も多いですが、Alertmanagerとの連携を考えるとPodにするわけにはいかないかなと思ったので、適当に独立したDocker上に導入しています。最初は冒頭に書いたようにSynology NAS(DS916+)のDockerサービス上に導入しました。

今回は次のような構成のUbuntu 18.04をターゲットにしてみます。

* Docker on Ubuntu 18.04 (HP ProLiant MicroServer Gen8)
  * Prometheus
  * Alertmanager
  * Grafana
* K8sクラスター内のPodとして稼動しているもの
  * node-exporter
  * kube-state-metrics

# Prometheus+Alertmanager+Grafanaの導入 (Dockerコンテナ）

次のようなMakefileを準備しています。192.168.1.5はUbuntuホストのIPアドレスです。

```Makefile:今回使用するMakefileの全体
NAME = prometheus
IMAGE = prom/prometheus:v2.15.0

ALERT_NAME = alertmanager
ALERT_IMAGE = prom/alertmanager:v0.20.0

GRAFANA_NAME = grafana
GRAFANA_IMAGE = grafana/grafana:6.5.2

HOST_IP = 192.168.1.5

.PHONY: run stop start alertmanager-run alertmanager-stop alertmanager-start grafana-run grafana-stop grafana-start

run:
        sudo docker run -it -d --name $(NAME) --restart=always \
                -p $(HOST_IP):9090:9090 \
                -v `pwd`/prometheus.yml:/etc/prometheus/prometheus.yml \
                -v `pwd`/alert_rules.yml:/etc/prometheus/alert_rules.yml \
                -v `pwd`/prometheus.root.prometheus:/prometheus \
                $(IMAGE)

stop:
        sudo docker stop $(NAME)

start:
        sudo docker start $(NAME)

alertmanager-run:
        sudo docker run -it -d --name $(ALERT_NAME) --restart=always \
                -p $(HOST_IP):9093:9093 \
                -v `pwd`/alertmanager.yml:/etc/alertmanager/alertmanager.yml \
                $(ALERT_IMAGE)

alertmanager-stop:
        sudo docker stop $(ALERT_NAME)

alertmanager-start:
        sudo docker start $(ALERT_NAME)

grafana-run:
        sudo docker run -it -d --name $(GRAFANA_NAME) --restart=always \
                -p $(HOST_IP):3000:3000 \
                -v `pwd`/grafana.var.lib.grafana:/var/lib/grafana \
                $(GRAFANA_IMAGE)

grafana-stop:
        sudo docker stop $(GRAFANA_NAME)

grafana-start:
        sudo docker start $(GRAFANA_NAME)
```

カレントディレクトリにある prometheus.root.prometheus/ディレクトリ には UID=65534(nobody)、 grafana.var.lib.grafana/ディレクトリ には UID=472 (grafana) のユーザーで書き込めるよう権限を付与する必要があります。

SynologyではGUIからでは存在しないIDに権限を変更することはできなかったので、とりあえずNobodyに書き込み権限を付与しましたが、UbuntuであればUID、GIDを指定して権限が変更できるので、```$ sudo chown 472:472 grafana.var.lib.grafana```のように変更しています。



## 設定ファイルの内容

各Dockerコンテナに-vオプションで渡している設定ファイルの内容は次のとおりです。マウント先がディレクトリの場合には空ディレクトリを指定しています。

```yaml:prometheus.ymlファイル
global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s
alerting:
  alertmanagers:
  - static_configs:
    - targets: ["192.168.1.5:9093"]
    scheme: http
    timeout: 10s
    api_version: v1
scrape_configs:
- job_name: prometheus
  honor_timestamps: true
  scrape_interval: 15s
  scrape_timeout: 10s
  metrics_path: /metrics
  scheme: http
  static_configs:
  - targets:
    - 192.168.1.5:9090
- job_name: kubernetes
  scheme: http
  metrics_path: /metrics
  static_configs:
   - targets:
       - 192.168.1.51:9100
       - 192.168.1.52:9100
       - 192.168.1.53:9100
       - 192.168.1.54:9100
     labels:
       group: 'k8s'
- job_name: k8s-system-metrics
  scheme: http
  metrics_path: /metrics
  static_configs:
   - targets:
       - 192.168.1.47:8080
     labels:
       group: 'k8s-system'
rule_files:
  - "alert_rules.yml"
```

.scrape_configs.job_nameに指定している/metricsは、自分自身とnode-exporterとkube-state-metricsのサービスを指しています。このサービスの導入に利用したdeployment等のYAMLファイルは次に掲載しています。192.168.1.5[1-4]のIPアドレスは4台のk8sノードを指していて、192.168.1.199:8080はkube-state-metricsのサービスで公開したIP:ポートを指しています。

```yaml:alert_rules.ymlファイルの抜粋
groups:
- name: fs_alert
  rules:
  - alert: FilesystemAlert
    expr: node_filesystem_avail_bytes{mountpoint="/"} < 8589934592
    for: 10m
    labels:
      severity: critical
      type: MiscAlert
    annotations:
      summary: fs_alert
```

alert_rules.ymlには複数の定義が書けるので、実際には複数の定義が記述されています。

```yaml:alertmanager.yml
global:
  resolve_timeout: 5m
  smtp_from: "user01@example.com"
  smtp_smarthost: "example.com:25"

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'web.hook'
  routes:
  - receiver: "email"
    match:
      type: MiscAlert
receivers:
- name: "email"
  email_configs:
    - to: "user01@example.com"
      require_tls: false
- name: 'web.hook'
  webhook_configs:
  - url: 'http://127.0.0.1:5001/'
inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'dev', 'instance']
```

.global.smtp_from, .global.smtp_smarthost, .receivers.email_configs.to の各変数には実際のメールアドレス、SMTPホストを指しています。SMTP-AUTHなしで送信できるようになっているので最低限の設定になっています。メールサーバーの管理は、その組織毎のポリシーで様々なので、適切に設定してください。

## 各プロセスの起動

あとは適切な監視スクリプトを/usr/local/sbinの下に置いて、適宜、このMakefileを呼び出してプロセスを起動しています。

```bash:各プロセスの起動
$ make run
$ make alertmanager-run
$ make grafana-run
```

再起動時に再起動する設定は、--restart=alwaysを追加しました。これによって、docker runの引数にあった*-rm*は削除しています。

```$ make stop```などで停止した後は、runではなく、startを使用してください。通常は自動的に再起動するので明示的に*stopタスクを実行しない限り、使用することはないと思います。

# 監視対象へのnode-exporterの導入

ここからは、K8sクラスターの中から、kubectlコマンドを利用して導入していきます。

あらかじめ ```$ kubectl create ns monitoring``` で、namespaceを作成しています。変更する場合には、YAMLファイル中の、.metadata.namespaceを編集してください。

```YAML:01.daemonset.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  template:
    metadata:
      labels:
        app: node-exporter
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '9100'
        prometheus.io/path: '/metrics'
    spec:
      containers:
      - name: node-exporter
        image: quay.io/prometheus/node-exporter:v0.18.1
        ports:
        - containerPort: 9100
      hostNetwork: true
      hostPID: true
```

ファイルが準備できたら、kubectlコマンドで各ノードでnode-exporterを実行させます。

```bash
$ sudo kubectl apply -f 01.daemonset.yaml
```

# kube-state-metricsの導入

動かしているのは4ノード構成のK8sクラスターなので、kube-state-metricsはK8sクラスター内で1つだけ稼動すれば十分です。規模の大きな環境でShadingを考慮しなければいけない場合は、実験的機能があるのでドキュメントを参照してください。

## 導入に必要なYAMLファイルの準備

まずGithubのプロジェクトをcloneして、この中にあるexamples/standard/ディレクトリの中のファイルを利用します。

```bash
$ git clone https://github.com/kubernetes/kube-state-metrics.git
$ cd kube-state-metrics/examples/standard/
```

このディレクトリの中にある設定で通常は問題ないと思いますが、今回はPrometheusはk8sクラスターの外にあるので、外部のPrometheusからアクセスできるようにサービスを公開しています。


```diff:service.yamlファイルの修正箇所
$ git diff
diff --git a/examples/standard/service.yaml b/examples/standard/service.yaml
index fcd83797..acf96c31 100644
--- a/examples/standard/service.yaml
+++ b/examples/standard/service.yaml
@@ -7,7 +7,8 @@ metadata:
   name: kube-state-metrics
   namespace: kube-system
 spec:
-  clusterIP: None
+  type: LoadBalancer
+  loadBalancerIP: 192.168.1.47
   ports:
   - name: http-metrics
     port: 8080
```

ディレクトリ全体のYAMLファイルを適用する場合には、```-f .```が利用できます。

```bash:kube-state-metrics/examples/standard/ディレクトリ下で実行する様子
$ sudo kubectl apply -f .
```

# Prometheus+Grafanaで観察した印象

各Podの状況も把握することができるのでリトライ回数の増加などを検出することができるようになります。https://github.com/kubernetes/kube-state-metrics/tree/master/docs を確認して、deprecatedな項目は利用せずに、stableな項目を利用するようにしたいところです。

ただ項目の種類も多いですし、Podの数に応じて項目数も増えるので、適切に項目と閾値を指定するには少し実験が必要そうです。

node-exporterは通常のUbuntuサーバーの状態を確認するにも便利ですし、nginx-prometheus-exporterを稼動して、nginx上の/stub_statusを/metricsに変換したりさせて、k8sに限らずいろいろなシステム・サービスがモニターできるようになりました。