えんじにあメモ

試してみた技術とか、たまに家電ネタ

おうちk8sにPrometheus/Grafanaを導入してNature Remoの可視化をする

前回おうちk8sKubernetes Dashboardを導入しました

sminamot-dev.hatenablog.com

今回はPrometheus/Grafanaの導入に加え、自宅に設置しているNature Remoのセンサーを利用して部屋の室温や湿度などを可視化してみたいと思います

🆙 2020/06/26 更新 master環境にもnode-exporterをデプロイするようvalues.yamlの差分を更新しました

🆙 2020/03/17 更新
データ永続化のためにNFSのPV/PVCを利用するよう修正しました

事前準備

namespace

PrometheusとGrafanaをデプロイするnamespaceは何でも良いですが、今回はすべてmonitoringというnamespaceを作ってそこにデプロイしていきます

$ kubectl create ns monitoring

kubensなどを使って作成したnamespaceに切り替えておいてください

Helm

PrometheusとGrafanaについてはHelmを使って入れてみます

まずはHelmのインストールから(Macでの動作を想定)

$ brew install helm

# 確認
$ helm version
version.BuildInfo{Version:"v3.1.0", GitCommit:"b29d20baf09943e134c2fa5e1e1cab3bf93315fa", GitTreeState:"clean", GoVersion:"go1.13.8"}

Prometheus/Grafanaのインストールのために公式のチャートリポジトリを追加します

$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/

追加されたことを確認しておきます

$ helm search repo stable
NAME                                    CHART VERSION   APP VERSION             DESCRIPTION
stable/acs-engine-autoscaler            2.2.2           2.1.1                   DEPRECATED Scales worker nodes within agent pools
stable/aerospike                        0.3.2           v4.5.0.5                A Helm chart for Aerospike in Kubernetes
stable/airflow                          6.1.1           1.10.4                  Airflow is a platform to programmatically autho...
stable/ambassador                       5.3.1           0.86.1                  A Helm chart for Datawire Ambassador
# ...

あとは対象のチャート名を指定してhelm installでインストール可能ですが、設定ファイルを修正して利用したいので、デフォルトの設定ファイルを取得します

# prometheus
$ helm show values stable/prometheus > prometheus-values.yaml

# grafana
$ helm show values stable/grafana > grafana-values.yaml

それぞれのyamlをおうちk8s用に修正していきます

Prometheus

Prometheusについては修正箇所がいくつかあります

kube-state-metrics

デフォルトの設定だと、kube-state-metricsのイメージにarmベースのものが用意されていないため、ラズパイ上で動かすことができません

そこで以前書いた、Github Actionsでの複数アーキテクチャ向けのイメージ作成の方法を使ってラズパイ(arm)向けのイメージを作成しました

sminamot-dev.hatenablog.com

上記の方法で作ってもらってもOKですが、現時点で最新版(v1.9.5)イメージを作ってあるのでそれを使ってもらっても構いません
作ったイメージは コチラ

PV/PVC

helmでPrometheusをインストールする際に、prometheus-serverとprometheus-alertmanagerにはPersistentVolume/PersistentVolumeClaimが必要になります

マネージドサービスでは動的に作ってくれる設定などもありますが、もちろんラズパイ上に立てたk8sにはそんな機能はありません

PV/PVCを使わずemptyDirを利用するように変更もできますが、Podが再起動した際にデータが消えてしまうので、事前にPV/PVCを作っておきましょう

PersistentVolumeはNFSを利用しますがmasterをNFSサーバとして利用する記事は以下で書いてるので、NFSサーバ/クライアントの設定を事前に行ってください

sminamot-dev.hatenablog.com

おうちk8sのmasterサーバにログインし、PVに利用するディレクトリを作っておきます

# masterサーバ上で
$ sudo mkdir -p /mnt/share/nfs/prometheus/{alertmanager,server}

# 各プロセスをNonRootで動かすため作成したディレクトリの所有権も変えておく
$ sudo chown $USER /mnt/share/nfs/prometheus

準備ができたのでPV/PVCを作ります(今回はそれぞれ1GiBを利用する設定)

nfs-pv.yaml

