kube-state-metrics で不要なメトリクスを出力しない方法

## はじめに
kubernetes を長期間運用していると、次第に収集するメトリクスも増えていくと思います。収集するメトリクスが増えると Prometehus のメモリ使用量も増え、TSDB のデータサイズを圧迫します。また、マネージドな Prometheus サービス等を利用している場合はメトリクスの蓄積量に比例して費用が増えていくケースもあると思います。

そのため、今回は kube-state-metrics で不要なメトリクスを出力しない方法を説明します。 
(2022/6/29 時点で最新であった v2.5.0 の内容を元に執筆しています。)

- https://github.com/kubernetes/kube-state-metrics/releases/tag/v2.5.0

## kube-state-metrics とは
以下が[公式の Overview](https://github.com/kubernetes/kube-state-metrics#overview) から一部抜粋してきた説明文になります。

>kube-state-metrics is a simple service that listens to the Kubernetes API server and generates metrics about the state of the objects.

上記の説明に書いてある通り、 Kubernetes の API server を定期的に確認して Kubernetes のオブジェクトの状態をメトリクスとして提供してくれます。

## kube-state-metrics でメトリクスを削減する方法
kube-state-metrics でメトリクスを削減する方法は以下の3種類になります。

- `--resources`
- `--namespaces` or `--namespaces-denylist`
- `--metric-allowlist` or `--metric-denylist`


使い方については以降で説明していきます。

### `--resources`

#### 概要

起動時の引数に `--resources` を指定することでメトリクスを出力する kubernetes のオブジェクトを指定することができます。

公式ドキュメント上の説明は以下になります。

>--resources string                           Comma-separated list of Resources to be enabled. Defaults to "certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments"

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md

kube-state-metrics は `--resources` に指定されたオブジェクトに関するメトリクスを出力するようになっており、v2.5.0 の時点では28種類のオブジェクトがデフォルトで設定されています。機能的には node-exporter の [`collector`](https://github.com/prometheus/node_exporter#collectors) と同じようなものになります。

公式のドキュメントの説明だと改行がなくて一覧が分かりにくいので、ソースコードからの抜粋を以下に記載します。

```go
	// DefaultResources represents the default set of resources in kube-state-metrics.
	DefaultResources = ResourceSet{
		"certificatesigningrequests":      struct{}{},
		"configmaps":                      struct{}{},
		"cronjobs":                        struct{}{},
		"daemonsets":                      struct{}{},
		"deployments":                     struct{}{},
		"endpoints":                       struct{}{},
		"horizontalpodautoscalers":        struct{}{},
		"ingresses":                       struct{}{},
		"jobs":                            struct{}{},
		"leases":                          struct{}{},
		"limitranges":                     struct{}{},
		"mutatingwebhookconfigurations":   struct{}{},
		"namespaces":                      struct{}{},
		"networkpolicies":                 struct{}{},
		"nodes":                           struct{}{},
		"persistentvolumes":               struct{}{},
		"persistentvolumeclaims":          struct{}{},
		"poddisruptionbudgets":            struct{}{},
		"pods":                            struct{}{},
		"replicasets":                     struct{}{},
		"replicationcontrollers":          struct{}{},
		"resourcequotas":                  struct{}{},
		"secrets":                         struct{}{},
		"services":                        struct{}{},
		"statefulsets":                    struct{}{},
		"storageclasses":                  struct{}{},
		"validatingwebhookconfigurations": struct{}{},
		"volumeattachments":               struct{}{},
```

- https://github.com/kubernetes/kube-state-metrics/blob/v2.5.0/pkg/options/resource.go#L27-L56

#### 設定方法

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --resources=leases
```

`--resources` に設定されてる内容については、起動時ログの`Active resources:`に表示されます。

```diff
I0628 08:00:51.581470       1 server.go:100] Using resources leases
I0628 08:00:51.581544       1 types.go:136] Using all namespace
I0628 08:00:51.581551       1 server.go:122] Metric allow-denylisting: Excluding the following lists that were on denylist: 
W0628 08:00:51.581590       1 client_config.go:617] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
I0628 08:00:51.582018       1 server.go:250] Testing communication with server
I0628 08:00:51.587316       1 server.go:255] Running with Kubernetes cluster version: v1.24. git version: v1.24.2. git tree state: clean. commit: f66044f4361b9f1f96f0053dd46cb7dce5e990a8. platform: linux/amd64
I0628 08:00:51.587387       1 server.go:257] Communication with server successful
I0628 08:00:51.587695       1 server.go:202] Starting metrics server: [::]:8080
I0628 08:00:51.587825       1 metrics_handler.go:96] Autosharding disabled
I0628 08:00:51.587897       1 server.go:191] Starting kube-state-metrics self metrics server: [::]:8081
I0628 08:00:51.587936       1 server.go:66] levelinfomsgTLS is disabled.http2false
+ I0628 08:00:51.587921       1 builder.go:232] Active resources: leases
I0628 08:00:51.588224       1 server.go:66] levelinfomsgTLS is disabled.http2false
```

`--resources` が設定されていない状態だと、以下のようにデフォルトの値が設定されます。

```diff
- I0628 07:10:13.641643       1 builder.go:232] Active resources: certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments
```

`--resources` に `leases` しか設定しない状態で kube-state-metrics を起動すると以下のように `kube_lease_XXX` の形式のメトリクスのみが出力され、それ以外のメトリクスが出力されなくなります。

```
# HELP kube_lease_owner Information about the Lease's owner.
# TYPE kube_lease_owner gauge
kube_lease_owner{lease="kind-control-plane",owner_kind="Node",owner_name="kind-control-plane"} 1
kube_lease_owner{lease="kind-worker2",owner_kind="Node",owner_name="kind-worker2"} 1
kube_lease_owner{lease="kind-worker",owner_kind="Node",owner_name="kind-worker"} 1
# HELP kube_lease_renew_time Kube lease renew time.
# TYPE kube_lease_renew_time gauge
kube_lease_renew_time{lease="kind-control-plane"} 1.656403259e+09
kube_lease_renew_time{lease="kind-worker2"} 1.656403259e+09
kube_lease_renew_time{lease="kind-worker"} 1.656403265e+09
```

#### 注意点

`--resources`に対して現状の kube-state-metrics では deny list 形式では指定する方法がありません。例えば、`secrets`のメトリクスだけを出力したくない場合は以下のように設定することになります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --resources=certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments
```

