Prometheus
개요
프로메테우스는 처음 사운드클라우드에서 만든 모니터링, 알람 시스템이다.
많은 회사들이 프메를 사용하면서 점차 관측 가능성 툴쪽에서는 거의 defacto가 돼버렸다.
이 친구.. 문서가 꽤나 잘 돼있다..!
쿠버네티스 환경에서는 쉽게 관련되는 리소스들을 한꺼번에 설치할 수 있다.[1]
자세한 건 Prometheus Operator 참고.
특징
프로메테우스는 여러 특징을 가지고 있다.
- 데이터를 Pull 방식으로 수집한다.
- 즉, 중앙 서버는 각지에 설치된 익스포터가 노출하는 경로로 요청을 날려 데이터를 가져온다.
- 중앙 서버가 경로를 노출하여 다른 곳에서 데이터를 Push 해주는 방식과는 다르기에 분산 환경에서 설정 관리에 용이하다.
- 자체로도 지표들을 확인하고 시각화할 수 있으나, 대체로 Grafana를 이용해 시각화 대시보드를 만들어 곁들여 사용한다.
- PromQL이라고 하는 데이터 쿼리 언어를 가지고 있다.
- 이걸 잘 활용하여 유의미한 데이터를 얻어내는 것이 엔지니어링 역량 중 하나라고 할 수 있다.
- 최소한의 기본기를 가지고 있으면 매우 도움이 되며, 관련한 자격증도 있을 정도니 잘 익혀두는 것이 좋다.
- TSDB
- Time Series DataBase, 즉 시계열 데이터를 저장하는 저장소를 가진다.
- 이를 통해 시간을 단위로 지속적으로 수집되는 로그, 메트릭 데이터를 효율적으로 저장한다.
- 이 저장소는 기본적으로 동적 확장이 지원되지 않으므로, Thanos와 같은 외부 스토리지 솔루션을 사용하는 것이 추천된다.
- 동적 재설정
SIGHUP
시그널이나,/-/reload
엔드포인트로 요청을 날려 변경된 설정을 동적으로 넣어줄 수 있다.
몇 가지 안 좋은 특징도 가지고 있다.
- 수평 확장이 용이하지 않다.
- 프로메테우스 서버가 동적 확장을 잘 지원하지 않는 듯하다.
- federation이라는 기능을 제공하나, 많이 쓰이진 않는 듯.
- 그래서 다른 서드파티 솔루션을 결국 많이 연계하게 된다.
데이터를 중앙에서 긁어오는 Pull 방식은 여러 장점이 있다.
중앙에서 주체적으로 데이터를 가져오기에 대상에 문제가 생겼을 때 즉각적으로 알아차릴 수 있다.
중앙에서 긁어올 대상을 추가하는 식으로 편하게 수집 대상을 조절할 수 있다.
어떤 데이터가 오는지 궁금하다면 관리자가 직접 프로메테우스 서버가 긁어오는 경로에 요청을 날려 데이터를 받아볼 수 있다.
구조
기본적으로 이런 구성으로 되어 있다.
- Prometheus server
- 데이터를 긁어오고 저장한다.
- TSDB를 사용하여 데이터를 효율적으로 저장한다.
- Exporter
- 중앙 서버가 긁어갈 데이터를 노출하는 서버
- 내가 수집하고자 하는 대상에 익스포터를 설치하면 된다.
- 노드의 정보를 얻고 싶으면 Node Exporter, 스프링이면 Spring Exporter.. 이런 식이다.
- Visualization
- 시각화 도구.
- 그라파나를 웹 대시보드로 많이 쓰지만 다른 클라이언트를 사용할 수도 있고 방식은 다양하다.
- Pushgateway
- Push 형식으로 짧게 실행해 데이터를 가져올 때 사용하는 방식.
- 배치를 통해 간혹 가져와야할 데이터라면 이걸 이용하면 된다.
- Alertmanager
- 알람을 해주는 기능 서버
- 그라파나를 시각화도구로 사용할 경우 그라파나 alert를 사용하기에, 거의 사용하지 않는다.
- Service Discovery
- 중앙 서버에서 데이터를 긁어올 곳을 동적으로 찾는 과정, 행위.
- 일반적으로 중앙 서버에서는 데이터를 어디에서 긁어올지 직접 설정해야 한다.
- 그러나 이 방식은 정적이고 귀찮으므로, 특정 조건을 걸어두어 그 조건에 맞게 긁어올 장소를 탐색하게 하는 기능이다.
설정
프로메테우스는 yaml 형식으로 설정을 적용한다.
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
# - "first.rules"
# - "second.rules"
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['localhost:9090']
global에는 전역적으로 적용될 설정을 지정한다.
rule을 통해 몇가지 조건과 규칙을 설정할 수 있다.
scrape_configs가 실제로 데이터를 가져올 익스포터의 경로를 설정하는 필드이다.
데이터를 긁어오는 각 작업의 단위는 job으로 규정되어 여디에서 긁어올지를 명시해주면 된다.
일반적으로 익스포터들은 9090포트에 /metrics 경로로 데이터를 노출하도록 돼있다.
데이터를 쿼리할 때, 기본 단위는 잡이다.
이 잡들을 묶어 또 그룹을 만들 수 있다.
관련 문서 참고하자.[2]
데이터
프로메테우스의 데이터 형식이 거의 표준화가 된 지금, 이 형식을 잘 아는 것이 또 하나의 관건이라고 할 수 있겠다.
형식
http_requests_total{method="GET", status="200"} 100 @ 1609746000.0
http_requests_total{method="GET", status="200", region="us-east-1"} 50 @ 1609746000.0
http_requests_total{method="POST", status="200"} 80 @ 1609746000.0
이게 프로메테우스 서버에 저장되는 데이터의 예시이다.
실제로 익스포터에서 데이터를 긁어올 때는 이런 식으로 타임스탬프가 빠져있는데, 프로메테우스 서버가 데이터를 긁어오는 시점에 타임스탬프가 지정되기 때문이다.
메트릭 이름 {라벨1="ㅇㅇ", 라벨2 = "ㅇㄹㅇㄹ"} 값 @ 타임스탬프
단순하게 보면 key value가 메트릭이름:값
인 거고, 거기에 특정 시간이 붙어있는 형태이다.
라벨은 해당 메트릭을 쉽게 분류하고 필터링하기 위해 붙는 값이다.
http_requests_total
이란 메트릭을 여러 개 수집하는데, 이때 어떤 것은 POST 데이터만 모은 놈일 수도 있고 어떤 놈은 status가 200인 놈들만 모았을 수도 있다.
이런 것들을 라벨로서 구체적으로 특정할 수 있게 해주는 것이다.
메트릭 이름을 나누어 표현하지 않고 같은 이름으로 표현하는 것인가?
- 다채롭고 유연하게 쿼리를 하고 유의미한 데이터를 뽑아내는데 도움이 된다.
- 유지보수와 확장성에 용이하다.
- 메트릭 이름이 많아지면 메트릭 자체가 늘어나고, 그만큼 부하가 커진다고 한다(이건 잘 모르겠다).
유형
일단 예시의 데이터는 Counter, 사진의 데이터는 Histogram인데, 이건 유형을 말한다.
프로메테우스에서 정의되는 지표에는 아래 4가지 유형이 존재한다.
Counter
계속 누적되면서 카운팅되는 데이터.
가령 서버가 받은 http 요청 총량 같은 것은 계속 늘어나기만 하는 데이터이다.
이런 것이 counter에 해당한다.
Gauge
단순 수치 데이터.
counter와 다르게 이 값은 오르내릴 수 있다.
대표적으로는 순간 메모리 사용량 같은 것이 있다.
Histogram
버킷이란 설정 단위를 여러 개 두고, 이로부터 분포를 구한 데이터.
분포뿐 아니라 총합을 나타낼 수도 있다.
가령 http 요청 처리에 걸린 시간을 이렇게 단위 별로 구분한다.
이 단위들이 버킷이 되며, 각 버킷은 개수가 누적된다.
이를 통해 히스토그램을 나타내는 것이다.
Summary
histogram과 유사한데, 조금 더 시간 단위로 요약된 데이터.
가령 전체 http 요청 중에서 중간 정도에 해당하는 값이라던가, 90분위수라던가.
아직 관련한 이해가 깊지 않아 여길 참고해야겠다.[3]
규칙(Rule)
프로메테우스는 두 가지 타입의 규칙을 지정할 수 있다.
룰이 뭔가 하니, 그냥 설정하는 개념 중 하나이다.
이 두가지 규칙을 보면 이해하기 편할 것이다.
Recording Rule
쿼리를 짜다보면 구문이 어마무시하게 길어질 수도 있고, 이로 인해 가독성이 떨어지게 될 수 있다.
또한 복잡한 쿼리를 매번 계산하는 것은 리소스 낭비이기도 하다.
중복되는 연산이라면, 캐시해두듯, 변수로 지정하듯 미리 연산 결과를 저장해서 활용하면 연산 수가 줄어들지 않겠는가?
이럴 때 일종의 alias처럼 사용할 수 있으면서 연산 결과를 TSDB에 저장하는 기능을 제공하는 것이 바로 Recording Rule이다.[4]
groups:
- name: example
rules:
- record: code:prometheus_http_requests_total:sum
expr: sum by (code) (prometheus_http_requests_total)
작성방식은 이렇게 된다.
중요하게 볼 지점은 record, expr인데, 전자는 이름, 후자는 실제 계산이라고 보면 되겠다.
<레벨>:<메트릭 이름>:<연산자>
와 같은 식으로 record 이름을 작성하라고 권장된다.
- Level
- 어느 정도 차원에서 이뤄지는 쿼리인지
- 클러스터 전체인지, 네임스페이스 별 인지 등등
- Metric
- 실제 메트릭 이름
- 가급적이면
_total
정도의 접미사 아니면 고유하게, 연산 관련은 빼는 게 좋다.
- Operations
- 적용될 연산자의 이름
- 가장 최종으로 적용되는 것을 넣는 게 아무래도 좋을 것이다.
여기에 웰 프렉티스가 있으니 참고한다.[5]
- record: instance_path:requests:rate5m
expr: rate(requests_total{job="myjob"}[5m])
- record: path:requests:rate5m
expr: sum without (instance)(instance_path:requests:rate5m{job="myjob"})
이런 식으로 위의 룰을 아래에서 쓸 수도 있다.
Alerting Rule
이건 알람을 전송하는 규칙에 대한 설정이다.
많이 쓰지 않는다고 하여 구태여 정리하지 않았다.
서비스 디스커버리(Service Discovery)
보통은 scrape 설정에 기본적으로 메트릭을 긁어올 경로와 대상을 지정해야 한다.
그러나 서비스가 점점 많아지고 복잡해진다면, 이마저도 어렵고 불편한 과정이 될 것이다.
이를 위해 프로메테우스에서는 특정한 설정을 잡아두면 그 설정에 맞게 알아서 메트릭을 긁어올 대상을 물색하는, 서비스 탐색 기능을 지원한다.[6]
기본적으로는 두 가지 형태의 디스커버리가 있다.
- File SD
- 파일의 내용을 긁어온다.
- 파일의 변경 사항(inotify)에 즉각적으로 반응
- yaml, json 형식 가능
- 로컬 환경의 데이터만 가능
- HTTP SD
- HTTP 통신을 통해 메트릭을 긁어온다.
- refresh 간격에 따라 탐색 진행
- json 형식
기본적인 이 두 방식을 기반으로, 다양한 시스템으로부터 데이터를 편하게 받을 수 있도록 여러 세부적인 설정을 지원하고 있다.
이런 식으로 여러 가지 대상이 가능하다.[7]
scrape 부분에 이 설정을 넣어주면 된다.
scrape_configs:
- job_name: "kubernetes-apiservers"
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# insecure_skip_verify: true
authorization:
credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels:
[
__meta_kubernetes_namespace,
__meta_kubernetes_service_name,
__meta_kubernetes_endpoint_port_name,
]
action: keep
regex: default;kubernetes;https
- job_name: "kubernetes-cadvisor"
kubernetes_sd_configs:
- role: node
scheme: https
metrics_path: /metrics/cadvisor
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
authorization:
credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
내가 좋아하는 쿠버네티스 관련 설정만 잠시 보자.
쿠버네티스 관련해서는 크게 아래 타입을 지정하여 탐색을 할 수 있다.
- node
- service
- pod
- enpoints
- enpoinslice
- ingress
각각은 메타 라벨을 가지고 있는데, 이들을 relabel_configs.source_labels
에 넣고, 매칭되는 값들을 대상으로 삼는다.
위의 예시에서는 endpoints 중 default 네임스페이스에 위치한 kubernetes 서비스의 https 엔드포인트를 대상으로 삼는 것이다.
이렇게 설정하면 해당 경로를 통해 http 요청을 날리고 /metrics에 경로로 노출된 값을 읽어오게 된다.
두번째 예시에서는 메트릭 경로까지 커스텀하는 모습이 보이며, action이 labelmap인데, 이것은 매칭되는 이름들을 다른 이름으로 변경시킬 때 쓴다.
relabel_configs
에서 액션에 따라 설정이 달라진다는 것을 참고하자.
PromQL
그렇게 수집된 데이터를 효과적으로 쿼리할 때는 Prometheus Query Language를 사용한다.
SQL처럼 엄청 복잡한 것은 아니고, 조회하는 것에만 특화된 언어라고 보면 되겠다.
기본적으로 차원에 따라 쿼리는 두 가지 유형을 가진다.
일단 하나의 메트릭을 스칼라(보통 샘플이라 한다)라고 쳤을 때,
Instant Query는 한 타임의 메트릭(인스턴트 벡터)들을 쿼리하는 것을 말하며, 기본적인 사용방법이다.
Range Query는 단위 시간 내의 메트릭을 쿼리하는 것을 말한다.
각 행이 시간 단위로 쪼개진 행렬이라 봐도 되지 않을까 싶었는데, 항상 같은 수의 샘플이 들어온다는 보장이 없다.
선형대수적 연산도 불가하기에 그냥 시계열 집합으로서 range vector로 표현하는 듯하다.
instant vector
http_requests_total{job="prometheus",group="canary", env!~"staging|testing"}
기본적으로 쿼리할 때는 이런 식으로 한다.
어떤 메트릭을 잡고, 그 뒤에는 {}
을 이용해 라벨 필터링을 건다.
{job=~".*"} # Bad!
참고로 이런 식으로 빈 문자열이 들어가도록 잡을 수는 없다.
최소한 하나의 문자라도 들어가게 +
라도 넣어야 한다.
{__name__=~"job:.*"}
메트릭 이름은 __name__
이란 예약어를 가지고 있어 이를 활용할 수 있다.
range vector
http_requests_total{job="prometheus"}[5m]
인스턴트 벡터에서 기간을 잡으면 range 벡터가 된다.
이때는 []
를 이용해 기간을 넣어주면 된다.
이건 현재 시점으로부터 5분전까지의 데이터를 모아서 출력한다.
변형자
sum(http_requests_total{method="GET"}[5m] offset 5m)
offset 변형자를 넣으면 잡히는 기간의 범위를 수정할 수 있다.
여기에서는 offset이 5분이므로, 5분 전부터 10분 전까지의 메트릭을 모으는 것이라 보면 되겠다.
참고로 -을 붙이는 게 가능한데, 이건 미래를 쿼리하는 꼴이다.
특정 과거시점에서 이걸 사용해서 메트릭을 내도록 설정하는 것이다.
sum(http_requests_total{method="GET"} @ 1609746000)
@를 사용하면 unix 시간 기준 특정 시점부터의 데이터를 모을 수 있게 된다.
2021-01-04T07:40:00+00:00
시점에서부터의 데이터를 뽑는 방식이다.
http_requests_total @ start()
rate(http_requests_total[5m] @ end())
start와 end는 내가 확인하고자 하는 시간 범위를 지정한 경우 그 시작점과 끝점을 나타낸다.
서브쿼리
<instant_query> '[' <range> ':' [<resolution>] ']' [ @ 변형 ] [ offset 변형 ]
서브쿼리는 하나의 쿼리 내용을 여러 시간에 걸쳐서 시행하는 것을 말한다.
[ : ]
가 서브쿼리를 하는 문법이며, range에는 전체 시간 범위, resolution은 간격을 의미한다.
rate(http_requests_total[5m])[30m:1m]
이렇게 하면 5분간의 초당 http 요청 평균을, 1분 간격으로 30번 쿼리하는 것이다.
연산자
비교 연산자는 크게 4가지가 있는데, 정규식을 쓸때는 뒤에 ~을 붙인다.
논리 연산자에서 unless는 차집합이다.
지피티한테 물어봤다.
이 경우 unless는 중간값만 뺀 결과를 보여주게 된다.
집계 함수이다.
라벨을 통해 빼고 싶은 값들을 필터링할 수 있는데, 이때는 without|by
를 사용한다.
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
parameter는 count_values, topk
등에 필요한 추가 인자이다.
without은 특정 라벨로 제외를 시킬 때, by는 특정 라벨이 있는 놈만 고를 때 사용한다.
함수
https://prometheus.io/docs/prometheus/latest/querying/functions/
관련 문서
이름 | noteType | created |
---|---|---|
2024-04-07(일) | - | 2024-06-13 |
Prometheus | knowledge | 2025-02-26 |
Prometheus-Adapter | knowledge | 2025-03-04 |
Prometheus Operator | knowledge | 2025-03-30 |
4주차 - 관측 가능성 | project | 2025-02-23 |
4W - 프로메테우스 스택을 통한 EKS 모니터링 | published | 2025-02-28 |
4W - 이스티오 메트릭 커스텀, 프로메테우스와 그라파나 | published | 2025-05-03 |
6W - 이스티오 컨트롤 플레인 성능 최적화 | published | 2025-05-18 |
7W - 이스티오 메시 스케일링 | published | 2025-06-09 |
E-이스티오 컨트롤 플레인 성능 최적화 | topic/explain | 2025-05-18 |
E-이스티오 메시 스케일링 | topic/explain | 2025-06-08 |
참고
https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack ↩︎
https://prometheus.io/docs/prometheus/latest/configuration/configuration/ ↩︎
https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/ ↩︎
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config ↩︎