ラズパイk8s用の監視システム(Node Exporter + Prometheus + InfluxDB + Grafana)

[Raspberry Pi 4でKubernetesクラスターを構築](https://qiita.com/reireias/items/0d87de18f43f27a8ed9b)したわけですが、こういったシステムがあると、**イケてるダッシュボードを作って監視したくなる**修正をもつのがエンジニアという生き物です。

Kubernetesの監視といえば、[Prometheus](https://prometheus.io/) + [Grafana](https://grafana.com/)が鉄板ですが、それだけでは面白くないので、独自のコンセプトの元、監視システムを構築してみました。

# システム構成
![monitoring.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214526/e2eab490-bdd8-9497-bec1-be7c758fef07.png)

# ダッシュボード
![Screenshot from 2020-02-16 23-45-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214526/a49d5a26-8916-3d86-947e-50c9b5457323.png)

# コンセプト
- ダッシュボードはスマホからも見れるようにインターネットに公開する
- 自宅のLANはインターネットに公開しない
- CPU温度をモニタリングする
- 運用コストはなるべく安価にすます


## ネットワーク面
自宅のネットワークを公開すると、セキュリティ面で考慮すべき事柄が多くなりため、極力避ける方針です。
普通に構築してしまうと、Prometheusはpull型のモニタリングシステムであることから、 `Grafana → Prometheus → 監視対象` となり、どこかでインターネットからLAN内への通信が発生してしまいます。

そこで、今回はPrometheusの[remote storage機能](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage)を利用し、remote storageにクラウド上に構築した[InfluxDB](https://www.influxdata.com/)を指定することで、`Grafana → Influxdb ← Prometheus` というように、インターネットからLANへのアクセスが発生しないように構築しました。

## インフラ面
ダッシュボードとInfluxDBはクラウド上に構築する必要があるのですが、なるべく安価にすませつつ、独自ドメインで運用したかったため、**禁断のGCP永久無料枠のインスタンス**を採用することとしました。
（なぜ禁断かというと、アカウントで1インスタンスしか無料で利用できないので、他で使いたくなると困るため）

ドメインはエンジニアなら誰しもがもっている`.dev`ドメインを使い、[Let's Encrypt](https://letsencrypt.org/ja/)の証明書を利用しています。

Prometheus自体は監視対象にアクセスできる場所に構築する必要があるため、普通のご家庭なら何台か余っているRaspberry Pi 3を使いLAN内に構築しました。

# 詳細

## kube-state-metrics
Kubernetesの情報をPrometheusで取得できる形式で出力してくれる、[kube-state-metrics](https://github.com/kubernetes/kube-state-metrics)をKubernetesクラスター上に構築します。
基本はREADME通りに`kubectl apply`すれば構築ができるのですが、イメージはarmに対応しているものに変更する必要があります。（Raspberry PiのCPUがarm 32bit v7のため）

やり方は様々あると思いますが、今回は本家をforkし、GitHub Actionsで自分のDocker Hub上にarm用イメージをpushする方式としました。
<a href="https://github.com/reireias/kube-state-metrics"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/reireias/kube-state-metrics.png" width="460px"></a>
[reireias/kube-state-metrics上のブランチ](https://github.com/reireias/kube-state-metrics/commit/16e15da194d035c5cc47f812a82c6bbc3155f7c3)

arm用のイメージをGitHub Actions(CPUはx86_64)上でビルドするには少しコツがいるので、ご紹介します。
[multiarch/qemu-user-static](https://github.com/multiarch/qemu-user-static)を利用し、CPU情報をエミュレートすることで、これが実現できます。
workflowの設定は下記のようになります。

```yaml:.github/workflows/main.yml
name: main

on: [push, pull_request]

jobs:
  main:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: setup docker for arm
        run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
      - name: push image
        run: |
          docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
          make quay-push
        env:
          GOARCH: arm
          GOARM: 7
```

## Node exporter
Kubernetesの各NodeのCPUやメモリを取得するために[Node exporter](https://github.com/prometheus/node_exporter)を各Nodeに設置します。
arm用のバイナリは[Releases](https://github.com/prometheus/node_exporter/releases)にあるのですが、CPUの温度を取得できるようにするため最新のコードから自分でビルドしています。
（※つい先日`1.0.0-rc0`のビルドが追加されていたので、そちらでも大丈夫そうです）

```console
# clone
$ git clone https://github.com/prometheus/node_exporter.git

# build for arm
$ GOOS=linux GOARCH=arm GOARM=7 go build
```

各ノードへの配置は下記リポジトリ内の[node-exporter](https://github.com/reireias/raspberrypi-ansible/tree/master/roles/node-exporter)というroleにまとめています。
<a href="https://github.com/reireias/raspberrypi-ansible"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/reireias/raspberrypi-ansible.png" width="460px"></a>

## Prometheus
Prometheusはarm用のバイナリがGitHubのReleasesから配布されているので、それをそのまま利用します。
こちらもAnsibleのroleとして作成しています。
[reireias/raspberrypi-ansible - roles/prometheus](https://github.com/reireias/raspberrypi-ansible/tree/master/roles/prometheus)

Prometheus用の設定ファイルは下記のように設定しています。

- node exporter用とkubernetes用のjobを設定
- node exporterはlabelの関係で3台それぞれを定義
- remote strage設定にInfluxDBのエンドポイントを指定

```yaml:roles/prometheus/templates/prometheus.yml
global:
  scrape_interval:     60s # By default, scrape targets every 15 seconds.
  evaluation_interval: 60s # By default, scrape targets every 15 seconds.
  external_labels:
      monitor: 'pikube'

rule_files:
  # - "first.rules"
  # - "second.rules"
scrape_configs:
  - job_name: 'prometheus'

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

  - job_name: node
    static_configs:
      # node exporterその1
      - targets:
        - 192.168.99.100:9100
        labels:
          node_name: pikube01
      # node exporterその2
      - targets:
        - 192.168.99.101:9100
        labels:
          node_name: pikube02
      # node exporterその3
      - targets:
        - 192.168.99.102:9100
        labels:
          node_name: pikube03
  # kube-state-metrics
  - job_name: kubernetes
    static_configs:
      - targets:
        - 192.168.99.200:8080
# remote storageの設定
remote_write:
  - url: 'https://{{ influxdb_domain }}/api/v1/prom/write?db=prometheus&u={{ influxdb_user }}&p={{ influxdb_pass }}'
remote_read:
  - url: 'https://{{ influxdb_domain }}/api/v1/prom/read?db=prometheus&u={{ influxdb_user }}&p={{ influxdb_pass }}'
```

## InfluxDB + Grafana on GCE
GCP上に永久無料枠で構築したGCEインスタンスに、InfluxDBとGrafanaを構築しています。
InfluxDBは一言で説明するなら、時系列データに特化したDBです。

また、当然アクセスはSSLで行うので、Nginxを配置し、Let's Encryptによる証明書を取得し、設置しています。
Let's Encryptの証明書は3ヶ月と期間が短いので、自動更新もcronで設定しています。

GrafanaはGUIで設定しています。
データソースにInfluxDBを指定可能なので、これを指定してダッシュボードを作成しています。
InfluxDBは時系列データに特化したDBですが、SQL文はわりと普通に書けるので、そんなに詰まる部分はありませんでした。

それらをAnsibleで構築したものがこちらです。
<a href="https://github.com/reireias/pikube-monitoring-ansible"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/reireias/pikube-monitoring-ansible.png" width="460px"></a>

GCPの永久無料枠でのGCEインスタンス構築は、下記記事を参考にさせていただきました。
[GCPで永久無料枠を利用してサービスを立ち上げたときにしたことの備忘録](https://qiita.com/riku-shiru/items/a870edd9dc0b132e092c)

# まとめ
イケてるダッシュボードが作れた！
これで外出時でも自宅のKubernetesクラスターの監視ができるようになった。（するとは言っていない）