見ての通り、パッとみただけでは `secrets` のメトリクスが出力されないように設定されていることがわかりません。デフォルトの設定値との diff をとることで初めて、 `secrets` が設定されていないことに気づくと思います。

継続して運用していくことを考えると、`--resources`に対してどの値を設定していないのかが、チームに新しいメンバーが参加した時でも分かるようにしておいた方が良いです。

### `--namespaces` / `--namespaces-denylist`

#### 概要

起動時の引数に `--namespaces` or `--namespaces-denylist` を指定することで namespace 単位で出力するメトリクスを制御できるようになります。

公式ドキュメント上の説明は以下になります。

>--namespaces string                          Comma-separated list of namespaces to be enabled. Defaults to ""
>--namespaces-denylist string                 Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md

`--namespaces` が指定した namespace に関するメトリクスだけ出力するようにする機能で、`--namespaces-denylist` が指定した namespace に関するメトリクスだけ出力しないようにする機能になります。

#### 設定方法（`--namespaces`）

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --namespaces=kube-system
```

出力例は以下になります。

```diff
# --namespaces 指定なし
- kube_service_info{namespace="kube-system",service="kube-state-metrics",uid="2b87f007-4953-46e1-ade4-5496a2ca8c20",cluster_ip="None",external_name="",load_balancer_ip=""} 1
- kube_service_info{namespace="default",service="kubernetes",uid="69799a55-9095-4ef9-8e59-c99ad01e8bbd",cluster_ip="10.96.0.1",external_name="",load_balancer_ip=""} 1
- kube_service_info{namespace="kube-system",service="kube-dns",uid="fae94b07-09d6-4939-8409-1a16d7cda19f",cluster_ip="10.96.0.10",external_name="",load_balancer_ip=""} 1

# --namespaces 指定あり
+ kube_service_info{namespace="kube-system",service="kube-state-metrics",uid="2b87f007-4953-46e1-ade4-5496a2ca8c20",cluster_ip="None",external_name="",load_balancer_ip=""} 1
+ kube_service_info{namespace="kube-system",service="kube-dns",uid="fae94b07-09d6-4939-8409-1a16d7cda19f",cluster_ip="10.96.0.10",external_name="",load_balancer_ip=""} 1

```

指定した `kube-system` に関するメトリクスのみが出力されていることを確認できます。


#### 設定方法（`--namespaces-denylist`）

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --namespaces-denylist=kube-system
```

