１日でAKS上のNGINX、Redisデモアプリ監視をPrometheus+Grafanaで検証する

はてなにも同じ記事書いてます。
https://kashionki38.hatenablog.com/entry/2020/03/01/003812


k8sの監視する方法を検証して、提案しようとしている。

急いでデモを準備する必要があり、OSSで監視したいって言うならPrometheus一択でしょうということで手元の環境で準備を始めた。

（最終的にはトレーシングもしたいということでIstioの導入に切り替えたのでそれは別記事にします）

 

### デモ環境
https://docs.microsoft.com/ja-jp/azure/aks/kubernetes-walkthrough

そもそもk8s環境を作るのが初めてでどうしようかと思ったが、提案先はAKS-engin on VMということで、AKSで始めることにした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/956344d3-bc05-9fff-9427-669d5f7de6eb.png)

AKSでこんなデモの投票アプリを作成できるということで、これの監視をPrometheusで設定することにした。


諸々デモの手順どおりに進めるとこのようなpodが生成される。

```bash
$ kubectl get po
NAME READY STATUS RESTARTS AGE
azure-vote-back-679f7b955f-pwdpd 3/3 Running 0 2d1h
azure-vote-front-b47b4fbf8-4c8rk 3/3 Running 1 27h
```
azure-vote-frontにはnginxとPythonのflaskが入っていて、azure-vote-backにはRedisが入っている２層構成のようだ。


※ちなみにWindowsからkubectlを実行する際に最初はgit bush使っていたが、Windows10ならWSLでUbuntuをコンソールで実行できるのでそっちがおすすめ。  
watchとかgit bushに入ってないコマンドももちろん使えるし、別記事ではあるけどistioctlの導入も簡単。  



### Prometheusのインストール by helm
helmによるインストールが楽ということでprometheus-operatorというchartsの導入でやってみることにする。
https://github.com/helm/charts/tree/master/stable/prometheus-operator


以下参考にしつつ進めてみる。
https://qiita.com/nmatsui/items/6d8319f3216bd8786eb9

インストール自体はhelm installで終了。

```bash
$ helm install pg-op stable/prometheus-operator
```

何が今入っているのか確認。

```bash
$ kubectl get all
NAME                                                        READY   STATUS    RESTARTS   AGE
pod/alertmanager-pg-op-prometheus-operator-alertmanager-0   2/2     Running   0          10m
pod/azure-vote-back-5966fd4fd4-d87zv                        1/1     Running   0          94m
pod/azure-vote-front-67fc95647d-sgr42                       1/1     Running   0          94m
pod/pg-op-grafana-5b75f465d7-wq9rf                          2/2     Running   0          11m
pod/pg-op-kube-state-metrics-5fc85698d4-pjmzr               1/1     Running   0          11m
pod/pg-op-prometheus-node-exporter-2r6p8                    1/1     Running   0          11m
pod/pg-op-prometheus-node-exporter-cm2x8                    1/1     Running   0          11m
pod/pg-op-prometheus-node-exporter-fjfd2                    1/1     Running   0          11m
pod/pg-op-prometheus-operator-operator-7c7cb98579-xhlgt     2/2     Running   0          11m
pod/prometheus-pg-op-prometheus-operator-prometheus-0       3/3     Running   1          10m

NAME                                             TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                      AGE
service/alertmanager-operated                    ClusterIP      None           <none>         9093/TCP,9094/TCP,9094/UDP   10m
service/azure-vote-back                          ClusterIP      10.0.120.31    <none>         6379/TCP                     94m
service/azure-vote-front                         LoadBalancer   10.0.176.255   51.138.50.33   80:32737/TCP                 94m
service/kubernetes                               ClusterIP      10.0.0.1       <none>         443/TCP                      124m
service/pg-op-grafana                            ClusterIP      10.0.81.187    <none>         80/TCP                       11m
service/pg-op-kube-state-metrics                 ClusterIP      10.0.7.118     <none>         8080/TCP                     11m
service/pg-op-prometheus-node-exporter           ClusterIP      10.0.3.136     <none>         9100/TCP                     11m
service/pg-op-prometheus-operator-alertmanager   ClusterIP      10.0.7.11      <none>         9093/TCP                     11m
service/pg-op-prometheus-operator-operator       ClusterIP      10.0.67.126    <none>         8080/TCP,443/TCP             11m
service/pg-op-prometheus-operator-prometheus     ClusterIP      10.0.67.201    <none>         9090/TCP                     11m
service/prometheus-operated                      ClusterIP      None           <none>         9090/TCP                     10m

NAME                                            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/pg-op-prometheus-node-exporter   3         3         3       3            3           <none>          11m

NAME                                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/azure-vote-back                      1/1     1            1           94m
deployment.apps/azure-vote-front                     1/1     1            1           94m
deployment.apps/pg-op-grafana                        1/1     1            1           11m
deployment.apps/pg-op-kube-state-metrics             1/1     1            1           11m
deployment.apps/pg-op-prometheus-operator-operator   1/1     1            1           11m

NAME                                                            DESIRED   CURRENT   READY   AGE
replicaset.apps/azure-vote-back-5966fd4fd4                      1         1         1       94m
replicaset.apps/azure-vote-front-67fc95647d                     1         1         1       94m
replicaset.apps/pg-op-grafana-5b75f465d7                        1         1         1       11m
replicaset.apps/pg-op-kube-state-metrics-5fc85698d4             1         1         1       11m
replicaset.apps/pg-op-prometheus-operator-operator-7c7cb98579   1         1         1       11m

NAME                                                                   READY   AGE
statefulset.apps/alertmanager-pg-op-prometheus-operator-alertmanager   1/1     10m
statefulset.apps/prometheus-pg-op-prometheus-operator-prometheus       1/1     10m
```
なんかめちゃめちゃ入った。。。namespaceもdefaultでわけれてないけど気にせず進める。

