이번 글에서는 프로메테우스와 그라파나를 활용한 Observability 구성 시리즈의 세 번째 포스트로, 메트릭 유형에 대한 심층적인 이해와 시계열 데이터의 기초 개념에 대해 알아보겠습니다.
📌 시계열 데이터의 이해
시계열 데이터는 시간의 흐름에 따라 수집된 데이터 포인트들의 집합입니다. Observability 영역에서 메트릭은 대부분 시계열 형태로 저장되고 분석됩니다.
✅ 시계열 데이터의 특징
- 타임스탬프: 각 데이터 포인트는 정확한 발생 시간과 함께 기록됩니다.
- 순차적 정렬: 시간 순서대로 정렬되어 추세와 패턴을 볼 수 있습니다.
- 규칙적 간격: 일반적으로 일정한 간격(예: 10초, 1분)으로 수집됩니다.
- 변화 추적: 시간에 따른 값의 변화를 추적하여 동향을 파악할 수 있습니다.
✅ 시계열 데이터베이스
시계열 데이터는 일반적인 관계형 데이터베이스(RDBMS)와는 다른 특성을 가지고 있어, 이를 효율적으로 저장하고 관리하기 위한 특수한 데이터베이스가 필요합니다.
▶️ 시계열 데이터베이스의 특징:
- 높은 쓰기 처리량: 초당 수천, 수만 개의 데이터 포인트를 기록할 수 있습니다.
- 효율적인 압축: 시간에 따른 변화 패턴을 활용하여 데이터를 효율적으로 압축합니다.
- 시간 기반 쿼리 최적화: 특정 시간 범위에 대한 쿼리가 매우 빠릅니다.
- 다운샘플링: 오래된 데이터는 낮은 해상도로 압축하여 저장 공간을 절약합니다.
▶️ 주요 시계열 데이터베이스:
- 프로메테우스(Prometheus): 모니터링 중심의 시계열 데이터베이스
- 인플럭스DB(InfluxDB): 고성능 시계열 데이터베이스
- 타이머스케일DB(TimescaleDB): PostgreSQL 확장 기반 시계열 데이터베이스
- 그라파나 로키(Grafana Loki): 로그 중심 시계열 데이터베이스
- 오픈TSDB(OpenTSDB): HBase 기반 분산 시계열 데이터베이스
📌 프로메테우스의 데이터 모델
프로메테우스는 강력한 시계열 데이터베이스를 내장하고 있으며, 고유한 데이터 모델을 가지고 있습니다.
✅ 프로메테우스 데이터 모델의 구성요소
▶️ 메트릭 이름과 레이블:
프로메테우스의 시계열 데이터는 메트릭 이름과 키-값 쌍의 레이블 집합으로 식별됩니다.
<metric_name>{<label_name>=<label_value>, ...}
예시:
http_requests_total{method="GET", status="200", path="/api/users"}
- 메트릭 이름: 측정 중인 항목을 설명합니다(예: http_requests_total)
- 레이블: 메트릭의 차원을 식별합니다(예: method="GET")
▶️ 샘플:
시계열의 실제 데이터 포인트는 다음 요소로 구성됩니다:
- 부동 소수점 값
- 밀리초 정밀도의 타임스탬프
✅ 레이블과 차원
프로메테우스에서 레이블은 시계열 데이터에 차원을 추가하는 매우 중요한 개념입니다.
▶️ 레이블의 중요성:
- 다차원 데이터 모델: 동일한 메트릭에 대해 다양한 차원의 데이터를 구분할 수 있습니다.
- 유연한 쿼리: 레이블을 기반으로 필터링, 집계, 조인 등의 연산을 수행할 수 있습니다.
- 동적 대상 추적: 레이블을 통해 동적으로 변화하는 환경(예: 쿠버네티스 포드)을 효과적으로 추적할 수 있습니다.
▶️ 레이블 명명 규칙:
- 레이블 이름은 문자, 숫자, 언더스코어(_)로 구성됩니다.
- 레이블 이름은 문자나 언더스코어로 시작해야 합니다.
- 레이블 값은 어떤 유니코드 문자열이든 가능합니다.
▶️ 효과적인 레이블 사용 사례:
- 인스턴스 식별: instance="web-server-01"
- 서비스 구분: service="payment-api"
- 환경 구분: environment="production"
- 지역 정보: region="us-west"
- 팀 소유권: team="platform"
# 프로메테우스 대상 설정 예시 (prometheus.yml)
scrape_configs:
- job_name: 'api_servers'
static_configs:
- targets: ['api-server-01:9090', 'api-server-02:9090']
labels:
environment: production
service: user-api
tier: frontend
- targets: ['payment-service-01:9090', 'payment-service-02:9090']
labels:
environment: production
service: payment-api
tier: backend
📌 시계열 데이터 쿼리 기초
프로메테우스의 강력한 기능 중 하나는 PromQL(Prometheus Query Language)이라는 쿼리 언어입니다. PromQL을 사용하면 시계열 데이터를 효과적으로 검색하고 조작할 수 있습니다.
✅ 기본 쿼리 구조
▶️ 단순 쿼리:
특정 메트릭의 모든 시계열을 검색합니다.
http_requests_total
▶️ 레이블 필터링:
특정 레이블이 있는 시계열만 필터링합니다.
# method가 "GET"인 HTTP 요청만 선택
http_requests_total{method="GET"}
# status가 "5xx"로 시작하는 HTTP 요청만 선택
http_requests_total{status=~"5.."}
# env가 "dev"가 아닌 HTTP 요청만 선택
http_requests_total{env!="dev"}
▶️ 범위 벡터 선택:
특정 시간 범위의 데이터를 선택합니다.
# 지난 5분간의 HTTP 요청 데이터
http_requests_total[5m]
✅ 연산자와 함수
PromQL은 시계열 데이터를 조작하기 위한 다양한 연산자와 함수를 제공합니다.
▶️ 산술 연산자:
# 각 시계열 값에 2를 곱함
http_requests_total * 2
# 두 메트릭의 비율 계산
http_requests_total / http_requests_successful
▶️ 집계 함수:
# method 레이블별로 HTTP 요청 수 합계
sum(http_requests_total) by (method)
# status 레이블별 평균 요청 지연 시간
avg(request_duration_seconds) by (status)
# 모든 인스턴스의 최대 CPU 사용률
max(cpu_usage_percent)
▶️ 시간 기반 함수:
# 5분간의 초당 평균 요청 수
rate(http_requests_total[5m])
# 지난 1시간 동안의 요청 증가량
increase(http_requests_total[1h])
📌 메트릭 설계 모범 사례
효과적인 시스템 관측을 위해서는 메트릭을 적절하게 설계하는 것이 중요합니다. 다음은 메트릭 설계에 관한 몇 가지 모범 사례입니다.
✅ 메트릭 명명 규칙
일관된 메트릭 이름 지정은 가독성과 유지보수성을 높이는 데 중요합니다.
▶️ 메트릭 이름 구조:
- 단위 포함: 메트릭 이름에 단위를 포함시키면 이해하기 쉽습니다 (예: http_request_duration_seconds, memory_usage_bytes).
- 카멜케이스 또는 스네이크케이스: 일관된 스타일을 유지하세요 (예: httpRequestTotal 또는 http_request_total).
- 접두사 사용: 도메인이나 애플리케이션별로 접두사를 사용하여 구분합니다 (예: app_http_requests_total, system_memory_usage_bytes).
▶️ 명확한 이름 지정:
- 무엇을 측정하는지 명확히: count, total, bytes, errors, duration_seconds 등의 접미사를 사용합니다.
- 총계/카운터에는 _total 사용: 예를 들어, http_requests_total는 총 HTTP 요청 수를 나타냅니다.
- 상태보다는 동작에 초점: is_alive 보다는 last_heartbeat_timestamp_seconds와 같이 직접 측정 가능한 값을 사용합니다.
# 프로메테우스 메트릭 정의 예시 (Go 코드)
# 좋은 예:
http_requests_total{method="GET", status="200", path="/api/users"}
http_request_duration_seconds{method="POST", status="201", path="/api/orders"}
memory_usage_bytes{instance="web-01", area="heap"}
# 나쁜 예:
api_metric_1 # 무엇을 측정하는지 불분명
get_users # 메트릭 유형과 단위가 불명확
is_database_connection_ok # 이진 상태보다는 마지막 연결 시간 등을 측정하는 것이 좋음
✅ 적절한 카디널리티 관리
카디널리티(고유한 시계열의 수)가 너무 높으면 성능 문제가 발생할 수 있습니다.
▶️ 카디널리티 관리 방법:
- 필요한 레이블만 사용: 모든 가능한 정보를 레이블에 포함하려는 유혹을 피하세요.
- 무한정 증가하는 레이블 피하기: 사용자 ID, 요청 ID, 타임스탬프 등은 레이블로 사용하지 마세요.
- 레이블 값 제한: 가능한 경우 레이블 값의 수를 제한하세요(예: 모든 URL 경로 대신 주요 API 경로로 그룹화).
# 높은 카디널리티 방지 예시
# 나쁜 예 (무한정 증가하는 카디널리티):
http_requests_total{path="/user/12345", user_id="12345", request_id="abcd-1234-5678"}
# 좋은 예 (제한된 카디널리티):
http_requests_total{path="/user/:id", method="GET", status_code="200"}
✅ 목적에 맞는 메트릭 유형 선택
각 메트릭 유형은 특정 사용 사례에 적합합니다.
▶️ 유형 선택 가이드:
- 카운터: 이벤트 발생 횟수나 변화량 측정 (예: 요청 수, 오류 수)
- 게이지: 현재 상태나 값 측정 (예: 메모리 사용량, 활성 연결 수)
- 히스토그램: 값의 분포와 백분위수 계산 (예: 응답 시간, 요청 크기)
- 서머리: 클라이언트 측에서의 정확한 분위수 계산이 필요한 경우
📌 메트릭 수집과 저장
메트릭 데이터를 효과적으로 수집하고 저장하는 것은 성공적인 모니터링 시스템 구축의 핵심입니다.
✅ 프로메테우스의 메트릭 수집 방식
프로메테우스는 주로 풀(Pull) 기반 메트릭 수집 모델을 사용합니다.
▶️ 풀 모델의 특징:
- 프로메테우스가 대상 시스템에서 메트릭을 가져옴: 설정된 주기(일반적으로 15초)마다 HTTP 엔드포인트에 요청하여 메트릭을 수집합니다.
- 서비스 검색: 프로메테우스는 정적 구성, 파일, DNS, 쿠버네티스 등 다양한 방법으로 모니터링 대상을 발견할 수 있습니다.
- 필터링과 재레이블링: 수집 전후에 메트릭 데이터를 변환하고 필터링할 수 있습니다.
▶️ 프로메테우스 구성 예시:
# prometheus.yml
global:
scrape_interval: 15s # 기본 스크래핑 주기
evaluation_interval: 15s # 규칙 평가 주기
# 스크래핑 대상 설정
scrape_configs:
- job_name: 'api_servers' # 작업(Job) 이름
scrape_interval: 5s # 이 작업에 대한 개별 스크래핑 주기 (전역 설정 재정의)
metrics_path: '/metrics' # 메트릭 엔드포인트 경로
static_configs: # 정적 대상 구성
- targets: ['api-server-01:9090', 'api-server-02:9090']
labels: # 모든 메트릭에 추가될 레이블
environment: 'production'
tier: 'frontend'
- job_name: 'kubernetes-nodes'
kubernetes_sd_configs: # 쿠버네티스 서비스 검색
- role: node
api_server: 'https://kubernetes.default.svc:443'
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
# 레이블 재구성 (relabeling)
relabel_configs:
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: node_name
replacement: $1
✅ 메트릭 저장 및 보존
프로메테우스는 메트릭 데이터를 로컬 시계열 데이터베이스에 저장합니다.
▶️ 데이터 저장 특성:
- 시계열 식별자(fingerprint): 각 고유한 메트릭 이름과 레이블 조합에 대해 해시 값을 생성합니다.
- 샘플 압축: 시간에 따른 변화 패턴을 활용하여 데이터를 효율적으로 압축합니다(일반적으로 1.3~2바이트/샘플).
- 스토리지 계층: 메모리, WAL(Write-Ahead Log), 블록 저장소 등 여러 계층으로 구성됩니다.
▶️ 데이터 보존 정책:
- 기본 보존 기간: 프로메테우스는 기본적으로 15일간 데이터를 보존합니다.
- 스토리지 크기 제한: 디스크 공간에 따라 보존 기간을 조정할 수 있습니다.
- 장기 보관: 장기간 데이터 보관을 위해 Thanos, Cortex와 같은 솔루션을 활용할 수 있습니다.
▶️ 보존 정책 설정 예시:
# prometheus.yml
storage:
tsdb:
path: /prometheus # 데이터 저장 경로
retention.time: 30d # 데이터 보존 기간
retention.size: 10GB # 최대 스토리지 크기
wal:
replay-concurrency: 6 # WAL 리플레이 동시성 수준
📌 결론
이번 글에서 다룬 핵심 내용을 요약하면 다음과 같습니다:
- 시계열 데이터의 이해
- 시간에 따라 수집된 데이터 포인트의 집합으로 타임스탬프를 포함
- 특수한 시계열 데이터베이스를 통해 효율적으로 관리
- 프로메테우스 데이터 모델
- 메트릭 이름과 레이블 조합으로 고유한 시계열 식별
- 레이블을 통한 다차원 데이터 모델 지원
- 메트릭 유형과 활용
- 카운터: 증가만 하는 값 (요청 수, 오류 수)
- 게이지: 증감 가능한 스냅샷 값 (CPU 사용률, 메모리)
- 히스토그램/서머리: 값의 분포 측정 (응답 시간, 처리량)
- 효과적인 메트릭 설계
- 일관된 명명 규칙 적용
- 카디널리티 관리를 통한 성능 최적화
- 목적에 맞는 메트릭 유형 선택
- 메트릭 수집과 저장
- 풀(Pull) 기반의 메트릭 수집 방식
- 효율적인 압축과 보존 정책 설정
적절한 시계열 데이터 관리와 쿼리 기술을 익히면 시스템의 동작을 더 깊이 이해하고, 문제 발생 시 신속하게 대응할 수 있는 기반을 마련할 수 있습니다.