出力例は以下になります。

```diff
# --namespaces-denylist 指定なし
- kube_service_info{namespace="kube-system",service="kube-state-metrics",uid="2b87f007-4953-46e1-ade4-5496a2ca8c20",cluster_ip="None",external_name="",load_balancer_ip=""} 1
- kube_service_info{namespace="default",service="kubernetes",uid="69799a55-9095-4ef9-8e59-c99ad01e8bbd",cluster_ip="10.96.0.1",external_name="",load_balancer_ip=""} 1
- kube_service_info{namespace="kube-system",service="kube-dns",uid="fae94b07-09d6-4939-8409-1a16d7cda19f",cluster_ip="10.96.0.10",external_name="",load_balancer_ip=""} 1

# --namespaces-denylist 指定あり
+ kube_service_info{namespace="default",service="kubernetes",uid="69799a55-9095-4ef9-8e59-c99ad01e8bbd",cluster_ip="10.96.0.1",external_name="",load_balancer_ip=""} 1
```

指定した `kube-system` に関するメトリクスが出力されていないことが確認できます。

#### 設定方法（`--namespaces` と `--namespaces-denylist`　の両方を設定した場合）

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --namespaces=kube-system
  - --namespaces-denylist=kube-system
```

出力例は以下になります。

```diff
# --namespaces-denylist 指定なし
- kube_service_info{namespace="kube-system",service="kube-state-metrics",uid="2b87f007-4953-46e1-ade4-5496a2ca8c20",cluster_ip="None",external_name="",load_balancer_ip=""} 1
- kube_service_info{namespace="default",service="kubernetes",uid="69799a55-9095-4ef9-8e59-c99ad01e8bbd",cluster_ip="10.96.0.1",external_name="",load_balancer_ip=""} 1
- kube_service_info{namespace="kube-system",service="kube-dns",uid="fae94b07-09d6-4939-8409-1a16d7cda19f",cluster_ip="10.96.0.10",external_name="",load_balancer_ip=""} 1

# --namespaces-denylist 指定あり
+ N/A
```

>--namespaces-denylist string                 Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md

公式ドキュメントの `--namespaces-denylist` の説明を読むと「 `--namespaces` と `--namespaces-denylist` の両方が設定されている場合は、`--namespaces-denylist` が優先されます。」と書いてあるように見えますが、実際試したところ、両方が同時に機能しているように見えるので、namespace 単位でメトリクスの出力を制御したい場合は、 `--namespaces` か `--namespaces-denylist` のどちらかだけを使う方が良いかなと思います。


### `--metric-allowlist` / `--metric-denylist`

#### 概要

起動時の引数に `--metric-allowlist` or `--metric-denylist` を指定することでメトリクス単位で出力するメトリクスを制御できるようになります。

公式ドキュメント上の説明は以下になります。

>--metric-allowlist string                    Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.
>--metric-denylist string                     Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md

#### 設定方法(`--metric-allowlist`)

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --metric-allowlist=kube_node_created,kube_lease_*
```

上記のように出力したいメトリクスを`,`区切りで指定していきます。また、`*` による指定も可能です。
(lease に関するメトリクスが v2.5.0 の時点で2種類しかなく、分かりやすいのでサンプルとして使用しています。)

出力例は以下になります。

```diff
# --metric-allowlist 指定なし
- 特に制限なく、全てのメトリクスが出力されます

# --metric-allowlist 指定あり
+ # HELP kube_lease_owner Information about the Lease's owner.
+ # TYPE kube_lease_owner gauge
+ kube_lease_owner{lease="kind-worker",owner_kind="Node",owner_name="kind-worker"} 1
+ kube_lease_owner{lease="kind-control-plane",owner_kind="Node",owner_name="kind-control-plane"} 1
+ kube_lease_owner{lease="kind-worker2",owner_kind="Node",owner_name="kind-worker2"} 1
+ # HELP kube_lease_renew_time Kube lease renew time.
+ # TYPE kube_lease_renew_time gauge
+ kube_lease_renew_time{lease="kind-worker"} 1.656629831e+09
+ kube_lease_renew_time{lease="kind-control-plane"} 1.65662983e+09
+ kube_lease_renew_time{lease="kind-worker2"} 1.656629832e+09
+ # HELP kube_node_created Unix creation timestamp
+ # TYPE kube_node_created gauge
+ kube_node_created{node="kind-worker2"} 1.65638235e+09
+ kube_node_created{node="kind-worker"} 1.65638235e+09
+ kube_node_created{node="kind-control-plane"} 1.656382327e+09
```