※ちなみにkubectl get allでは本当に全リソースは出力できない。  
以下で全リソース出力可能。   

```bash
kubectl get "$(kubectl api-resources --namespaced=true --verbs=list -o name | tr "\n" "," | sed -e 's/,$//')"  
```
https://github.com/superbrothers/text.superbrothers.dev/blob/master/content/190616-kubectl-get-all-does-not-include-most-resources.md


### Prometheusのtargets確認
PrometheusはPull型アーキテクチャということで、各監視対象に対するexporterからPullしてくる必要がある。  
何をPullできているのかを`targets`にて確認可能。  
今はPrometheusを外部公開していないので、ローカルポートをフォワードしてアクセスする。  

```bash
$ kubectl port-forward $(kubectl get pod -l app=prometheus -o template --template "{{(index .items 0).metadata.name}}") 9090:9090
```
これで、`http://localhost:9090`でアクセス可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/80a96772-0778-557c-084a-8976b4761eaa.png)


ここで監視したい項目がちゃんとあるかを確認する。   
私として充足したいのは以下。
#### nodeのリソースメトリクス
node exporterで取る。これはちゃんとprometheus-operatorが入れば自動的に有効化されているはず。
#### pod/containerのリソースメトリクス
kubelet exporterでcadvisorから取るみたい。Qiitaの記事にあるように、kubelet exporterのexportが導入開始時はうまく行っていない。以下手順でhttps->httpに変更することでkubelet exporterも監視できるようになった。

>kubelet exporterが利用するportをhttpsからhttpに変更
デフォルトでは、Azure AKSではhttpsでのexportがうまくいかないようです。kubeletsの状態をprometheusにexportするポートをhttpsからhttpに変更します。
>$ kubectl get servicemonitors pg-exporter-kubelets --namespace monitoring -o yaml | sed 's/https/http/' | kubectl replace -f -
>https://github.com/coreos/prometheus-operator/issues/926 を参照

#### 各MW(nginx, redis)のメトリクス
もちろんDefaultで入っていないので別途exporterの導入とscrape（PrometheusのPull向き先設定）をする必要があるので、これから書いていく。

### Prometheusの監視メトリクス拡張
#### nginx exporterの導入
https://github.com/nginxinc/nginx-prometheus-exporter
exporterは選択肢が色々あるが、取り急ぎこのexporterを使ってみる。nginxのコネクション数が監視可能。   
他のexporterではアクセスログの監視もmtailでやってたりするのでそっちを今度は使ってみたいが。  
##### 事前準備
>For NGINX, expose the stub_status page at /stub_status on port 8080.  
git hubにあるようにnginxのstub statusを有効化を事前有効化する必要がある。httpでコネクション数が取れるやつ。  
nginx.confを変更する必要がある。  

containerないしはkubernetesでどうやってconfを変更するかは恐らくプロジェクトによってやり方まちまちなのだと思うが、今回はConfigMapを使ってfileをVolumeにマウントする方法にした。

###### nginx.confの準備

```nginx.conf
http {
（中略）
    server {
        location /stub_status {
            stub_status on;
        }
    }
}
```
まずはstub_statusの設定を追加した`nginx.conf`を用意。

