prometheus adapter의 rule 수정을 통해 다양한 사례를 예제로 살펴보고 깊이 이해하는 섹션을 갖는다.

prometheus-adapter Rule의 Resouce 추가를 통한 API 리소스 분리 예제

traefik_service_requests_total 메트릭은 아래와 같이 code, container, ... , namespace, pod, service, ingress와 같은 라벨이 붙어 있다.

Prometheus에 수집된 메트릭 정보

traefik_service_requests_total
{
  code="200", 
  container="traefik", 
  endpoint="metrics", 
  ingress="echo-server-ing", 
  instance="10.233.71.2:9100", 
  job="default/traefik-pod-monitor", 
  method="GET", 
  namespace="default", 
  pod="traefik-664df7d9f4-6rj5q", 
  protocol="http", 
  service="default-echo-server-svc-80@kubernetes"
}

 

prometheus-adapter-configmaps.yaml에 아래와 같이 resource가 정의되어 있다.

      resources:
        overrides:
          namespace: {resource: "namespace"}

여기에 위에 정의된 라벨 중, pod를 추가해서 prometheus adapter에 적용한다.

prometheus-adapter-configmaps.yaml

apiVersion: v1
data:
  config.yaml: |
    rules:
    - seriesQuery: 'traefik_service_requests_total'
      seriesFilters: []
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
      metricsQuery: "sum(increase(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)"
kind: ConfigMap
metadata:
  name: prometheus-adapter
  namespace: default

prometheus adapter configmaps 적용

# kubectl apply -f prometheus-adapter-configmaps.yaml
configmap/prometheus-adapter configured

# kubectl rollout restart deployment prometheus-adapter
deployment.apps/prometheus-adapter restarted

적용 후, api를 호출해본다.

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ | jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "namespaces/traefik_service_requests_total",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "pods/traefik_service_requests_total",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

즉 기존에 "namespaces/traefik_service_requests_total" 만 분류하는 형태에서, "pods/traefik_service_requests_total" 추가로 분류해준다.

새롭게 생성된 api로 데이터를 호출해본다.

라벨 중 pod는 pod="traefik-664df7d9f4-6rj5q"로 저장된다.

호출은 주소는 /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/traefik-664df7d9f4-6rj5q/traefik_service_requests_total 이것이 된다.

# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/traefik-664df7d9f4-6rj5q/traefik_service_requests_total" |jq
{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "describedObject": {
        "kind": "Pod",
        "namespace": "default",
        "name": "traefik-664df7d9f4-6rj5q",
        "apiVersion": "/v1"
      },
      "metricName": "traefik_service_requests_total",
      "timestamp": "2024-11-14T01:27:43Z",
      "value": "98",
      "selector": null
    }
  ]
} 

 

pod resource의 경우 아래와 같이 asterisk(*) 호출해서 여러 개의 pod 호출이 가능하다.

# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/traefik_service_requests_total" |jq
{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "describedObject": {
        "kind": "Pod",
        "namespace": "default",
        "name": "traefik-664df7d9f4-6rj5q",
        "apiVersion": "/v1"
      },
      "metricName": "traefik_service_requests_total",
      "timestamp": "2024-11-14T01:30:07Z",
      "value": "116",
      "selector": null
    }
  ]
}

아래는 traefik pod를 2개로 늘린 후, asterisk(*) 호출 결과이다.

# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/traefik_service_requests_total" |jq
{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "describedObject": {
        "kind": "Pod",
        "namespace": "default",
        "name": "traefik-664df7d9f4-49524",
        "apiVersion": "/v1"
      },
      "metricName": "traefik_service_requests_total",
      "timestamp": "2024-11-14T01:31:43Z",
      "value": "32812m",
      "selector": null
    },
    {
      "describedObject": {
        "kind": "Pod",
        "namespace": "default",
        "name": "traefik-664df7d9f4-6rj5q",
        "apiVersion": "/v1"
      },
      "metricName": "traefik_service_requests_total",
      "timestamp": "2024-11-14T01:31:43Z",
      "value": "58",
      "selector": null
    }
  ]
}

 

prometheus-adapter의 메트릭 이름 변경

 

심플한 메트릭 명칭 변경

prometheus adapter에 수집된 데이터는 "traefik_service_requests_total" 이름을 가진다.

메트릭 쿼리가 아래와 같이 sum + increase 1m으로 진행된다. (결론 저장된 메트릭 이름이 마음에 들지 않는다.)

metricsQuery: "sum(increase(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)"

sum_incr_1m_traefik_service_requests_total 메트릭 이름으로 저장되는 예제는 아래와 같다.

prometheus-adapter-configmaps.yaml

apiVersion: v1
data:
  config.yaml: |
    rules:
    - seriesQuery: 'traefik_service_requests_total'
      seriesFilters: []
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
      name:
        as: "sum_incr_1m_traefik_service_requests_total"
      metricsQuery: "sum(increase(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)"
kind: ConfigMap
metadata:
  name: prometheus-adapter
  namespace: defaultㅁㅁ

적용 후, api를 호출하면 아래와 같이 반영되었다.

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ |jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "pods/sum_incr_1m_traefik_service_requests_total",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "namespaces/sum_incr_1m_traefik_service_requests_total",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

 

기존 메트릭 명칭에서 regexp를 통한 메트릭 명칭 추출

"traefik_service_requests_total" 이름에서 단순하게 _total을 제거하고 싶은 경우가 있다.

regexp rule로 생각해보면 ^(.*)_total$과 같다.
 

prometheus-adapter-configmaps.yaml

apiVersion: v1
data:
  config.yaml: |
    rules:
    - seriesQuery: 'traefik_service_requests_total'
      seriesFilters: []
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
      name:
        matches: "^(.*)_total$"
      metricsQuery: "sum(increase(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)"