上記のように `--metric-allowlist` で指定したメトリクスのみが出力されるようになります。

#### 設定方法(`--metric-denylist`)

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --metric-denylist=kube_configmap_metadata_resource_version,kube_secret_*
```

上記のように出力したいくないメトリクスを`,`区切りで指定していきます。また、`*` による指定も可能です。

出力例は以下になります。

```diff
# --metric-denylist 指定なし
- kube_configmap_metadata_resource_version{namespace="kube-public",configmap="kube-root-ca.crt"} 339
- kube_configmap_metadata_resource_version{namespace="kube-public",configmap="cluster-info"} 360
- kube_configmap_metadata_resource_version{namespace="kube-system",configmap="kube-proxy"} 254

# --metric-denylist 指定あり
+ N/A
```

上記のように `--metric-denylist` で指定したメトリクスは出力されないようになります。

#### 設定方法（`--metric-allowlist` と `--metric-denylist`　の両方を設定した場合）

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --metric-allowlist=kube_lease_*
  - --metric-denylist=kube_lease_owner
```

lease に関するメトリクスを出力するが、`kube_lease_owner` のメトリクスを出力したくないという意図で上記のように設定して見います。

デプロイすると以下のようになります。

```diff
# kubectl get po
kube-state-metrics-869fc5bb58-k2p2j          0/1     Error     1 (2s ago)     2s

# kubectl logs
I0630 23:01:12.267110       1 server.go:93] Using default resources
I0630 23:01:12.267218       1 types.go:136] Using all namespace
- F0630 23:01:12.267256       1 main.go:65] Failed to run kube-state-metrics: allowlist and denylist are both set, they are mutually exclusive, only one of them can be set
goroutine 1 [running]:
k8s.io/klog/v2.stacks(0x1)
	/go/pkg/mod/k8s.io/klog/v2@v2.60.1/klog.go:860 +0x8a
k8s.io/klog/v2.(*loggingT).output(0x264d0a0, 0x3, 0x0, 0xc000294a10, 0x1, {0x1e5b651?, 0x1?}, 0x264e2a0?, 0x0)
	/go/pkg/mod/k8s.io/klog/v2@v2.60.1/klog.go:825 +0x686
k8s.io/klog/v2.(*loggingT).printfDepth(0x264d0a0, 0x0?, 0x0, {0x0, 0x0}, 0x7fe82bd9c290?, {0x18a2283, 0x24}, {0xc00012b540, 0x1, ...})
	/go/pkg/mod/k8s.io/klog/v2@v2.60.1/klog.go:630 +0x1f2
k8s.io/klog/v2.(*loggingT).printf(...)
	/go/pkg/mod/k8s.io/klog/v2@v2.60.1/klog.go:612
k8s.io/klog/v2.Fatalf(...)
	/go/pkg/mod/k8s.io/klog/v2@v2.60.1/klog.go:1516
main.main()
	/go/src/k8s.io/kube-state-metrics/main.go:65 +0x514
```

`--metric-allowlist` と `--metric-denylist` を両方を設定することはできないようになっており、起動時にエラーになります。

#### `--resources`と`--metric-denylist`の比較

kube-state-metrics で出力するメトリクス名には `kube_<オブジェクト名>_XXXX` の命名規則があります。そのため、`secrets` に関するメトリクスを出力しないようした時に、以下の2つの方法のどちらでも設定できます。
　
- `--resources` による設定

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --resources=certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments
```

- `--metric-denylist`による設定

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --metric-denylist=kube_secret_*
```

上記の2つのうちどちらを選ぶかのポイントは `kube-state-metricsのリソースの使用量`と `manifestの可読性`になるかなと思います。リソース消費が少ないことを優先するなら `--resources` の方が良いですが、リソースの消費量の増加は許容できる範囲なので、可読性を重視して `--metric-denylist` を選択するという考え方もあると思います。この辺の判断はチームとしてどう運用していきたいかによると思います。

## その他

kube-state-metrics でメトリクスを削減する方法は以下の3種類になりますが、関連する事柄について少し記載します。

- `--resources`
- `--namespaces` or `--namespaces-denylist`
- `--metric-allowlist` or `--metric-denylist`

### `--metric-opt-in-list`

#### 概要

