Tanzu PackagesのPrometheusのメトリクスをMimirに保存する

Prometheusは標準ではTSDB(Time Series Database)と呼ばれるフォーマットのローカルストレージ上のDBにメトリクスを蓄積していく。
このDB、耐障害性や冗長化については考慮されておらず、障害が起きた時の対処法は以下のように[記載されている](https://prometheus.io/docs/prometheus/latest/storage/#operational-aspects)。

> If your local storage becomes corrupted for whatever reason, the best strategy to address the problem is to shut down Prometheus then remove the entire storage directory. 

ざっくり言うと、障害が起きた時は全部消せ、とエンタープライズ用途には厳しいことが書いていて、TSDBは基本的に小規模・短期利用のためのDBとなっている。
では大規模・長期で使いたい場合はどうすればいいかというと、Remote StorageというPrometheus外のストレージに書き込んで保存することで障害によるデータ破損を回避することが出来る。
雰囲気的には以下のような感じになる。
```mermaid
flowchart LR
  subgraph Prometheus
    Agent
  end
  remote["Remote Storage"]
  Grafana --> remote
  Prometheus -- "Remote Write" --> remote
  Prometheus -- "Scrape" --> App
```

このRemote Storage、どのようなものが対応しているかについては[こちら](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage)に一覧がある。
大手パブリッククラウドのストレージから、Splunk、New RelicといったSaaS製品、Kafka、Thanos、InfluxDBなどの自前で用意しやすいものなど様々なストレージが提供されているので、自分が使いやすいものを選ぶと良さそう。
自前で用意する場合は一昔前はThanosがメジャーだったが、どうも今はGrafana LabsがPrometheusのメトリクスの長期保存用に開発している[Grafana Mimir](https://github.com/grafana/mimir)(発音はミーミル？)が流行っているみたいなので、今回はこれを使ってRemote Writeを確認する。

## 前提
ここでは以下の環境で実施する。
- vSphere with TanzuのTKGクラスタ構築済み (v1.25.7+vmware.3-fips.1を使用)
- `Type: LoadBalancer`が利用できる
- `helm`、`yq`、`tanzu cli`はインストール済み

Prometheus、Grafanaの構築はTanzu Packagesを利用する。

## PrometheusとGrafanaの構築
[Tanzu Packages(2023.12.18)](https://docs.vmware.com/en/VMware-Tanzu-Packages/2023.12.18/tanzu-packages/packages-monitoring.html)を使って構築する。
ここはあまり本質ではないのでサクサク行く。
Prometheusを含む`PackageRepository`である`tanzu-standard`をデプロイする。バージョンは[こちら](https://docs.vmware.com/en/VMware-Tanzu-Packages/2023.12.18/tanzu-packages/prep.html#supervisor-tkr)を確認して、環境に合わせて変更すること。
```yaml
cat <<EOF | kubectl apply -f -
apiVersion: packaging.carvel.dev/v1alpha1
kind: PackageRepository
metadata:
  name: tanzu-standard
  namespace: tkg-system
spec:
  fetch:
    imgpkgBundle:
      image: projects.registry.vmware.com/tkg/packages/standard/repo:v2.2.0_update.2
EOF
```
cert-managerをインストールする。

```sh
kubectl create ns cert-manager
tanzu package install cert-manager -p cert-manager.tanzu.vmware.com -n cert-manager -v 1.10.2+vmware.1-tkg.1
```
Contourをインストールする。
```sh
CONTOUR_VER=1.23.5+vmware.1-tkg.1
kubectl create ns tanzu-system-ingress
cat <<EOF > ./contour-data-values.yaml
infrastructure_provider: vsphere
namespace: tanzu-system-ingress
contour:
  logLevel: info
  replicas: 2
  useProxyProtocol: false
envoy:
 service:
   type: LoadBalancer
   annotations: {}
   externalTrafficPolicy: Cluster
   disableWait: false
 hostPorts:
   enable: true
   http: 80
   https: 443
 hostNetwork: false
 terminationGracePeriodSeconds: 300
 logLevel: info
certificates:
 duration: 8760h
 renewBefore: 360h
EOF
tanzu package install contour -p contour.tanzu.vmware.com -v $CONTOUR_VER --values-file contour-data-values.yaml -n tanzu-system-ingress
```
Prometheusをインストールする。なおデフォルトでは"default"という名前の`StorageClass`をAlertManagerが使おうとするため、もしそのような名前の`StorageClass`がない場合は作成するか、`StorageClass`の設定を変更すること。
```sh
PROM_VER=2.37.0+vmware.3-tkg.1
tanzu package available get prometheus.tanzu.vmware.com/$PROM_VER --generate-default-values-file
cp prometheus-default-values.yaml prometheus-data-values.yaml
tanzu package install prometheus -p prometheus.tanzu.vmware.com -v $PROM_VER --values-file prometheus-data-values.yaml -n tanzu-system-monitoring  --create-namespace
```
[Grafana](https://docs.vmware.com/en/VMware-Tanzu-Packages/2023.12.18/tanzu-packages/packages-grafana-super.html)も同様に構築する。なお、7.5.7を使うとcert-managerの古いAPIバージョンを要求するためインストールに失敗する。
また、[既知不良](https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.4/tkg-deploy-mc/workload-troubleshoot-index.html#secret-not-created-when-installing-grafana-from-default-yaml-file-3)があるので、ワークアラウンドをあわせて実施する。
```sh
kubectl create ns tanzu-system-dashboards 
kubectl create secret generic grafana -n tanzu-system-dashboards --from-literal=admin=admin
GRAFANA_VER=7.5.16+vmware.1-tkg.2
tanzu package available get grafana.tanzu.vmware.com/$GRAFANA_VER --generate-default-values-file
cp grafana-default-values.yaml grafana-data-values.yaml
sed -i "s/grafana.system.tanzu/grafana.$(kubectl get svc -n tanzu-system-ingress envoy -o jsonpath={.status.loadBalancer.ingress[0].ip}).nip.io/g" grafana-data-values.yaml
yq eval 'del(.grafana.secret)' grafana-data-values.yaml -i
tanzu package install grafana -p grafana.tanzu.vmware.com -v $GRAFANA_VER --values-file grafana-data-values.yaml -n tanzu-system-dashboards --create-namespace
```

## Grafana Mimirの構築
[Get started with Grafana Mimir using the Helm chart](https://grafana.com/docs/helm-charts/mimir-distributed/latest/get-started-helm-charts/)を参考にMimirを構築する。
最初にHelmに渡すyamlを作成し、Ingressで外部にサービスを公開するようにする。
※今回の構成ではこの工程は省くことも出来るが、実利用では外部サービスとして利用することが多そうなのでIngressで公開した。
```yaml
cat <<EOF > ./custom.yaml
nginx:
  ingress:
    enabled: true
    hosts:
      - host: mimir.$(kubectl get svc -n tanzu-system-ingress envoy -o jsonpath={.status.loadBalancer.ingress[0].ip}).nip.io
        paths:
          - path: /
            pathType: Prefix
EOF
```
Minirをインストールする。
```sh
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm -n mimir-test upgrade -i mimir grafana/mimir-distributed --create-namespace -f ./custom.yaml
```
インストールすると以下のようなメッセージが出る。
```sh
Remote write endpoints for Prometheus or Grafana Agent:
From outside the cluster via ingress:
  http://mimir.10.220.16.163.nip.io/api/v1/push

From inside the cluster:
  http://mimir-nginx.mimir-test.svc:80/api/v1/push

Read address, Grafana data source (Prometheus) URL:
From outside the cluster via ingress:
  http://mimir.10.220.16.163.nip.io/prometheus

From inside the cluster:
  http://mimir-nginx.mimir-test.svc:80/prometheus
```
それぞれMimirのアクセス方法になるので念の為残しておくと後々便利。

構築したMimirのエンドポイントをGrafanaのデータソースに追加する。
"http\://grafana.<envoyのExternalIP>.nip.io"にアクセスすればGrafanaのUIが見れるので、admin/adminでログインし、`Configuration`->`Data Sources`->`Add data source`でPrometheusを選択する。
![1707297552477.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/a7c6ce16-fe4c-2ae9-5735-a68724c52527.png)
データソースに`mimir-nginx.mimir-test.svc/prometheus`を追加して、`Save&Test`をクリックして追加する。
![1707298640300.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/04ba472e-88e3-aa1d-0728-d9c2f14ea142.png)
以上でGrafana、Mimirの設定は完了となる。


## PrometheusからMimirにwriteする
PrometheusにはRemote Write向けのモードとして[Agentモード](https://prometheus.io/docs/prometheus/latest/feature_flags/#prometheus-agent)があり、これを設定するとPrometheusはDiscovery, Scrape, Remote Writeしかしないシンプルなモードに移行する。
ここではPrometheusをAgentモードで起動し、Remote WriteでMimirに書き込むよう設定を変更する。
設定の変更方法についてはmakingさんの「[Tanzu Kubernetes GridのPrometheus Packageでremote-write-receiverとagent modeをセットアップして複数のKubernetesクラスタを監視する](https://ik.am/entries/761)」を参考にした。
prometheus-data-values.yamlを開き、PrometheusのDeploymentの引数に`--enable-feature=agent`を追加する。また、Agentモード時は以下の制約があるのでそれぞれ削除する。
- ` --storage.tsdb.retention.time`と`--storage.tsdb.path`は使えない
- prometheus_yml内にalertの設定やrule_filesが書けない

ファイルの差分は以下となる。
```diff:prometheus-data-values.yaml
@@ -12,7 +12,7 @@
         receiver: default-receiver
         repeat_interval: 3h
   deployment:
-    replicas: 1
+    replicas: 0
     rollingUpdate:
       maxSurge: 25%
       maxUnavailable: 25%
@@ -65,11 +65,8 @@
         evaluation_interval: 1m
         scrape_interval: 1m
         scrape_timeout: 10s
-      rule_files:
-      - /etc/config/alerting_rules.yml
-      - /etc/config/recording_rules.yml
-      - /etc/config/alerts
-      - /etc/config/rules
+      remote_write:
+      - url: http://mimir.10.220.16.163.nip.io/api/v1/push
       scrape_configs:
       - job_name: 'prometheus'
         scrape_interval: 5s
@@ -140,30 +137,6 @@
           ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
           insecure_skip_verify: true
         bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
-      alerting:
-        alertmanagers:
-        - scheme: http
-          static_configs:
-          - targets:
-            - alertmanager.prometheus.svc:80
-        - kubernetes_sd_configs:
-            - role: pod
-          relabel_configs:
-          - source_labels: [__meta_kubernetes_namespace]
-            regex: default
-            action: keep
-          - source_labels: [__meta_kubernetes_pod_label_app]
-            regex: prometheus
-            action: keep
-          - source_labels: [__meta_kubernetes_pod_label_component]
-            regex: alertmanager
-            action: keep
-          - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_probe]
-            regex: .*
-            action: keep
-          - source_labels: [__meta_kubernetes_pod_container_port_number]
-            regex:
-            action: drop
     recording_rules_yml: |
       groups:
         - name: kube-apiserver.rules
@@ -680,12 +653,11 @@
           - --webhook-url=http://127.0.0.1:9090/-/reload
     containers:
       args:
-        - --storage.tsdb.retention.time=42d
         - --config.file=/etc/config/prometheus.yml
-        - --storage.tsdb.path=/data
         - --web.console.libraries=/etc/prometheus/console_libraries
         - --web.console.templates=/etc/prometheus/consoles
         - --web.enable-lifecycle
+        - --enable-feature=agent
     replicas: 1
     rollingUpdate:
       maxSurge: 25%
@@ -700,7 +672,7 @@
     type: ClusterIP
 pushgateway:
   deployment:
-    replicas: 1
+    replicas: 0
   service:
     port: 9091
     targetPort: 9091
```
AlertManagerとPushGatewayは使用しないためレプリカ数を0にした。
設定を反映する。
```sh
tanzu package installed update prometheus -f prometheus-data-values.yaml -n tanzu-system-monitoring
```
Agentモードで動作していることを確認する。
ポートフォワードでPrometheus Serverにアクセスする。
```
kubectl port-forward service/prometheus-server -n tanzu-system-monitoring 9090:80
```
アクセスすると、以下のようにAgentモードで起動していることが分かる。
![1707377430564.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/58ffafdb-a652-305a-1847-33fc5fde1785.png)

Grafanaにアクセスする。
Agentモードになっているため、DatasourceがPrometheusだと表示に失敗する。
![1707377591008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/fb787522-bef9-44db-f9b1-9c224ac6a266.png)
左上の`datasource`のプルダウンから先ほど登録した`Mimir`に切り替えてみる。
![1707377698317.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f980711f-baca-abd5-08be-16c3ad50efb0.png)
データが確認できた。
これにより、Remote Writeが適切に行われ、PrometheusはローカルのTSDBを使わずMimirにメトリクスを溜め込むことが確認できた。