kind: ConfigMap
metadata:
  name: prometheus-adapter
  namespace: default

적용 결과

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ |jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "pods/traefik_service_requests",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "namespaces/traefik_service_requests",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

 

matches: "^(.*)_total$" field가 있고, as 필드가 없다면 regexp에서 ${1}의 결과를 메트릭 명칭으로 사용한다.

그럼 as를 사용해서 추출된 ${1} 데이터 _sum_incr_1m라는 문자열을 붙여본다.

 

prometheus-adapter-configmaps.yaml

apiVersion: v1
data:
  config.yaml: |
    rules:
    - seriesQuery: 'traefik_service_requests_total'
      seriesFilters: []
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
      name:
        matches: "^(.*)_total$"
        as: "${1}_sum_incr_1m"
      metricsQuery: "sum(increase(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)"
kind: ConfigMap
metadata:
  name: prometheus-adapter
  namespace: default

적용 후, 결과

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ |jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "namespaces/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "pods/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

regexp를 사용해서 다양하게 문자를 추출할 수 있고, 이를 조합해서 메트릭 명칭을 수정할 수 있다.

 

traefik의 데이터를 ingress 메트릭으로 수집하기

traefik_service_requests_total
{
  code="200", 
  container="traefik", 
  endpoint="metrics", 
  ingress="echo-server-ing", 
  instance="10.233.71.2:9100", 
  job="default/traefik-pod-monitor", 
  method="GET", 
  namespace="default", 
  pod="traefik-664df7d9f4-6rj5q", 
  protocol="http", 
  service="default-echo-server-svc-80@kubernetes"
}

수집된 메트릭 중, ingress Label은 additionalPodMonitors에 설정해서 강제로 추가한 설정이다.

# kubectl get ingress
NAME              CLASS     HOSTS   ADDRESS          PORTS   AGE
echo-server-ing   traefik   *       192.168.122.11   80      47h

ingress 데이터를 확인해도 수집된 ingress 레벨의 데이터와 동일하게 매핑된다.

위에서 설명했듯이 ingress를 추가하려면 resource에 ingress로 수집하도록 아래와 같이 추가한다.

prometheus-adapter-configmaps.yaml

apiVersion: v1
data:
  config.yaml: |
    rules:
    - seriesQuery: 'traefik_service_requests_total'
      seriesFilters: []
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
          ingress: {resource: "ingress"}
      name:
        matches: "^(.*)_total$"
        as: "${1}_sum_incr_1m"
      metricsQuery: "sum(increase(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)"
kind: ConfigMap
metadata:
  name: prometheus-adapter
  namespace: default

prometheus adapter에 적용 후, 수집된 메트릭을 확인해보면 아래와 같다.

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ |jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "pods/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "ingresses.networking.k8s.io/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "namespaces/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

ingress를 resurce로 수집된 데이터는 기존과 다르게 ingresses.networking.k8s.io/traefik_service_requests_sum_incr_1m 매핑되어 있다.

호출 주소는 아래와 같다.

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/ingresses.networking.k8s.io/echo-server-ing/traefik_service_requests_sum_incr_1m | jq
{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "describedObject": {
        "kind": "Ingress",
        "namespace": "default",
        "name": "echo-server-ing",
        "apiVersion": "networking.k8s.io/v1"
      },
      "metricName": "traefik_service_requests_sum_incr_1m",
      "timestamp": "2024-11-15T04:22:48Z",
      "value": "112",
      "selector": null
    }
  ]
}

 

prometheus-adapter  API 호출 주소 생성 룰

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ |jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "pods/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "ingresses.networking.k8s.io/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "namespaces/traefik_service_requests_sum_incr_1m",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

위와 같을 경우, 표시해주는 메트릭의 호출 주소는 "Resouce" / "메트릭 이름"으로 표시된다.

 

1. "namespaced": true 일경우, Resource 앞 뒤로, "/namespaces/[Resouce namespace]"/"Resouce"/"[Resouce Label Data]"가 붙는다.

2. Resource Case에 몇 가지 경우 [Resouce Label Data]을 asterisk(*)로 대체할 수 있다. (예 : pod, node)

 

pods/traefik_service_requests_sum_incr_1m 의 경우, 아래와 같은 메트릭이 사용되었다면

traefik_service_requests_total
{
  code="200", 
  container="traefik", 
  endpoint="metrics", 
  ingress="echo-server-ing", 
  instance="10.233.71.2:9100", 
  job="default/traefik-pod-monitor", 
  method="GET", 
  namespace="default", 
  pod="traefik-664df7d9f4-6rj5q", 
  protocol="http", 
  service="default-echo-server-svc-80@kubernetes"
}

메트릭 호출 주소에는 아래와 같은 정보가 사용되었다.

  • namespace -> default
  • pod -> traefik-664df7d9f4-6rj5q

[Resouce namespace] : default
Resouece : pod이나 pods로 수집 / pod, pods 모두 호출 가능
[Resouce Label Data] : traefik-664df7d9f4-6rj5q

"/namespaces/default/pods/traefik-664df7d9f4-6rj5q/traefik_service_requests_sum_incr_1m" 와 같이 호출하면 된다.

# kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/traefik-664df7d9f4-6rj5q/traefik_service_requests_sum_incr_1m | jq
{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "describedObject": {
        "kind": "Pod",
        "namespace": "default",
        "name": "traefik-664df7d9f4-6rj5q",
        "apiVersion": "/v1"
      },
      "metricName": "traefik_service_requests_sum_incr_1m",
      "timestamp": "2024-11-15T04:36:18Z",
      "value": "58",
      "selector": null
    }
  ]
}