Prometheus-Adapter
개요
프로메테우스에서 제공하는 메트릭 서버 확장을 위한 어댑터.
프로메테우스 서버에서 데이터를 가져오고, 이를 metric api로 노출한다.
결과적으로 프로메테우스의 데이터들을 HPA에 활용할 수 있게 된다.
기본 메트릭api에서는 노출하는 메트릭이 cpu, 메모리밖에 없기 때문에 이를 통해 메트릭을 확장하는 것이 매우 유용하다.
구조
프로메테우스 어댑터가 하는 일에 집중해서 보자.
어떤 식으로든, 프로메테우스는 각종 메트릭을 수집하고 쿼리를 할 수 있게 노출하고 있다.
이때 어댑터가 프로메테우스 서버에 접속해 쿼리를 통해 메트릭을 긁어온다.
이제 이 어댑터는 데이터를 뿌릴 준비가 된 것이다.
그런데 HPA#메트릭 수집을 보면 알 수 있듯이 HPA는 계산에 사용할 메트릭을 kube-apiserver의 특정 경로만을 이용한다.
이를 위해 API Aggregation Layer를 이용해 저 메트릭들이 api 서버의 특정 경로를 통해 노출되도록 만드는 것이다.
그리고 HPA에서 이를 가져오도록 설정만 하면, 해당 메트릭을 기준으로 스케일링이 가능해진다!
설치
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm show values >> values.yaml
# values.yaml에서 프메 서버 url 설정
helm install my-release prometheus-community/prometheus-adapter
Helm을 통해 쉽게 어댑터를 배포할 수 있다.
주의할 것은 어댑터가 긁긁할 프로메테우스 서버의 주소를 잘 적어줘야 한다는 것.
(공식 문서에서 이걸 바로 말해주지 않아서, 처음에 순간 헤맸다...)
헬름에서는 알아서 관련 오브젝트를 전부 만들어주기에 편리하다.
하지만 그렇게까지 이게 어려운 것도 아니라, 직접 일일히 오브젝트들을 만들어 적용해볼 수도 있다.[1]
ConfigMap 작성
아래의 예제 코드들에는 원래 꺽쇠 기호가 들어가 있다.
그러나 현재 내 블로그를 웹사이트 정적 파일로 변환하는 플러그인에서 해당 기호를 템플릿 엔진의 변수로 인식하여 에러가 발생하고 있다.
그래서 당장은 어쩔 수 없이 예제 코드에서 꺽쇠 기호 표기를 // //
와 같은 식으로 변경하여 코드를 담는다.
rules:
- seriesQuery: '{__name__=~"^container_.*",container!="POD",namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^container_(.*)_seconds_total$"
as: "$1_per_second"
metricsQuery: "sum(rate(//.Series//{//.LabelMatchers//,container!="POD"}[2m])) by (//.GroupBy//)"
아무 데이터나 죄다 긁어오지 않고, 특정 룰에 기반해 필요한 데이터만 긁어오도록 할 수 있다.
참고로 이미 배포한 이후에 이걸 수정한다면 반드시 어댑터 파드를 재시작시켜줘야 한다.
얘도 config reloader 만들어줘 빼액
본격적으로 이걸 어떻게 작성해야 하는지 보자.[2]
위의 설정은 총 4가지 섹션으로 구분된다.
Discovery
이 룰에 매칭돼야 하는 모든 프로메테우스 메트릭을 탐색하는 단계이다.
seriesQuery: '{__name__=~"^container_.*_total",container!="POD",namespace!="",pod!=""}'
seriesFilters:
- isNot: "^container_.*_seconds_total"
---
seriesQuery: 'http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}'
seriesQuery
필드를 통해 어떤 메트릭을 불러올지를 먼저 결정한다.
여기는 PromQL 작성하듯이 작성해주면 된다.
이때 조금 더 세부적으로 필터를 걸 때 seriesFilters
를 이용해 is
, isNot
필드를 넣어줄 수도 있다.
Association
resources:
template: "kube_<\<.Group>\>_<\<.Resource>\>"
---
resources:
overrides:
microservice: {group: "apps", resource: "deployment"}
불러온 메트릭이 쿠버네티스의 어떤 리소스에 매칭되는지 지정하는 단계로, resource
필드를 이용한다.
이걸 지정하기에 최종 api 조회를 할 때 상세 경로를 통해 원하는 리소스만 매칭하여 조회할 수 있게 된다.
가령 /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/test/어떤 메트릭
이런 식으로 특정 네임스페이스의 특정 파드 메트릭을 조회할 때 활용할 수 있다는 말이다.
resources:
overrides:
service:
resource: service
여기에서 매칭하는 것은 메트릭의 라벨로, 메트릭 라벨에 service
라는 것이 있고 이것에 쿠버 서비스와 같은 값이라면, 이런 식으로 작성하면 된다.
즉 메트릭 라벨과 쿠버 리소스를 매칭시키는 단계라고 보면 되겠다.
보통은 이렇게 overides
를 이용하면 된다.
template
필드를 활용해서 // //
를 쓰는 특이한 문법이 보이는데, 이것은 Go 템플릿으로 매칭을 시키는 방식이다.
//.Group//
으로 apiGroup, //.Resource//
로 리소스 이름을 매칭시킬 수 있다.
그래서 위의 예시의 경우 "kube_v1_pod"라는 메트릭 라벨이 있었다면 이것은 파드와 매칭이 되었을 것이다.
Naming
name:
matches: "^(.*)_total$"
as: "${1}_per_second"
해당 룰이 커스텀 메트릭 api로서 어떤 이름을 가질 것인지 지정하는 단계이다.
먼저 메트릭 이름을 matches
를 이용해 매칭을 한다.
여기에서 Regex로 그룹을 지정하고나면, as
에서 메트릭 이름으로 위와 같이 활용할 수 있다.
as
를 넣지 않을 경우 기본적으로 첫번째로 지정한 Regex 그룹이 이름이 된다.
Querying
metricsQuery: "sum(rate(//.Series//{//.LabelMatchers//,container!="POD"}[2m])) by (//.GroupBy//)"
이 api를 호출했을 때 어떤 식의 쿼리로서 반환돼야 하는지 지정하는 단계이다.
여기에서도 PromQL을 작성하듯이 해주면 되는데, 이번에도 특이한 // //
들이 눈에 띈다.
이것들을 통해 특정 경로를 통해 요청이 들어왔을 때 각각 다른 방식으로 응답을 줄 수 있게 된다.
- Series - 그냥 [[#Discovery]] 단계에서 가져온 메트릭 이름이다.
- LabelMatchers - 오브젝트와 매칭되는 라벨 리스트인데, 실질적으로 [[#Association]]에서 본 Group, Resource가 여기에 쓰인다.
- 만약 해당 오브젝트가 네임스페이스 소속이라면 여기에 namespace도 같이 들어간다.
- GroupBy - LabelMatchers의 리스트로 그룹바이하기 위한 라벨 리스트이다.
처음에 너무 생소하게 생겨서 잘 이해못했는데, 그냥 각 단계에서만 사용할 수 있는 특별 템플릿이 있다고만 생각하면 되겠다.
metric{service="dd", pod="tt", namespace="ee", verb="GET"}
이런 메트릭이 있다쳐보자.
이때 HPA에서 ee 네임스페이스의 파드들에 대한 메트릭 api를 요청한다고 치면, 각 템플릿 값들은 다음과 같이 매칭된다.
(/apis/custom.metrics.k8s.io/v1beta1/namespaces/ee/pods/*/metric
으로 요청이 들어온 상황)
- Series - metric
- LabelMatchers -
- GroubBy - pod
처음에는 왜 이렇게 복잡하게 설정해야 하나 싶었는데, 다시 보니 최소한의 룰 지정으로 여러 메트릭을 불러오면서도 다양한 경로로 메트릭을 노출할 수 있는 방법인 것 같다.
다만 나중에는 다른 방식으로 문법이 바뀔 수도 있지 않을까 하는 생각은 조금 든다.
LabelValuesByName
: a map mapping the labels and values from theLabelMatchers
field. The values are pre-joined by|
(for used with the =~ matcher in Prometheus).GroupBySlice
: the slice form ofGroupBy
.
이런 추가적인 방식도 있기는 하다는데, 거의 쓰이지 않는다고 봐도 무방하다(고 한다).
메트릭 조회
나는 이런 식으로 규칙을 작성했다.
파드 단위로 오고가는 패킷 수를 메트릭으로 노출한 것이다.
k get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/packets_per_second" | jq
간단하게 kubectl로 이것을 조회해보면 이런 식으로 조회되는 것을 확인할 수 있다!
- type: Pods
pods:
metric:
name: packets_per_second
target:
type: AverageValue
averageValue: 1000m
HPA에서는 이런 식으로 써주면 된다.
이름이 packets_per_second였으니 이걸 잘 맞춰서 써준다.
관련 문서
이름 | noteType | created |
---|---|---|
Prometheus-Adapter | knowledge | 2025-03-04 |