kube-state-metrics v2.3.0 から `--metric-opt-in-list` の機能が追加されました。

公式ドキュメント上の説明は以下になります。

>--metric-opt-in-list string                  Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md

これはデフォルトでは出力されないけど、利用者が該当のメトリクスを指定することで利用できるようになるメトリクスを制御するための機能になります。高いカーディナリティの持つメトリクスを新しく kube-state-metrics に追加するときなどに使われることを想定して追加されました。

この機能の導入を契機に、メトリクスの説明には `Opt-in` の列が追加されました。
kube-state-metrics v2.5.0 の時点で  `Opt-in` に対応しているメトリクスは `kube_pod_nodeselectors` のみになります。

| Metric name| Metric type | Description | Unit (where applicable) | Labels/tags | Status | Opt-in |
| ---------- | ----------- | ----------- | ----------------------- | ----------- | ------ | ------ |
| kube_pod_nodeselectors| Gauge | Describes the Pod nodeSelectors | |  `pod`=&lt;pod-name&gt; <br> `namespace`=&lt;pod-namespace&gt; <br> `nodeselector_NODE_SELECTOR`=&lt;NODE_SELECTOR&gt; <br> `uid`=&lt;pod-uid&gt; | EXPERIMENTAL | Opt-in |

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/pod-metrics.md

#### 設定方法

設定例は以下になります。

```yaml
containers:
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.5.0
  args:
  - --metric-opt-in-list=kube_pod_nodeselectors
```

まだ、対応しているメトリクスが　`kube_pod_nodeselectors` のみなので確認できていませんが、複数指定する場合は恐らくいつも通り `,` 区切りで指定することになると思います。


出力例は以下になります。

```
# HELP kube_pod_nodeselectors Describes the Pod nodeSelectors.
# TYPE kube_pod_nodeselectors gauge
kube_pod_nodeselectors{namespace="kube-system",pod="kube-state-metrics-84898b978d-rml7z",uid="4f78fe43-60f8-46aa-82e0-5172ae060937",nodeselector_kubernetes_io_os="linux"} 1
kube_pod_nodeselectors{namespace="kube-system",pod="kube-proxy-f7h4m",uid="a90db164-ff5a-4e91-9dfd-47150c836593",nodeselector_kubernetes_io_os="linux"} 1
kube_pod_nodeselectors{namespace="kube-system",pod="kube-apiserver-kind-control-plane",uid="992ad0ac-029c-4333-abc7-48747140b89a"} 1
```

#### 気になる点

新規メトリクスを追加時に`--metric-opt-in-list` を利用が常に検討されるようになると、 kube-state-metrics のアップデート時の影響が抑えられるようになるのでいいなと思ってます。ただ、新規メトリクスを追加するときは default は true で PR を出す人が大半だと思うので、コミニュティ側で強くこの機能を主導していかないと、新規メトリクスの追加時にこの機能の利用は検討されないと思います。

kube-state-metrics には `Metrics Stages` という考え方があるのですが、これも厳密に運用されているわけではなさそうです。`STABLE` だとメジャーアップデート以外でもラベル名が変更になるなどの破壊的な変更が入ったことがあり、新規にメトリクス追加する際に必ず `EXPERIMENTAL` にする必要もなく、 `EXPERIMENTAL` がどのタイミングで `STABLE` になるのかも特に決まってる様子はないというのが実情です。

| Stage        | Description                                                                                                                |
| ------------ | -------------------------------------------------------------------------------------------------------------------------- |
| EXPERIMENTAL | Metrics which normally correspond to the Kubernetes API object alpha status or spec fields and can be changed at any time. |
| STABLE       | Metrics which should have very few backwards-incompatible changes outside of major version updates.                        |
| DEPRECATED   | Metrics which will be removed once the deprecation timeline is met.                                                        |

- https://github.com/kubernetes/kube-state-metrics/blob/master/docs/README.md#metrics-stages


恐らく`--metric-opt-in-list` の方もそこまで厳密に運用されることはないと思っています。基本的にはアップデート時に Changelog をチェックして、影響がありそうなメトリクスが追加される場合は `--metric-denylist` 等を用いて、利用者側で自衛していく形の運用が必要になるかなと思います。

あくまで、ボランティアとして空いた時間にみんながご好意で開発してくれているだけなので、自分達のサービスに関係する部分については、自分達で確認する必要があるものだと思って利用された方がいいと思います。


### Prometheus でメトリクスの削減