###### ConfigMapを使ったnginx.confのcontainerへのマウント
```bash
$ kubectl create configmap nginx-config --from-file nginx.conf
```
configmapを作る。`--from-file`で、ファイル名がそのままkeyになりvalueがngixn.confのファイルの中身になる。

`$ kubectl get configmap nginx-confg -o yaml`

```yaml
data:
  nginx.conf: |-
（中略）
    http {
（中略）
        server {
            listen 8080;
            location /stub_status {
                stub_status on;
                allow 127.0.0.1;
                deny all;
            }
        }
（中略）
```

作った`.spec.template.spec.volumes`でconfigmapをvolumeに追加し、volumeMountsで`/etc/nginx/nginx.conf`にマウントする。

`$ kubectl get deploy azure-vote-front -o yaml`

```yaml
（中略）
spec:
（中略）
  template:
（中略）
    spec:
      containers:
（中略）
        name: azure-vote-front
（中略）
        volumeMounts:
        - mountPath: /etc/nginx/nginx.conf
          name: nginx-config
          subPath: nginx.conf
（中略）
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: nginx.conf
            path: nginx.conf
          name: nginx-config
        name: nginx-config
```
これで完了。
以下で`nginx.conf`がちゃんと反映されてるか確認する。

```bash
$ kubectl exec -it azure-vote-front-67fc95647d-sgr42 //bin/cat //etc/nginx/conf/nginx.conf
```
confimapはデフォルトでは動的に反映されるようになるはずだが、いつまでも反映されなければpod消してデプロイしなおす。

反映されていたら以下でstub statusが設定できているか確認。（curlがないのでwgetしてる。）

```bash
$ kubectl exec -it azure-vote-front-67fc95647d-sgr42 //bin/sh
wget http://127.0.0.1/stub_status -qO -
```

#### nginx exporterをサイドカー配置
nginx exporterはpod内の別コンテナにサイドカーとして配置する。  
引数として`-nginx.scrape-uri http://<nginx>:8080/stub_status`を渡す必要があるっぽいので`.spec.template.spec.containers[1].args`に指定してみる。

`$ kubectl get deploy azure-vote-front -o yaml`

```yaml
（中略）
spec:
（中略）
  template:
（中略）
    spec:
      containers:
（中略）
      - args:
        - -nginx.scrape-uri
        - http://127.0.0.1:8080/stub_status
        env:
        - name: name
          value: nginx-prom-exp
        image: nginx/nginx-prometheus-exporter:0.6.0
        imagePullPolicy: IfNotPresent
        name: nginx-prom-exp
        ports:
        - containerPort: 9113
          protocol: TCP
        resources:
          limits:
            cpu: 250m
            memory: 256Mi
          requests:
            cpu: 100m
            memory: 128Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
```
これでサイドカーも配置完了。
外部podからexporterのポート9113にアクセスできるようにserviceでポートを開ける。
`kubectl get service azure-vote-front -o yaml`

```yaml
（中略）
spec:
（中略）
  ports:
  - name: web
    nodePort: 32737
    port: 80
    protocol: TCP
    targetPort: 80
  - name: nginx-prom-exp
    nodePort: 31217
    port: 9113
    protocol: TCP
    targetPort: 9113
```
ここまででexporterの設定はすべて完了。
以下でprometheusのpodからazure-vote-frontへPullしてnginxメトリクスが取れるか確認。

azure-vote-frontのcluster-ipを確認。

```bash
$ kubectl get service
NAME                                     TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE
azure-vote-front                         LoadBalancer   10.0.176.255       80:32737/TCP,9113:31217/TCP   6d1h
```
確認したcluster-ipに対してwgetしてメトリクスがpullできてるか確認する。

```bash
$ kubectl exec -it prometheus-pg-op-prometheus-operator-prometheus-0 -c prometheus //bin/sh
$ wget http://10.0.176.255:9113/metrics -qO -
```

#### Redis exporterの導入

https://github.com/oliver006/redis_exporter
Redisのexporterはこれを使ってみる。

サイドカー配置。  

`kubectl get deploy azure-vote-back -o yaml`

```yaml
      - image: oliver006/redis_exporter:latest
        imagePullPolicy: Always
        name: redis-exporter
        ports:
        - containerPort: 9121
          protocol: TCP
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
```

ポート開放。  
`kubectl get service azure-vote-back -o yaml`

