TKG2.1にPrometheusとGrafanaをインストールする

TKG2.1でPrometheusをデプロイした時のメモ。ついでにkubeletに関するメトリクスが取れないバグのWorkaroundも検証した。

'23/4/18訂正：
`/api/v1/nodes/$1/proxy/metrics/cadvisor`を残すよう修正。

## 前提
- TKG2.1以降であること
- cert-managerとContourが導入済みであること

cert-managerとContourの導入方法は必要であれば[こちら](https://qiita.com/ipppppei/items/4eb8278a14d174949300)参照。

## Prometheusのインストール
Prometheusのバージョンを取得
```sh
tanzu package available list prometheus.tanzu.vmware.com -A
```
設定ファイルを抽出。
```sh
export PROMETHEUS_VER=2.37.0+vmware.1-tkg.1
tanzu package available get prometheus.tanzu.vmware.com/$PROMETHEUS_VER --default-values-file-output prometheus-default-values.yaml
```
コメントを削除。
```sh
sed -i "s/^# //g" prometheus-default-values.yaml
```
最初、全て残した状態でデプロイしてみたが、どうもAlert Managerの設定に問題があるようでデプロイに失敗したため、必要な設定だけ作ってインストールすることにする。作ったファイルは以下。
```yaml
$ cat prometheus-default-values.yaml
---
ingress:
  enabled: true
  virtual_host_fqdn: prometheus.10-151-199-248.sslip.io
```
FQDNのところはEnvoyのIPにsslip.ioから飛ばすようにしている。
この設定でインストールする。
```sh
export NAMESPACE=my-packages
tanzu package install prometheus --package prometheus.tanzu.vmware.com --version $PROMETHEUS_VER --values-file prometheus-default-values.yaml --namespace $NAMESPACE
```
`ingress.enabled`を`true`にしたが、実際は`httpProxy`リソースとしてデプロイされる。
```sh
$ kubectl get httpproxy -n tanzu-system-monitoring
NAME                   FQDN                                 TLS SECRET       STATUS   STATUS DESCRIPTION
prometheus-httpproxy   prometheus.10-151-199-248.sslip.io   prometheus-tls   valid    Valid HTTPProxy
```

なお、デフォルト値でインストールすると、kubeletに関するメトリクスの取得に失敗する。
参考：https://github.com/google/cadvisor/issues/2785
今回はそこを修正してみる。デフォルト値でデプロイ後、Prometheusにアクセスし、`Status`->`Configuration`から設定ファイルを取得し、`kubernetes-nodes-cadvisor`というジョブの定義をコピーする。
コピー後、replacementのパスを`/api/v1/nodes/$1/proxy/metrics`に変更してTanzu Packageの`prometheus_yml`に設定する。
設定したファイルは以下となる。
```yaml
$ cat prometheus-default-values.yaml
---
ingress:
  enabled: true
  virtual_host_fqdn: prometheus.10-151-199-248.sslip.io
prometheus:
  config:
    prometheus_yml: |
      scrape_configs:
      - job_name: kubernetes-nodes-cadvisor
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: https
        authorization:
          type: Bearer
          credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          insecure_skip_verify: true
        follow_redirects: true
        enable_http2: true
        relabel_configs:
        - separator: ;
          regex: __meta_kubernetes_node_label_(.+)
          replacement: $1
          action: labelmap
        - separator: ;
          regex: (.*)
          target_label: __address__
          replacement: kubernetes.default.svc:443
          action: replace
        - source_labels: [__meta_kubernetes_node_name]
          separator: ;
          regex: (.+)
          target_label: __metrics_path__
          replacement: /api/v1/nodes/$1/proxy/metrics
          action: replace
        kubernetes_sd_configs:
        - role: node
          kubeconfig_file: ""
          follow_redirects: true
          enable_http2: true
```
設定後、再デプロイする。ConfigMapだけの更新でPodは再作成されず、ConfigMapの再読み込みのためにPodを手動で再起動する必要がある点に注意。
```sh
tanzu package installed update prometheus -n $NAMESPACE --values-file prometheus-default-values.yaml
kubectl delete pod -n tanzu-system-monitoring -l component=server
```
デプロイ後、kubeletで検索すると候補が保管されるようになる。
![1681725767711.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/43ef38b5-6ac7-1ff0-c91a-f0f42d972623.png)

ただし、デフォルトで存在していた`scrape_configs`も消えているので、デフォルト値に上書きしたい場合は`prometheus_yml`をすべて転記した上でreplacementの値を書き換えるとよい。また、元々あったジョブでは`container_xxxx`といったメトリクスを取っていため、別名で保存した方がよい。
デフォルト値を修正した設定ファイルも参考までに以下に置いておく。
<details><summary>prometheus-default-values.yaml</summary><div>

```yaml
---
ingress:
  enabled: true
  virtual_host_fqdn: prometheus.10-151-199-248.sslip.io
prometheus:
  config:
    prometheus_yml: |
      global:
        scrape_interval: 1m
        scrape_timeout: 10s
        evaluation_interval: 1m
      alerting:
        alertmanagers:
        - follow_redirects: true
          enable_http2: true
          scheme: http
          timeout: 10s
          api_version: v2
          static_configs:
          - targets:
            - alertmanager.tanzu-system-monitoring.svc:80
        - follow_redirects: true
          enable_http2: true
          scheme: http
          timeout: 10s
          api_version: v2
          relabel_configs:
          - source_labels: [__meta_kubernetes_namespace]
            separator: ;
            regex: default
            replacement: $1
            action: keep
          - source_labels: [__meta_kubernetes_pod_label_app]
            separator: ;
            regex: prometheus
            replacement: $1
            action: keep
          - source_labels: [__meta_kubernetes_pod_label_component]
            separator: ;
            regex: alertmanager
            replacement: $1
            action: keep
          - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_probe]
            separator: ;
            regex: .*
            replacement: $1
            action: keep
          - source_labels: [__meta_kubernetes_pod_container_port_number]
            separator: ;
            regex: null
            replacement: $1
            action: drop
          kubernetes_sd_configs:
          - role: pod
            kubeconfig_file: ""
            follow_redirects: true
            enable_http2: true
      rule_files:
      - /etc/config/alerting_rules.yml
      - /etc/config/recording_rules.yml
      - /etc/config/alerts
      - /etc/config/rules
      scrape_configs:
      - job_name: prometheus
        honor_timestamps: true
        scrape_interval: 5s
        scrape_timeout: 5s
        metrics_path: /metrics
        scheme: http
        follow_redirects: true
        enable_http2: true
        static_configs:
        - targets:
          - localhost:9090
      - job_name: kube-state-metrics
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: http
        follow_redirects: true
        enable_http2: true
        static_configs:
        - targets:
          - prometheus-kube-state-metrics.tanzu-system-monitoring.svc.cluster.local:8080
      - job_name: node-exporter
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: http
        follow_redirects: true
        enable_http2: true
        static_configs:
        - targets:
          - prometheus-node-exporter.tanzu-system-monitoring.svc.cluster.local:9100
      - job_name: kubernetes-pods
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: http
        follow_redirects: true
        enable_http2: true
        relabel_configs:
        - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
          separator: ;
          regex: "true"
          replacement: $1
          action: keep
        - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
          separator: ;
          regex: (.+)
          target_label: __metrics_path__
          replacement: $1
          action: replace
        - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
          separator: ;
          regex: ([^:]+)(?::\d+)?;(\d+)
          target_label: __address__
          replacement: $1:$2
          action: replace
        - separator: ;
          regex: __meta_kubernetes_pod_label_(.+)
          replacement: $1
          action: labelmap
        - source_labels: [__meta_kubernetes_namespace]
          separator: ;
          regex: (.*)
          target_label: kubernetes_namespace
          replacement: $1
          action: replace
        - source_labels: [__meta_kubernetes_pod_name]
          separator: ;
          regex: (.*)
          target_label: kubernetes_pod_name
          replacement: $1
          action: replace
        kubernetes_sd_configs:
        - role: pod
          kubeconfig_file: ""
          follow_redirects: true
          enable_http2: true
      - job_name: kubernetes-nodes-cadvisor
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: https
        authorization:
          type: Bearer
          credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          insecure_skip_verify: true
        follow_redirects: true
        enable_http2: true
        relabel_configs:
        - separator: ;
          regex: __meta_kubernetes_node_label_(.+)
          replacement: $1
          action: labelmap
        - separator: ;
          regex: (.*)
          target_label: __address__
          replacement: kubernetes.default.svc:443
          action: replace
        - source_labels: [__meta_kubernetes_node_name]
          separator: ;
          regex: (.+)
          target_label: __metrics_path__
          replacement: /api/v1/nodes/$1/proxy/metrics
          action: replace
        kubernetes_sd_configs:
        - role: node
          kubeconfig_file: ""
          follow_redirects: true
          enable_http2: true
      - job_name: kubernetes-apiservers
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: https
        authorization:
          type: Bearer
          credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          insecure_skip_verify: true
        follow_redirects: true
        enable_http2: true
        relabel_configs:
        - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
          separator: ;
          regex: default;kubernetes;https
          replacement: $1
          action: keep
        kubernetes_sd_configs:
        - role: endpoints
          kubeconfig_file: ""
          follow_redirects: true
          enable_http2: true
      - job_name: kubernetes-nodes-container
        honor_timestamps: true
        scrape_interval: 1m
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: https
        authorization:
          type: Bearer
          credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          insecure_skip_verify: true
        follow_redirects: true
        enable_http2: true
        relabel_configs:
        - separator: ;
          regex: __meta_kubernetes_node_label_(.+)
          replacement: $1
          action: labelmap
        - separator: ;
          regex: (.*)
          target_label: __address__
          replacement: kubernetes.default.svc:443
          action: replace
        - source_labels: [__meta_kubernetes_node_name]
          separator: ;
          regex: (.+)
          target_label: __metrics_path__
          replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor
          action: replace
        kubernetes_sd_configs:
        - role: node
          kubeconfig_file: ""
          follow_redirects: true
          enable_http2: true
```
</div></details>

## Grafanaのインストール
利用可能なGrafanaのPackageのバージョンを取得する。
```sh
tanzu package available list grafana.tanzu.vmware.com -A
export GRAFANA_VER=7.5.17+vmware.1-tkg.1
```
設定ファイルを抽出する。
```sh
tanzu package available get grafana.tanzu.vmware.com/$GRAFANA_VER --default-values-file-output grafana-default-values.yaml
```
設定ファイルを書き換える。今回は以下のように設定した。
```yaml
$ cat grafana-default-values.yaml
ingress:
  enabled: true
  virtual_host_fqdn: grafana.10-151-199-248.sslip.io
```
設定ファイルを使ってインストールする。
```sh
export NAMESPACE=my-packages
tanzu package install grafana --package grafana.tanzu.vmware.com --version $GRAFANA_VER --values-file grafana-default-values.yaml --namespace $NAMESPACE
```
なお、以下のようなエラーが出る場合は古いGrafanaを指定している可能性があるのでバージョンを見直した方がよい。
```
kapp: Error: Expected to find kind 'cert-manager.io/v1alpha2/Issuer', but did not:
```
インストールに成功後、アクセス先を取得し、ブラウザでアクセスする。
```sh
$ kubectl get httpproxy -n tanzu-system-dashboards
NAME                FQDN                              TLS SECRET    STATUS   STATUS DESCRIPTION
grafana-httpproxy   grafana.10-151-199-248.sslip.io   grafana-tls   valid    Valid HTTPProxy
```

パスワードを変更していなければ、ユーザ名admin、パスワードadminでログインできる。
![1681728002877.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/6a1a0847-a207-3717-ec69-ee369b839016.png)


なお、デフォルトのダッシュボードはN/Aのものがある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/b2bc4940-b68f-9d43-1bbc-92a68b7bf227.png)

そのため、ダッシュボードはデフォルトの物を使うというより、自作した方がよさそうだ。