#### 概要
Prometheus の `relabel` の機能を使うことで、同じようにメトリクスを削減することができます。
`relabel` 自体は名前の通り、Prometheus 上でメトリクスのラベルをもとに何かしらの変換を行える機能になります。その中で実行できる `relabel_action` のうち、以下の4種類についてはメトリクスの削減にも利用することができます。

- メトリクス自体の削減が可能なもの
  - `keep`：条件に一致するメトリクス以外を削除する
  - `drop`：条件に一致するメトリクスを削除する
- メトリクスに含まれる特定のラベルの削減が可能なもの
  - `labelkeep`：メトリクスから条件に一致するラベル以外のラベルを削除する
  - `labeldrop`：メトリクスから条件に一致するラベルを削除する

公式ドキュメント上の該当機能に関する説明は以下になります。

>`<relabel_action>` determines the relabeling action to take:
>
>* `keep`: Drop targets for which `regex` does not match the concatenated `source_labels`.
>* `drop`: Drop targets for which `regex` matches the concatenated `source_labels`.
>* `labeldrop`: Match `regex` against all label names. Any label that matches will be removed from the set of labels.
>* `labelkeep`: Match `regex` against all label names. Any label that does not match will be removed from the set of labels.

- https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config

Prometheus 上で `relabel` の設定をできる箇所は複数あるのですが、メトリクスを削減目的で設定する場合は主に以下の3箇所のいずれかに設定すると思います。それぞれで `relabel` を実行するタイミングが異なります。

- scrape_config
  - relabel_configs：スクレイプ時
  - metric_relabel_configs：Prometheus が内部に持つ TSDB に書き込む手前
- remote_write
  - write_relabel_configs：Prometheus が [Remote storage](https://prometheus.io/docs/prometheus/latest/storage/#remote-storage-integrations) に書き込む手前

それぞれの使い方ですが Prometheus が内部にTSDB のデータサイズを削減したい場合は `metric_relabel_configs`、長期保管するメトリクスを削減したり、マネージドな Prometheus サービスの費用低減のために削減する部分は `write_relabel_configs` に書いてあると、第三者から見て設計の意図がわかりやすくなるかなとは思います。


#### 設定方法

設定例については、Grafana Labs のブログにわかりやすい記事があったので、参考にさせて頂きました。

詳細について知りたい方は以下のブログのを参照ください。
- https://grafana.com/blog/2022/03/21/how-relabeling-in-prometheus-works/


以下のようなメトリクスが出つ力される場合に

```yaml
my_custom_counter_total{server="webserver01",subsystem="kata"} 192  1644075074000
my_custom_counter_total{server="sqldatabase",subsystem="kata"} 14700  1644075074000
```

- kubernetes の namespace　上の `testing` と `staging` のメトリクスを削減したい場合

```yaml
- source_labels: [__meta_kubernetes_namespace]
  regex: “testing|staging”
  action: drop
```

- メトリクスの中で、`subsystem` のラベルに関する情報を削減したい場合

```yaml
- regex: "subsystem"
  action: labeldrop
```

#### 注意点

本当に利用しないメトリクスを削減したい場合は、Prometheus の `relabel` の機能ではなく、メトリクスを出力する側で削減することを検討した方がいいと思います。その方が Prometheus 側で追加で処理をする必要がなくなりますし、Prometheus の設定ファイルの記述量が減るので運用しやすいかなと思います。

長期間、Prometheus の運用をしていくと設定ファイルがどんどん肥大化していって、新規で参加したメンバーがかなり理解しづらい状態になりやすいので設定がシンプルな方が自分は運用しやすいなと思っています。

ただ、現状の kube-state-metrics では、`--metric-denylist` を用いてメトリクス名単位での削除する機能しかなく、特定のラベルを条件にメトリクスを削除したい場合は、Prometheus の `relabel` の機能を使うしかないので、そこは理解して使い分ける必要があります。


## おわりに

Kubernetes を利用している人で、kube-state-metrics を利用している人の数はかなり多いはずなのに、あまり日本語の情報がなさそうだなと思ったので、kube-state-metrics でのメトリクス削減方法について書いてみました。

もっとメトリクスを削減したいなと思った方は node-exporter の [`--no-collector.<name>`](https://github.com/prometheus/node_exporter#collectors)見ておくといいのかなと思います。デフォルトの設定のままで node-exporter を利用している場合は、結構削減できる箇所があると思います。