```yaml
spec:
  clusterIP: 10.0.120.31
  ports:
  - name: redis
    port: 6379
    protocol: TCP
    targetPort: 6379
  - name: redis-exp
    port: 9121
    protocol: TCP
    targetPort: 9121
```
これでexporterは完了。

同様にPrometheusからpullできるか確認。

azure-vote-frontのcluster-ipを確認。

```bash
$ kubectl get service
NAME                                     TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE
azure-vote-back                          ClusterIP      10.0.120.31    <none>          6379/TCP,9121/TCP             6d1h
```

確認したcluster-ipに対してwgetしてメトリクスがpullできてるか確認する。

```bash
$ kubectl exec -it prometheus-pg-op-prometheus-operator-prometheus-0 -c prometheus //bin/sh
/prometheus $ wget http://10.0.120.31:9121/metrics -qO -
```

#### Proetheusのscrape configの設定
exporterを設定できてもまだやることがある。Prometheusはpull型なので、exporterのアドレスをPrometheus自身が把握している必要がある。  
指定の方法は静的と動的（サービスディスカバリ）と２種類あり、サービスディスカバリの設定方法はこちらのサイトがわかりやすかった。  
https://christina04.hatenablog.com/entry/prometheus-service-discovery

今回はとりあえず早く実装したかったので、静的な実装で進めた。  
Prometheusの`scrape_configs`で設定するようだが、prometheus-operatorにおいてどうやって追加するのかわからず詰まった。。  

最終的に以下のページを参照して、`secret`を作成しカスタムリソースのprometheusをのなかで`additionalScrapeConfigs`として読み込むことで実装できた。
https://github.com/coreos/prometheus-operator/blob/master/Documentation/additional-scrape-config.md

secretを作る。

```bash
$ kubectl create secret generic additional-scrape-configs --from-file=prometheus-additional.yaml
```

`prometheus-additional.yaml`

```yaml
- job_name: custome/nginx-exporter/0
  static_configs:
    - targets:
      - 10.0.176.255:9113
- job_name: redis_exporter
  static_configs:
  - targets: 
      - '10.0.120.31:9121'
```

カスタムリソースprometheusの編集。secretを`additionalScrapeConfigs`として参照するようにする。
`kubectl get prometheus pg-op-prometheus-operator-prometheus -o yaml`

```yaml
（中略）
spec:
  additionalScrapeConfigs:
    key: prometheus-additional.yaml
    name: additional-scrape-configs
```

ここまででPrometheusの設定と各exporterの設定は完了しているはず。  
`http://localhost:9090/targets`で`custome/nginx-exporter/0`と`redis_exporter`がいるか確認。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/aa03650b-dc00-793b-1e56-11f10014501e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/986dbf4a-e4aa-0fe9-1eaa-5414bb9a2edf.png)


`http://localhost:9090/graph`でqueryにnginxとかredisとか打ってみて引っかかったら完全にOK。感動。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/c12e3c4f-ee4a-2fca-edb9-5029ae9a8247.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/b3cea50d-002a-75dd-fea6-d59d35f2cd0b.png)


### Grafanaダッシュボードカスタム
Prometheusだけだと可視性や操作性が悪いのでGrafanaで見る。  
prometheus-operatorにGrafanaダッシュボードはデフォルトでいくつか含まれているが、複数podを一緒に見れなかったり、追加したexporterのダッシュボードはなかったりでカスタムが必要。  
とはいえ、自分で高度なものを作るのもめんどくさいので公開されているテンプレートをimportする。めちゃくちゃかっこいいダッシュボードで自分の環境を見れることに感動する。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/ca7ab012-118a-a388-fd27-93345aea615a.png)


##### 使ってみてよかったダッシュボード
- Node
    + https://grafana.com/grafana/dashboards/11074
- Pod
    + https://grafana.com/grafana/dashboards/6336
    + https://grafana.com/grafana/dashboards/10518
- Redis
    + https://grafana.com/grafana/dashboards/763

nginxはコネクションしか取れてなくシンプルなので自分で作った。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248118/67b47d0d-d546-49c0-4ed4-2973f6f1f8d8.png)


ここまでで、AKS上のazure-voteデモアプリのnode、pod/container、nginx、redisのリソースを監視できるようになった。

### できていないこと
- Grafanaの外部公開（やったけど少し力尽きた。かつセキュアな観点が抜けてるので書かない）
- Prometheusデータ永続化（今はPrometheusが死ぬとデータも飛ぶ。）
- REDメトリクス監視（アクセスログでレスポンスタイムとスループットは可視化したかったけどまだできてない。Istio入れてみたらいったんこれでいいじゃんとなった。）