kind: PersistentVolume
apiVersion: v1
metadata:
  name: prometheus-alertmanager
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: alertmanager
  nfs:
    server: raspberrypi-master.local
    path: /mnt/share/nfs/prometheus/alertmanager
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: prometheus-server
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: prometheus-server
  nfs:
    server: raspberrypi-master.local
    path: /mnt/share/nfs/prometheus/server

nfs-pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: prometheus-alertmanager
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: alertmanager
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: prometheus-server
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: prometheus-server

デプロイします🚀

$ kubectl apply -f nfs-pv.yaml -f nfs-pvc.yaml -n monitoring

values.yaml

今までの変更点をprometheus-values.yamlにも反映させます

変更差分は以下の通り

@@ -180,7 +180,7 @@
     ## alertmanager data Persistent Volume existing claim name
     ## Requires alertmanager.persistentVolume.enabled: true
     ## If defined, PVC must be created manually before volume will be bound
-    existingClaim: ""
+    existingClaim: "prometheus-alertmanager"

     ## alertmanager data Persistent Volume mount root path
     ##
@@ -188,7 +188,7 @@

     ## alertmanager data Persistent Volume size
     ##
-    size: 2Gi
+    size: 1Gi

     ## alertmanager data Persistent Volume Storage Class
     ## If defined, storageClassName: <storageClass>
@@ -276,10 +276,10 @@
   ## Security context to be added to alertmanager pods
   ##
   securityContext:
-    runAsUser: 65534
+    runAsUser: 1001
     runAsNonRoot: true
-    runAsGroup: 65534
-    fsGroup: 65534
+    runAsGroup: 1001
+    fsGroup: 1001

   service:
     annotations: {}
@@ -396,7 +396,7 @@
   ## kube-state-metrics container image
   ##
   image:
-    repository: quay.io/coreos/kube-state-metrics
+    repository: sminamot/kube-state-metrics
     tag: v1.9.5
     pullPolicy: IfNotPresent

@@ -511,7 +511,7 @@
   ##
   image:
     repository: prom/node-exporter
-    tag: v0.18.1
+    tag: v1.0.0-rc.0
     pullPolicy: IfNotPresent

   ## Specify if a Pod Security Policy for node-exporter must be created
@@ -559,7 +559,8 @@
   ## Node tolerations for node-exporter scheduling to nodes with taints
   ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
   ##
-  tolerations: []
+  tolerations:
+    - operator: "Exists"
     # - key: "key"
     #   operator: "Equal|Exists"
     #   value: "value"
@@ -836,7 +837,7 @@
     ## Prometheus server data Persistent Volume existing claim name
     ## Requires server.persistentVolume.enabled: true
     ## If defined, PVC must be created manually before volume will be bound
-    existingClaim: ""
+    existingClaim: "prometheus-server"

     ## Prometheus server data Persistent Volume mount root path
     ##
@@ -844,7 +845,7 @@

     ## Prometheus server data Persistent Volume size
     ##
-    size: 8Gi
+    size: 1Gi

     ## Prometheus server data Persistent Volume Storage Class
     ## If defined, storageClassName: <storageClass>
@@ -953,10 +954,10 @@
   ## Security context to be added to server pods
   ##
   securityContext:
-    runAsUser: 65534
+    runAsUser: 1001
     runAsNonRoot: true
-    runAsGroup: 65534
-    fsGroup: 65534
+    runAsGroup: 1001
+    fsGroup: 1001

   service:
     annotations: {}
@@ -972,7 +973,7 @@
     loadBalancerSourceRanges: []
     servicePort: 80
     sessionAffinity: None
-    type: ClusterIP
+    type: NodePort

     ## Enable gRPC port on service to allow auto discovery with thanos-querier
     gRPC:
  • runAsUser/runAsGroup/fsGroup についてはnodeサーバの所有権を変えたユーザのuid/gidを設定します
    • 自分の環境でのアカウントは1001でした
cat /etc/passwd | grep $USER
sminamot:x:1001:1001::/home/sminamot:/bin/bash
  • node-exporterのイメージはラズパイのCPU温度などを取得できるよう、latestでもあるv0.18.1ではなく、v1.0.0-rc.0を使ってます

  • master nodeの環境の情報も取得できるようnode-exporterをmasterにも入れます

    • nodeExporter.tolerationsの設定を変えてdaemonsetがmasterにも配置されるようにしています

