
개요
회사에서 모니터링 프로젝트를 하던 중 추이 분석 관련 그래프, 언제 트래픽이 있었는지 등 시계열 데이터를 조회하고 분석해야하는 상황이 있었습니다.
저희 프로젝트는 sql-exporter를 사용해서 메트릭 데이터를 프로메테우스에 저장하고있어 이 메트릭 데이터를 효과적으로 질의할 수 있어야했습니다.
이때 활용했던 방법이 PromQL인데, 이번 글에서는 PromQL란 무엇인지, 주요 용도, 주요 사용되는 내장 함수 등을 정리하고자 합니다.
PromQL (Prometheus Query Language)이란❓
PromQL이란?
- Prometheus 모니터링 시스템에서 시계열 데이터를 조회하고 분석하기 위한 전용 쿼리 언어입니다.
- SQL과 유사하게 데이터를 질의하지만, 시계열 데이터에 특화되어 있어 시간의 흐름에 따른 메트릭 변화를 효과적으로 분석할 수 있습니다.
- 즉, Promethues에서는 PromQL을 이용해서 TSDB(Time Series Database)를 제어할 수 있습니다.
데이터 구조
Prometheus는 데이터를 저장할 때 다음과 같은 형식을 사용합니다.
metric_name{label1 = "value1", label2 = "value2",...}
- 메트릭 이름(Metric Name)
- 측정하려는 값의 본질 (예: cpu_usage - 전체 CPU 사용량)
- 레이블(Labels)
- 그 값의 구체적인 속성 (예: method="GET", status="200", instance="server-01")
레이블을 활용한 실제 예시
만약 웹 서버의 에러율을 분석하고 싶다면, 레이블을 통해 다음과 같이 필터링하거나 그룹화할 수 있습니다.
| 사용 목적 | PromQL 예시 | 설명 |
| 특정 데이터 필터링 | http_requests_total{status="500"} | 상태 코드가 500(에러)인 데이터만 골라냅니다. |
| 다중 조건 필터링 | http_requests_total{method="POST", env="prod"} | 운영 환경(prod)이면서 POST 방식인 데이터만 조회합니다. |
| 차원별 그룹화(집계) | sum by (method) (http_requests_total) | 모든 서버의 요청을 '메소드별'로 합산하여 보여줍니다. |
PromQL의 주요 특징
1. 시계열 데이터 특화
- 시간에 따라 변하는 데이터를 자연스럽게 다룰 수 있습니다.
2. 표현식 기반
- 함수형 프로그래밍 스타일로 복잡한 쿼리를 간결하게 작성할 수 있습니다
3. 레이블 기반 필터링
- 다차원 데이터를 레이블을 통해 유연하게 필터링하고 그룹화할 수 있습니다.
다차원 데이터 레이블이란❓
- 레이블은 메트릭에 붙는 키-값(Key-Value) 쌍으로, 데이터의 개별 속성을 정의하는 다차원 식별자 역할을 합니다.
- 동일한 메트릭(예: http_requests_total)이라도 레이블(예: method, status, service)에 따라 데이터를 다각도로 쪼개거나 합쳐서 분석할 수 있습니다.
4. 실시간 쿼리
- 실시간으로 데이터를 조회하고 계산할 수 있어 즉각적인 모니터링이 가능합니다
PromQL의 장단점
장점
1. 강력한 시계열 데이터 분석 능력
- 시간의 흐름에 따른 데이터 변화를 쉽게 추적하고 분석할 수 있습니다.
- 증가율, 평균, 백분위수 등 다양한 통계를 간단한 함수로 계산할 수 있습니다.
2. 직관적인 레이블 기반 필터링
- 여러 차원의 데이터를 레이블을 통해 자유롭게 필터링하고 그룹화할 수 있어, 복잡한 조건의 데이터도 명확하게 조회할 수 있습니다.
3. 풍부한 내장 함수
- 집계, 변환, 예측 등 다양한 내장 함수를 제공하여 복잡한 계산도 간단하게 수행할 수 있습니다.
4. 시각화 도구와의 뛰어난 통합
- Grafana, Prometheus UI 등 주요 모니터링 대시보드 도구들과 완벽하게 통합되어 즉시 시각화가 가능합니다.
5. 알림 규칙 설정 용이
- 임계값 기반 알림을 쉽게 설정할 수 있어, 시스템 이상 징후를 자동으로 감지할 수 있습니다.
6. 클라우드 네이티브 생태계 표준
- Kubernetes, Docker 등 현대적인 인프라 환경에서 널리 채택된 표준 쿼리 언어입니다.
단점
1. 초기 학습 곡선
- SQL에 익숙한 사용자라도 시계열 데이터 특유의 개념과 문법을 새로 학습해야 합니다.
2. SQL과의 문법 차이
- 기존 SQL 지식을 직접 활용하기 어렵고, 새로운 문법 체계에 적응이 필요합니다.
3. 복잡한 쿼리의 성능 이슈
- 여러 메트릭을 조인하거나 복잡한 계산을 수행할 때 성능 저하가 발생할 수 있습니다.
4. 장기 데이터 보관의 어려움
- 시계열 데이터의 특성상 데이터가 빠르게 증가하여 스토리지 관리와 비용 고려가 필요합니다.
5. 제한적인 데이터 타입
- 주로 숫자형 메트릭 데이터만 다루며, 문자열이나 복잡한 데이터 구조 처리에는 제약이 있습니다.
PromQL의 데이터 타입
PromQL에서 다루는 데이터는 크게 4가지 타입으로 분류됩니다. 각 타입을 정확히 이해하면 복잡한 쿼리도 쉽게 작성할 수 있습니다.
1. Instant Vector
- 정의
- 같은 시점(동일한 타임스탬프)을 가진 시계열 데이터의 집합입니다.
- 즉, 여러 시계열의 "현재 순간" 값들을 모아놓은 것입니다.
- 특징
- 각 시계열마다 가장 최근의 단일 값만을 가집니다
- 같은 타임스탬프를 공유하는 여러 시계열로 구성됩니다
- 그래프(Graph) 뷰에서 시간 흐름에 따라 선으로 표시됩니다
- 대부분의 PromQL 쿼리가 Instant Vector를 반환합니다
- 레이블이 다르면 각각 별도의 시계열로 취급됩니다
- 기본 예시
# 모든 HTTP 요청 메트릭의 현재 값
http_requests_total
# 레이블 필터링
http_requests_total{method="GET", status="200"}
2. Range Vector
- 정의
- 특정 시간 범위 동안 기록된 시계열 데이터의 집합입니다.
- 특징
- 현재를 기준으로 과거 일정 기간의 데이터를 배열 형태로 가집니다
- [5m], [1h], [30d] 같은 시간 범위 선택자를 사용합니다
- 직접 그래프로 표시할 수 없습니다
- 주로 rate(), increase(), avg_over_time() 같은 함수의 입력값으로 사용됩니다
- 시간 범위 표기법
- s - 초 (seconds)
- m - 분 (minutes)
- h - 시간 (hours)
- d - 일 (days)
- w - 주 (weeks)
- y - 년 (years)
- 기본 예시
# 최근 5분간의 HTTP 요청 데이터
http_requests_total[5m]
# 최근 1시간의 CPU 사용 데이터
cpu_usage_seconds[1h]
3. Scalar (스칼라)
- 정의
- 시간 정보나 레이블 정보가 없는 단순한 숫자(부동 소수점) 값입니다.
- 특징
특정 시계열에 속하지 않는순수한 숫자입니다타임스탬프나레이블을 가지지 않습니다- 쿼리 내에서 상수로 사용되거나 연산에 활용됩니다
- 일부 함수는 Scalar 값을 반환합니다
- 기본 예시
# 리터럴 숫자
100
3.14
# 연산에서 활용
memory_used_bytes / 1024 / 1024 # MB 단위 변환
cpu_usage_percent > 80 # 임계값 비교
4. String (문자열)
- 정의
- 단순한 문자열 값입니다.
- 특징
- 현재 PromQL에서는 매우 제한적으로 사용됩니다
- 주로 함수의 인자나 레이블 값으로만 사용됩니다
메트릭 값으로는 사용할 수 없습니다 (Prometheus는 숫자형 데이터만 저장)연산에 직접적으로 사용되지 않습니다
- 기본 예시
# 레이블 필터링에서 사용
http_requests_total{method="GET"}
# 정규표현식 매칭
http_requests_total{status=~"5.."}
PromQL의 주요 내장 함수
1. 증가율 계산 함수
rate() : 지정된 시간 범위 동안의 초당 평균 증가율을 계산
입력: Range Vector ➡️ 출력: Instant Vector
# 최근 5분간 초당 평균 요청 수
rate(http_requests_total[5m])
# 엔드포인트별 초당 요청 수
rate(http_requests_total[5m]) by (endpoint)
- 사용 경우
- Counter 타입 메트릭의 증가율 계산
- 알림 규칙 설정
- 장기 추세 파악
irate() : 현재 순간의 즉시 증가율을 계산
# 현재 순간의 초당 요청 수
irate(http_requests_total[5m])
- 현재 순간의 즉시 증가율을 계산합니다. 단, 최근 두 데이터 포인트만 사용됩니다.
- rate() vs irate()
- rate() : 완만한 그래프, 평균값, 알림용
- irate() : 민감한 그래프, 즉각 반응, 실시간 모니터링용
increase() : 지정된 시간 범위 동안의 총 증가량을 계산
# 최근 1시간 동안의 총 요청 수
increase(http_requests_total[1h])
2. 집계 함수
sum() : 메트릭 값들의 합계 계산
입력: Instant Vector ➡️ 출력: Instant Vector (또는 Scalar)
# 모든 인스턴스의 총 요청 수
sum(rate(http_requests_total[5m]))
# 엔드포인트별 총 요청 수
sum(rate(http_requests_total[5m])) by (endpoint)
# instance 레이블을 제외하고 합계
sum(rate(http_requests_total[5m])) without (instance)
- 그룹화
- by (label): 지정한 레이블로만 그룹화
- without (label): 지정한 레이블을 제외하고 그룹화
avg(), max(), min() : 평균, 최대값, 최소값을 계산
# 평균 CPU 사용률
avg(cpu_usage_percent) by (instance)
# 최대 메모리 사용량
max(memory_usage_bytes) by (instance)
# 최소 디스크 여유 공간
min(disk_free_bytes) by (mount_point)
count() : 메트릭 개수 계산
입력: Instant Vector ➡️ 출력: Instant Vector (또는 Scalar)
# 정상 상태인 인스턴스 수
count(up == 1)
# 80% 이상 CPU를 사용하는 서버 개수
count(cpu_usage_percent > 80)
topk(), bottomk() : 상위/하위 k개의 시계열 선택
# CPU 사용률 상위 5개 인스턴스
topk(5, cpu_usage_percent)
# 메모리 사용량 하위 3개 서버
bottomk(3, memory_usage_bytes)
3. 시간 범위 함수
avg_over_time() : 시간 범위 내의 평균값을 계산
입력: Renge Vector ➡️ 출력: Instant Vector (또는 Scalar)
# 최근 5분간 평균 CPU 사용률
avg_over_time(cpu_usage_percent[5m])
# 최근 1시간 평균 응답 시간
avg_over_time(http_response_time_seconds[1h])
max_over_time(), min_over_time() : 시간 범위 내의 최대/최소값 찾기
# 최근 1시간 최대 CPU 사용률 (피크)
max_over_time(cpu_usage_percent[1h])
# 최근 5분간 최대 응답 시간
max_over_time(http_request_duration_seconds[5m])
4. 백분위수 계산 함수
histogram_quantile() : 히스토그램 데이터에서 특정 백분위수 계산
입력: Scalar(분위수), Instant Vector(히스토그램) ➡️ 출력: Instant Vector
# 95 백분위수 응답 시간
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# 99 백분위수 응답 시간
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# 엔드포인트별 95 백분위수
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, endpoint)
)
- le, endpoint란?
- 히스토그램의 각 구간 상한값을 나타내는 특수 레이블입니다. (0.1초, 0.5초, 1.0초 등).
- histogram_quantile()은 이 `le` 값들을 보고 백분위수를 계산하므로, `sum`으로 집계할 때 `le`를 제거하면 백분위수 계산이 불가능합니다.
5. 시간 관련 함수
offset : 특정 시간만큼 과거의 데이터 조회
# 1시간 전 데이터
http_requests_total offset 1h
# 어제 같은 시간 데이터
http_requests_total offset 1d
# 현재와 1시간 전 비교
rate(http_requests_total[5m]) / rate(http_requests_total[5m] offset 1h)
6. 예측 함수
predict_linear() : 선형 회귀를 사용하여 미래 값을 예측
# 4시간 후 디스크 사용량 예측
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600)
# 디스크가 가득 찰지 예측
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600) < 0
7. 변환 함수
scalar() : 단일 값을 가진 Instant Vector를 Sclar로 변환
# Vector를 Scalar로 변환
scalar(sum(up))
# 조건부 비교에 활용
http_requests_total > scalar(sum(threshold_value))
PromQL의 주요 내장 함수 활용 예시
예시 1 : API 에러율 모니터링
# 엔드포인트별 5xx 에러율
sum(rate(http_requests_total{status=~"5.."}[5m])) by (endpoint)
/
sum(rate(http_requests_total[5m])) by (endpoint)
* 100
사용 함수: rate(), sum(), by()
타입 흐름
- http_requests_total[5m] → Range Vector
- rate(...) → Instant Vector
- sum(...) by (endpoint) → Instant Vector
- 나누기 → Instant Vector
- * 100 → Instant Vector (에러율 %)
예시 2 : CPU 사용률 Top5
# CPU 사용률 상위 5개 서버
topk(5,
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
)
사용 함수: rate(), avg by(), topk()
타입 흐름
- Range Vector → rate() → Instant Vector
- avg by (instance) → Instant Vector
- 100 - ... → Instant Vector (CPU 사용률)
- topk(5, ...) → Instant Vector (상위 5개)
예시 3 : 트래픽 급증 감지
# 1시간 전 대비 트래픽 2배 이상 증가
sum(rate(http_requests_total[5m]))
/
sum(rate(http_requests_total[5m] offset 1h))
> 2
사용 함수: rate(), sum(), offset
타입 흐름
- Range Vector → rate() → Instant Vector → sum() → Scalar
- offset 1h → 1시간 전 Scalar
- 나누기 → Scalar
- > 2 → Boolean
예시 4 : 응답 시간 비교 (오늘 vs 어제)
# 오늘과 어제 p95 응답 시간 차이
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
-
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m] offset 1d))
사용 함수: rate(), histogram_quantile(), offset
타입 흐름
- Range Vector → rate() → Instant Vector
- histogram_quantile() → Instant Vector (p95)
- offset 1d → 어제 데이터
- 빼기 → Instant Vector (차이)
데이터 타입 간 관계
1. Range Vector ➡️ Instant Vector 변환
# Range Vector (그래프 표시 불가)
# 최근 5분간의 모든 데이터 포인트를 배열로 가짐
http_requests_total[5m]
# Instant Vector로 변환 (그래프 표시 가능)
rate(http_requests_total[5m]) # 초당 평균 증가율 계산
increase(http_requests_total[5m]) # 총 증가량 계산
avg_over_time(cpu_usage[5m]) # 5분간 평균값 계산
max_over_time(memory_usage[1h]) # 1시간 내 최대값 계산
Range Vector는 반드시 함수를 통해 Instant Vector로 변환해야 합니다.
왜냐하면, Range Vector는 시간 범위 내 여러 개의 값(배열)을 가지므로 직접 그래프로 표시할 수 없습니다. 함수를 통해 이 값들을 하나의 값으로 계산해야 시각화가 가능합니다.
2. Instant Vector ➡️ Scalar 변환
- scalar() 함수 사용
- 사용 시기
- 단일 시계열을 숫자 값으로 변환하여 다른 계산에 활용할 때
# Instant Vector → Scalar
scalar(sum(up))
# 활용 예시: Scalar를 임계값으로 사용
http_requests_total > scalar(sum(threshold_value))
주의‼️: scalar() 함수는 정확히 1개의 시계열만 가진 Vector에만 사용 가능합니다. 여러 시계열이 있으면 에러 발생
- 레이블 없이 집계
sum(http_requests_total) # → Scalar
avg(cpu_usage_percent) # → Scalar
count(up == 1) # → Scalar
레이블을 지정하지 않고 집계하면 모든 시계열이 하나로 합쳐져 자동으로 Scalar가 됩니다.
3. Scalar와 Vector 연산
Scalar는 Vector의 각 시계열에 자동 적용됩니다.
Vector와 Scalar를 연산하면, Scalar 값(숫자)이 Vector의 모든 시계열에 동일하게 적용됩니다. 마치 반복문처럼 각 시계열마다 같은 연산을 수행하는 것과 같습니다.
# 모든 시계열에 100 곱하기
cpu_usage * 100
# bytes를 MB로 변환
disk_usage_bytes / 1024 / 1024
# 임계값 비교
cpu_usage_percent > 80
활용 예시
# 메모리 사용률을 퍼센트로 변환
(memory_used_bytes / memory_total_bytes) * 100
# 각 서버마다 (사용량 / 전체) * 100 계산
# 모든 응답 시간을 밀리초로 변환
http_response_time_seconds * 1000
# 각 엔드포인트의 응답 시간에 1000을 곱함
핵심은 바로 Scalar 하나가 Vector의 모든 시계열에 브로드캐스팅(broadcasting)된다는 것 입니다.
'Monitoring' 카테고리의 다른 글
| [Grafana] Grafana Loki란 무엇일까❓개념부터 로그 확인까지 (0) | 2024.08.19 |
|---|---|
| [Grafana] Grafana와 Slack 연동하여 Alert 설정하기 (0) | 2024.08.19 |
| [Grafana] Prometheus와 Grafana로 데이터 시각화하기: Docker 환경에서의 설정 및 대시보드 작성 (0) | 2024.08.19 |
| [Prometheus] Prometheus란 무엇일까❓(Spring Boot 애플리케이션의 메트릭 모니터링) (0) | 2024.08.19 |