Kubernetesの監視#1  Promethus & Grafana on ROOK Ceph

Kubernetesを利用することのメリットの一つに、監視エージェントなどの導入が不要で、一度、ワーカーノードにモニタリングの機能をデプロイしておけば、名前空間を追加して、アプリケーションを載せても、アプリケーション毎に設定は不要という点がある。もちろん、必要なビューは設定する必要があるが、これまでの労力から比べれば簡単である。

[ROOK Ceph のブロックストレージを検証したメモ](https://qiita.com/MahoTakara/items/a1ed9811c790cd2a0d5f)の記事で、ダイナミック・プロビジョニング可能な永続ストレージが利用できるようになったので、この基盤を利用して、メトリックス監視の時系列データベースのプロメテウス(Promethus)とブラウザ画面であるグラファナ(Grafana)、そして、ログ分析 エラスティックサーチ(Elastiecsearch) とブラウザ画面 キバナ (Kibana)を導入してみたい。そこで、今回はプロメテウスとグラファナについてとりあげる。

この記事は、[ROOK Ceph のブロックストレージを検証したメモ](https://qiita.com/MahoTakara/items/a1ed9811c790cd2a0d5f) でインストールすたROOK Cephの環境を利用して、プロメテウスとグラファナを導入するものである。


## kube-state-metrics とは

kube-state-metricsは、Kubernetes APIサーバーから情報を取得して、APIオブジェクトの状態に関するメトリックを生成するサービスである。このサービスはKubernetesの デプロイメント、ノード、ポッドなど内部のさまざまなオブジェクトの健全性に焦点を当てたものである。

kube-state-metricsは、修正なしにKubernetes APIオブジェクトからメトリックを生成する。これにより、kube-state-metricsによって提供される機能の安定性がKubernetes APIオブジェクト自体と同じグレードになる。しかし、注意点として kubectlは 特定のヒューリスティックを適用して包括的なメッセージを表示する事から、kube-state-metrics と kubectlの表示は一致しないことがある。kube-state-metricsは、Kubernetes APIから加工されない生データを取得して公開する。これにより、ユーザーは必要なすべてのデータを取得し、必要に応じてヒューリスティックと呼ばれる手法によりデータを加工して表示する。

ヒューリスティクス（heuristic）または「発見的手法」とは、必ず正しい答えを導けるわけではないが、ある程度のレベルで正解に近い解を得ることができる。この手法は、答えの精度が保証されない代わりに、回答に至るまでの時間が少ないという特徴がある。

メトリックは、HTTPエンドポイント `/metrics` で、デフォルト8080のリスニングポートで公開される。。それらは平文として提供される。 これらは、Prometheus本体 または Prometheusクライアントのスクレイパーによって、データ収集されるように設計されている。 `/metrics`を ブラウザで開いて、未加工のメトリックを表示することもできる。


## kube-state-metrics のインストール

インストールは、GitHubからコードを取得して、デプロイすることができる。

~~~
git clone https://github.com/kubernetes/kube-state-metrics
cd kube-state-metrics
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=USER-MAIL-ADDRESS
kubectl apply -f examples/standard/
~~~

マスターノードに入って、エントリーポイントをリストして、得られたIPアドレスとポート番号そして`/metrics`でアクセスして、生データを取得することができる。

~~~
vagrant ssh master

kubectl get ep -n kube-system kube-state-metrics 
NAME                      ENDPOINTS                                               AGE
kube-state-metrics        10.244.2.12:8080,10.244.2.12:8081                       4m21s 

curl http://10.244.2.12:8080/metrics
# HELP kube_certificatesigningrequest_labels Kubernetes labels converted to Prometheus labels.
# TYPE kube_certificatesigningrequest_labels gauge
# HELP kube_certificatesigningrequest_created Unix creation timestamp
# TYPE kube_certificatesigningrequest_created gauge
# HELP kube_certificatesigningrequest_condition The number of each certificatesigningrequest condition
# TYPE kube_certificatesigningrequest_condition gauge
# HELP kube_certificatesigningrequest_cert_length Length of the issued cert
# TYPE kube_certificatesigningrequest_cert_length gauge
# HELP kube_configmap_info Information about configmap.
# TYPE kube_configmap_info gauge
kube_configmap_info{namespace="rook-ceph",configmap="rook-ceph-osd-node3-config"} 1
kube_configmap_info{namespace="kube-system",configmap="kube-proxy"} 1
kube_configmap_info{namespace="kube-public",configmap="cluster-info"} 1
kube_configmap_info{namespace="rook-ceph",configmap="local-device-node1"} 1
kube_configmap_info{namespace="rook-ceph",configmap="rook-config-override"} 1
~~~

## kube-state-metrics と metrics-server の違い

メトリック・サーバーは、Heapsterに触発され、コアメトリクスパイプラインの目標を果たすためのKubernetes監視アーキテクチャを実装するものです。これは、すべてのKubernetesノードのkubeletが提供するメトリックを定期的にスクレイピングして公開するクラスタ・レベルのコンポーネントです。これにより集約されたメトリックは、メモリに保存され、メトリックAPI形式で公開される。そして、最新の値のみを保存し、サードパーティの宛先へのメトリックの転送は行わない。

一方、kube-state-metricsは、例えば、デプロイメント、レプリカセットなどに基づくメトリックなど、KubernetesのAPIオブジェクトの状態から、完全に新しいメトリックを生成することに焦点を当てている。Kubernetes状態のスナップショット全体をメモリに保持し、それに基づいて新しいメトリックを継続的に生成する。そして、メトリックサーバーと同様に自発的にメトリックスを転送する責任はない。メトリック情報を必要する者が取りに来るのを待っているサービスである。

Prometheusなどの監視システムから、kube-state-metrics や metrics-server が提供するメトリック情報にアクセスすることもできる。


参考URL: https://github.com/kubernetes/kube-state-metrics


## Promethuse のインストール

ここでは Promethuseをインストールするために、HELM バージョン3 を使用した。kubectl で K8sクラスタへアクセスできる状態にしておき、helm3のクライアントを使って、インストールを実施する。

最初に、Helmチャートのリポジトリを追加する。

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

確認として prometheusのチャートをサーチしてみる。

~~~
helm search repo prometheus
NAME                                 	CHART VERSION	APP VERSION	DESCRIPTION                                       
stable/prometheus                    	10.3.1       	2.15.2     	Prometheus is a monitoring system and time seri...
stable/prometheus-adapter            	2.0.1        	v0.5.0     	A Helm chart for k8s prometheus adapter           
stable/prometheus-blackbox-exporter  	3.0.0        	0.15.1     	Prometheus Blackbox Exporter                      
stable/prometheus-cloudwatch-exporter	0.6.0        	0.6.0      	A Helm chart for prometheus cloudwatch-exporter   
stable/prometheus-consul-exporter    	0.1.4        	0.4.0      	A Helm chart for the Prometheus Consul Exporter   
stable/prometheus-couchdb-exporter   	0.1.1        	1.0        	A Helm chart to export the metrics from couchdb...
stable/prometheus-mongodb-exporter   	2.4.0        	v0.10.0    	A Prometheus exporter for MongoDB metrics         
stable/prometheus-mysql-exporter     	0.5.2        	v0.11.0    	A Helm chart for prometheus mysql exporter with...
stable/prometheus-nats-exporter      	2.3.0        	0.6.0      	A Helm chart for prometheus-nats-exporter         
stable/prometheus-node-exporter      	1.8.1        	0.18.1     	A Helm chart for prometheus node-exporter         
stable/prometheus-operator           	8.5.14       	0.34.0     	Provides easy monitoring definitions for Kubern...
stable/prometheus-postgres-exporter  	1.2.0        	0.8.0      	A Helm chart for prometheus postgres-exporter     
stable/prometheus-pushgateway        	1.2.13       	1.0.1      	A Helm chart for prometheus pushgateway           
stable/prometheus-rabbitmq-exporter  	0.5.5        	v0.29.0    	Rabbitmq metrics exporter for prometheus          
stable/prometheus-redis-exporter     	3.2.1        	1.0.4      	Prometheus exporter for Redis metrics             
stable/prometheus-snmp-exporter      	0.0.4        	0.14.0     	Prometheus SNMP Exporter                          
stable/prometheus-to-sd              	0.3.0        	0.5.2      	Scrape metrics stored in prometheus format and ...
stable/elasticsearch-exporter        	2.2.0        	1.1.0      	Elasticsearch stats exporter for Prometheus       
stable/helm-exporter                 	0.3.2        	0.4.0      	Exports helm release stats to prometheus          
stable/karma                         	1.4.0        	v0.50      	A Helm chart for Karma - an UI for Prometheus A...
stable/stackdriver-exporter          	1.2.2        	0.6.0      	Stackdriver exporter for Prometheus               
stable/weave-cloud                   	0.3.7        	1.4.0      	Weave Cloud is a add-on to Kubernetes which pro...
stable/kube-state-metrics            	2.6.3        	1.9.3      	Install kube-state-metrics to generate and expo...
stable/kuberhealthy                  	1.2.6        	v1.0.2     	The official Helm chart for Kuberhealthy.         
stable/mariadb                       	7.3.6        	10.3.21    	Fast, reliable, scalable, and easy to use open-...
~~~


次に prometheus用の名前空間(namespae)を作成する

~~~
kubectl create namespace prometheus
~~~

Helmチャートからパラメータを取得して編集するために、ディレクトリを作成して移動する。

~~~
mkdir prometheus
cd prometheus/
~~~

パラメータをファイルに取り出し編集する。

~~~
helm inspect values stable/prometheus > prometheus-values.yaml
~~~

編集内容は、ストレージクラスに、ROOK Cephを設定　`storageClass: "rook-ceph-block"`　と `pushgateway` を無効にする。
パラメーターのYAMLの編集が終わったら、プロメテウスをデプロイする。

~~~
helm install prometheus --namespace prometheus -f prometheus-values.yaml stable/prometheus
~~~

以下は実行例で、プロメテウスのUIにアクセスする為の情報が表示される。

~~~
tkr@luigi:~/sandbox-rook/prometheus$ helm install prometheus --namespace prometheus -f prometheus-values.yaml stable/prometheus
NAME: prometheus
LAST DEPLOYED: Sat Jan 25 13:23:37 2020
NAMESPACE: prometheus
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster:
prometheus-server.prometheus.svc.cluster.local


Get the Prometheus server URL by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}")
  kubectl --namespace prometheus port-forward $POD_NAME 9090


The Prometheus alertmanager can be accessed via port 80 on the following DNS name from within your cluster:
prometheus-alertmanager.prometheus.svc.cluster.local


Get the Alertmanager URL by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=alertmanager" -o jsonpath="{.items[0].metadata.name}")
  kubectl --namespace prometheus port-forward $POD_NAME 9093
#################################################################################
######   WARNING: Pod Security Policy has been moved to a global property.  #####
######            use .Values.podSecurityPolicy.enabled with pod-based      #####
######            annotations                                               #####
######            (e.g. .Values.nodeExporter.podSecurityPolicy.annotations) #####
#################################################################################

For more information on running Prometheus, visit:
https://prometheus.io/

~~~


## 起動完了の確認

ポッドの起動状態を確認する。以下のポッドが Running の状態になっていれば成功とみなして良い。

~~~
kubectl get po -n prometheus
NAME                                             READY   STATUS    RESTARTS   AGE
prometheus-alertmanager-84bbc87464-vpcs7         2/2     Running   0          3m27s
prometheus-kube-state-metrics-59bb448977-h5tz4   1/1     Running   0          3m27s
prometheus-node-exporter-7s8qk                   1/1     Running   0          3m27s
prometheus-node-exporter-npnjr                   1/1     Running   0          3m27s
prometheus-node-exporter-qwmpr                   1/1     Running   0          3m27s
prometheus-server-c9977fd86-z7dk7                2/2     Running   0          3m27s
~~~

次にサービスをリストしておく。

~~~
kubectl get svc -n prometheus
NAME                            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
prometheus-alertmanager         ClusterIP   10.32.0.181   <none>        80/TCP          3m33s
prometheus-kube-state-metrics   ClusterIP   None          <none>        80/TCP,81/TCP   3m33s
prometheus-node-exporter        ClusterIP   None          <none>        9100/TCP        3m33s
prometheus-server               ClusterIP   10.32.0.163   <none>        80/TCP          3m33s
~~~


## 公開先の追加

上記のClusterIPでは、外部のブラウザでアクセスできないので、サービスのタイプをNodePortに変更して、マスターノード経由でアクセスできるようにする。
変更するのは、prometheus-alertmanager と prometheus-server である。

~~~
kubectl edit svc -n prometheus prometheus-alertmanager
~~~

変更箇所は、32行目で `type: NodePort` へ変更する。

~~~
     20 spec:
     21   clusterIP: 10.32.0.11
     22   ports:
     23   - name: http
     24     port: 80
     25     protocol: TCP
     26     targetPort: 9093
     27   selector:
     28     app: prometheus
     29     component: alertmanager
     30     release: prometheus
     31   sessionAffinity: None
     32   type: NodePort
~~~


変更が有効になったこと、および、アサインされたポート番号を確認する。

~~~
kubectl get svc -n prometheus prometheus-alertmanager
NAME                            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
prometheus-alertmanager         NodePort    10.32.0.181   <none>        80:30218/TCP    6m12s
~~~

もう一つ、下記も同様に変更する。

~~~
kubectl edit svc -n prometheus prometheus-server 
~~~

変更が有効になったこと、および、アサインされたポート番号を確認する。

~~~
kubectl get svc -n prometheus prometheus-server
NAME                            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
prometheus-server               NodePort    10.32.0.163   <none>        80:31255/TCP    6m58s
~~~

これで、プロメテウスの設定は完了である。


## グラファナ(Grafana)のインストール

次に表示のアプリケーション グラファナも、Helmからインストールする。

~~~
helm search repo grafana
NAME          	CHART VERSION	APP VERSION	DESCRIPTION                                       
stable/grafana	4.4.0        	6.5.2      	The leading tool for querying and visualizing t...
~~~

~~~
helm inspect values stable/grafana > grafana-values.yaml
~~~

修正箇所はストレージクラスで次のように `storageClassName: rook-ceph-block` 修正する。
デプロイ後に表示されるパスワードの取得方法をロスすると、グラファナにログインできなくなるので、注意が必要だ。

~~~
helm install grafana --namespace prometheus -f grafana-values.yaml stable/grafana
NAME: grafana
LAST DEPLOYED: Sat Jan 25 13:37:11 2020
NAMESPACE: prometheus
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:

   kubectl get secret --namespace prometheus grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

2. The Grafana server can be accessed via port 80 on the following DNS name from within your cluster:

   grafana.prometheus.svc.cluster.local

   Get the Grafana URL to visit by running these commands in the same shell:

     export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=grafana,release=grafana" -o jsonpath="{.items[0].metadata.name}")
     kubectl --namespace prometheus port-forward $POD_NAME 3000

3. Login with the password from step 1 and the username: admin
#################################################################################
######   WARNING: Persistence is disabled!!! You will lose your data when   #####
######            the Grafana pod is terminated.                            #####
#################################################################################
~~~


これで二つの Helm インスタンスが実行されていることになる。

~~~
helm list --namespace prometheus
NAME      	NAMESPACE 	REVISION	UPDATED                                	STATUS  	CHART            	APP VERSION
grafana   	prometheus	1       	2020-01-25 13:37:11.709451488 +0900 JST	deployed	grafana-4.4.0    	6.5.2      
prometheus	prometheus	1       	2020-01-25 13:23:37.957212894 +0900 JST	deployed	prometheus-10.3.1	2.15.2     
~~~

グラファナも前述同様に、サービスタイプを編集する。

~~~
kubectl get svc -n prometheus grafana
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
grafana   ClusterIP  10.32.0.110   <none>        80:31300/TCP   2m52s
~~~

変更箇所は、26行と31行である。

~~~
     19 spec:
     20   clusterIP: 10.32.0.110
     21   ports:
     22   - name: service
     23     port: 80
     24     protocol: TCP
     25     targetPort: 3000
     26     nodePort: 31300  <--- 追加
     27   selector:
     28     app: grafana
     29     release: grafana
     30   sessionAffinity: None
     31   type: NodePort    <--- 変更
~~~


これで、ノードのポート番号 31300 でアクセスできるようになった。

~~~
kubectl get svc -n prometheus grafana
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
grafana   NodePort   10.32.0.110   <none>        80:31300/TCP   2m52s
~~~

ログインする為のパスワードは、次のコマンドで取得できる。

~~~
kubectl get secret --namespace prometheus grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
~~~

マスターノードのVMホストのIPアドレス 192.168.1.91 に NodePortを開いているので、http://192.168.1.91:31300/ でグラファナのログイン画面が表示される。
ユーザー admin と上記で得られたパスワードでログインすることができる。

次の初期画面で、Add data sourcesをクリックして、プロメテウスを選択する。

![Grafana1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/287a33a0-52a6-2d8c-9d08-f988c213af97.png)

HTTP URLに、プロメテウスのサービスのDNS名をセットする。これは Helmでインストールした時の応答メッセージに表示されたURLである。

![Grafana3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/305ef5d1-afdf-36b7-4032-8afef1a416d9.png)

テスト結果でグリーンの表示があれば成功なので次へすすむ。画面上左端のグラファナアイコンの右隣のHOMEのアイコンをクリックすると次の画面が表示される。

![Grafana4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/7059aff2-4ad6-58a0-4279-fead665c1238.png)

画面の右下のFind dashboard on Grafana.com をクリックするとダッシュボードのカタログが表示される。そして、Import dashboard でカタログのID番号をインプットして、データソースを選択すると、設定なしで既成のダッシュボードを利用できる。

筆者がお勧めのダッシュボードのIDと名前を以下に挙げておく。ImportからIDを入れるだけでダッシュボード画面が表示されるので大変便利である。

* 11074 1 Node Exporter for Prometheus Dashboard English Version UPDATE 1102
* 10000 Cluster Monitoring for Kubernetes
* 8685  K8s Cluster Summary
* 1860 Node Exporter Full
* 7249 Kubernetes Cluster

次の画面が、ID番号 11074 のダッシュボードである。

![Grafana5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/3bccea7a-0453-2b6a-1dfb-540d1b39ea30.png)

ダッシュボードを作成したり、閾値を設定して、通知するなど多彩な機能があるので、再度、とりあげたい。


## まとめ

オンプレミスの環境でも、K8sクラスタ+ROOKがあれば、大抵の永続ストレージの問題は解決され、快適な実行環境になるということだ。これによって、手軽にプロメテウスやグラファナなど監視ツールもデプロイできるようになる。アプリケーションが監視システムと同じクラスタ上に乗ることは、抵抗感もあるが、名前空間を分けてリソースの割り当てを厳格にする。または、付帯機能とアプリケーションの配置するノードを分けるなど、適切な措置を取れば、問題もないと推定される。