Grafana

GrafanaについてもPV/PVCを作成します

Prometheus同様、おうちk8sのmasterサーバにログインし、PVに利用するディレクトリを作っておきます

# masterサーバ上で
$ sudo mkdir -p /mnt/share/nfs/grafana

PV/PVCを作ります nfs-pv.yaml

kind: PersistentVolume
apiVersion: v1
metadata:
  name: grafana
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: grafana
  nfs:
    server: raspberrypi-master.local
    path: /mnt/share/nfs/grafana

nfs-pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: grafana
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: grafana

デプロイします🚀

$ kubectl apply -f nfs-pv.yaml -f nfs-pvc.yaml -n monitoring

grafana-values.yamlを修正します

@@ -111,7 +111,7 @@
 ## ref: http://kubernetes.io/docs/user-guide/services/
 ##
 service:
-  type: ClusterIP
+  type: NodePort
   port: 80
   targetPort: 3000
     # targetPort: 4181 To be used with a proxy extraContainer
@@ -186,16 +186,16 @@
 ##
 persistence:
   type: pvc
-  enabled: false
+  enabled: true
   # storageClassName: default
   accessModes:
     - ReadWriteOnce
-  size: 10Gi
+  size: 1Gi
   # annotations: {}
   finalizers:
     - kubernetes.io/pvc-protection
   # subPath: ""
-  # existingClaim:
+  existingClaim: "grafana"

 initChownData:
   ## If false, data ownership will not be reset at startup

デプロイ

ここまででPrometheus/Grafanaのデプロイ準備が完了したのでデプロイしちゃいましょう🚀

$ helm install prometheus stable/prometheus -f prometheus-values.yaml --namespace monitoring
$ helm install grafana stable/grafana -f grafana-values.yaml --namespace monitoring
# 更新時は helm upgrade

各種リソースが正常にデプロイされていればOKです

$ kubectl get all -n monitoring

それぞれWebUIでも表示できるか見てみましょう

# Prometheus
$ open http://$(kubectl get nodes --namespace monitoring -o jsonpath="{.items[0].status.addresses[0].address}"):$(kubectl get --namespace monitoring -o jsonpath="{.spec.ports[0].nodePort}" services prometheus-server)

# Grafana
$ open http://$(kubectl get nodes --namespace monitoring -o jsonpath="{.items[0].status.addresses[0].address}"):$(kubectl get --namespace monitoring -o jsonpath="{.spec.ports[0].nodePort}" services grafana)

f:id:shosfs:20200308222908p:plain

f:id:shosfs:20200308222948p:plain

Grafanaにはログインが必要で、usernameはadmin、passwordは以下コマンドで確認可能です

$ kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 -D

ログインできればOK f:id:shosfs:20200308223123p:plain

🎉🎉🎉🎉🎉

Grafanaのカスタマイズ

せっかくPrometheus/Grafanaのインストールができたので、Grafanaでなにかダッシュボードを作ってみます

ブラウザでGrafanaを開きログインしたら、Create a data sourceを選択します(画像はすでに追加したあとなので、初回はもしかしたらAdd data sourceかも) f:id:shosfs:20200308224312p:plain

"Prometheus" を選択 f:id:shosfs:20200308224642p:plain

Settings -> HTTP -> URLにhttp://prometheus-serverと入力 f:id:shosfs:20200308225358p:plain

Dashboardsの3つとも"Import"する f:id:shosfs:20200308230101p:plain

Settings から「Save & Test」を押し、正常に設定ができればOK

Dashboards -> Manageから取り込んだ3つのDashboardsが取り込まれればOK f:id:shosfs:20200308230922p:plain

試しに「Prometheus 2.0 Stats」など見てみるとそれっぽいものが表示されるはずです 👍 f:id:shosfs:20200308231401p:plain

その他のダッシュボードは Grafana Labs にいろんなダッシュボードが共有されてて、IDを入力するだけでダッシュボードの作成が可能です

ちなみに自分は このダッシュボード を入れてみました f:id:shosfs:20200308233943p:plain

Nature Remoの可視化

やっと本題のNature Remoの可視化です(遅ぇ)

Nature Remo API用のアクセストークン取得

Nature Remo Cloud API のリクエストにはアクセストークンが必要になります

ここのページ からアクセストークンの生成ができるので、値を控えておきます

exporterのk8sへのデプロイ

PrometheusとGrafanaはmonitoringネームスペースにデプロイしてましたが、Nature Remo用のexporterはremoネームスペースを作ってそこにデプロイしたいと思います

$ kubectl create ns remo

Nature Remo用のexporterを作っている方がいたので使わせてもらいます

github.com

↑のリポジトリにはPrometheus/Grafanaのyamlも用意されていますが、先ほど作って不要なため、Nature Remoのexporterのyamlだけ拝借します

使うのはremo-exporter-dep.yamlremo-exporter-svc.yamlですが、apiVersionの書き方が古かったり、NodePortになってたり(クラスタ内でリクエストできれば良いのでClusterIPでよい)するので、修正を加えたものが以下になります

  • deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: remo-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: remo-exporter
  template:
    metadata:
      labels:
        app: remo-exporter
    spec:
      containers:
        - name: remo-exporter
          image: kenfdev/remo-exporter:latest
          ports:
            - containerPort: 9352
              protocol: TCP
          env:
            - name: OAUTH_TOKEN_FILE
              value: '/etc/secrets/api-keys'
          volumeMounts:
            - name: api-keys-volume
              readOnly: true
              mountPath: '/etc/secrets'
      volumes:
        - name: api-keys-volume
          secret:
            secretName: api-keys
  • service.yaml
apiVersion: v1
kind: Service
metadata:
  name: remo-exporter
  labels:
    app: remo-exporter
spec:
  type: ClusterIP
  ports:
    - port: 9352
      protocol: TCP
      targetPort: 9352
  selector:
    app: remo-exporter

先ほど取得したアクセストークンはsecret/api-keysに記載しておきます

前回のKubernetes Dashboard と同様、kustomizeを使ってデプロイしましょう

kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: remo

resources:
  - deployment.yaml
  - service.yaml

secretGenerator:
  - name: api-keys
    files:
      - secret/api-keys

いざデプロイ🚀

$ kubectl apply -k .

デプロイができたらexporterから値が取得できるか確認しておきます

$ kubectl run -it --rm alpine --image alpine --restart=Never
/ # apk update && apk add curl
/ # curl http://remo-exporter.remo:9352/metrics
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 8.3553e-05
go_gc_duration_seconds{quantile="0.25"} 0.000161035
# ...

正しく取得できてそうです 🎉

Grafanaの設定

exporterもデプロイできたのであとはGrafanaの設定をするだけです

Grafanaの設定はmonitoringネームスペースなので戻しておきましょう

独自のscrapeを追加するためにextraScrapeConfigsの設定を加えます

grafana-values.yamlextraScrapeConfigsに追記してもいいですが、今回は別ファイルで定義して利用してみます

extraScrapeConfigs.yaml

- job_name: 'nature-remo'
  dns_sd_configs:
    - names: ['remo-exporter.remo']
      port: 9352
      type: A
      refresh_interval: 5s

デプロイします🚀

$ helm upgrade prometheus stable/prometheus -f grafana-values.yaml --set-file extraScrapeConfigs=extraScrapeConfigs.yaml  --namespace monitoring

あとはGrafanaのUIからポチポチグラフを追加していくと… f:id:shosfs:20200309232411p:plain

🎉🎉🎉🎉🎉

ダッシュボードのyamlは以下に置いておくので参考にどうぞ ✋

https://github.com/sminamot/raspi-k8s-monitoring/blob/master/grafana/remo/dashboard/grafana.json

まとめ

おうちk8sにPrometheus/Grafanaの導入と、GrafanaへNature Remoを可視化するダッシュボードを追加しました

最近Helmを少し触ってますが、公式のチャート使うと簡単にデプロイできるのでいいですね

あとHelmが2から3にアップデートされてから構成がシンプルになって扱いやすくなりました(まだ2の情報が多いので公式ドキュメント読むのが良さそう、2と3で公式のドキュメントがちゃんと別れてるのが良い)

今回作ったもの

以下に置いておきます

github.com

参考