Skip to content

Prometheus

지원 버전: Prometheus 2.x / 3.x 마지막 업데이트: 2026년 7월 27일

목차

소개

Prometheus는 SoundCloud에서 개발되어 CNCF(Cloud Native Computing Foundation)에 기증된 오픈소스 시스템 모니터링 및 알림 툴킷입니다. Kubernetes 환경에서 사실상의 표준(de facto standard) 모니터링 솔루션으로 자리잡았습니다.

주요 특징

  1. 다차원 데이터 모델: 메트릭명과 키-값 쌍(레이블)으로 식별되는 시계열 데이터
  2. PromQL: 다차원 데이터를 활용하는 유연한 쿼리 언어
  3. Pull 기반 수집: HTTP를 통해 타겟에서 메트릭을 주기적으로 스크랩
  4. 서비스 디스커버리: Kubernetes 등 동적 환경에서 모니터링 대상 자동 발견
  5. 알림 관리: 규칙 기반 알림 정의 및 Alertmanager를 통한 알림 라우팅
  6. 독립적인 서버: 단일 서버로 동작하며 분산 스토리지에 의존하지 않음

Prometheus가 적합한 경우

  • 순수 수치 시계열 기록
  • 머신 중심 모니터링과 고도로 동적인 서비스 지향 아키텍처
  • 다차원 데이터 수집 및 쿼리
  • 100% 정확도보다 시스템 개요 파악이 중요한 경우

Prometheus가 적합하지 않은 경우

  • 이벤트 로깅이나 트레이싱
  • 요청별 과금 등 100% 정확도가 필요한 경우
  • 장기 데이터 보존 (별도 장기 저장소 필요)

아키텍처

서비스 디스커버리와 대상으로부터 Retrieval이 메트릭을 스크랩해 TSDB에 저장하고, HTTP Server가 시각화 도구에 쿼리 API를 제공하며 Alert Rules가 Alertmanager를 거쳐 알림 채널로 전달되는 흐름을 보여준다.

데이터 흐름

  1. 서비스 디스커버리: Kubernetes API, DNS, 파일 등에서 스크랩 대상 발견
  2. 메트릭 수집: HTTP를 통해 대상의 /metrics 엔드포인트에서 메트릭 스크랩
  3. 데이터 저장: 수집된 메트릭을 로컬 TSDB에 저장
  4. 규칙 평가: 저장된 데이터에 대해 알림 및 기록 규칙 평가
  5. 알림 전송: 발화된 알림을 Alertmanager로 전송
  6. 쿼리 제공: HTTP API를 통해 PromQL 쿼리 처리

핵심 구성 요소

TSDB (Time Series Database)

Prometheus의 내장 시계열 데이터베이스는 효율적인 시계열 데이터 저장을 위해 설계되었습니다.

yaml
# TSDB 관련 설정
storage:
  tsdb:
    path: /prometheus               # 데이터 저장 경로
    retention.time: 15d             # 데이터 보존 기간
    retention.size: 50GB            # 최대 스토리지 크기
    wal-compression: true           # WAL 압축 활성화
    min-block-duration: 2h          # 최소 블록 크기
    max-block-duration: 36h         # 최대 블록 크기 (보존 기간의 10% 권장)

TSDB 블록 구조:

data/
├── 01BKGV7JBM69T2G1BGBGM6KB12/   # 2시간 블록
│   ├── chunks/                    # 시계열 데이터
│   │   └── 000001
│   ├── tombstones                 # 삭제된 데이터
│   ├── index                      # 레이블 인덱스
│   └── meta.json                  # 블록 메타데이터
├── 01BKGV7JC0RY8A6MACW02A2PJD/   # 또 다른 블록
├── chunks_head/                   # 현재 쓰기 중인 데이터
│   └── 000001
├── wal/                          # Write-Ahead Log
│   ├── 00000000
│   └── 00000001
└── lock                          # 프로세스 잠금

kube-state-metrics

Kubernetes API 객체에 대한 메트릭을 생성하는 서비스입니다.

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-state-metrics
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kube-state-metrics
  template:
    metadata:
      labels:
        app: kube-state-metrics
    spec:
      serviceAccountName: kube-state-metrics
      containers:
      - name: kube-state-metrics
        image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.10.1
        ports:
        - name: http-metrics
          containerPort: 8080
        - name: telemetry
          containerPort: 8081
        resources:
          requests:
            cpu: 10m
            memory: 128Mi
          limits:
            cpu: 100m
            memory: 256Mi
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          timeoutSeconds: 5
        readinessProbe:
          httpGet:
            path: /
            port: 8081
          initialDelaySeconds: 5
          timeoutSeconds: 5

주요 메트릭:

promql
# Pod 상태 메트릭
kube_pod_status_phase{phase="Running"}
kube_pod_container_status_restarts_total
kube_pod_container_resource_requests{resource="cpu"}
kube_pod_container_resource_limits{resource="memory"}

# Deployment 메트릭
kube_deployment_spec_replicas
kube_deployment_status_replicas_available
kube_deployment_status_replicas_unavailable

# Node 메트릭
kube_node_status_condition{condition="Ready"}
kube_node_status_allocatable{resource="cpu"}

node-exporter

호스트 수준의 하드웨어 및 OS 메트릭을 노출하는 익스포터입니다.

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true
      hostPID: true
      containers:
      - name: node-exporter
        image: prom/node-exporter:v1.7.0
        args:
          - --path.procfs=/host/proc
          - --path.sysfs=/host/sys
          - --path.rootfs=/host/root
          - --collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker/.+)($|/)
          - --collector.netclass.ignored-devices=^(veth.*|docker.*|br-.*)$
        ports:
        - name: metrics
          containerPort: 9100
        volumeMounts:
        - name: proc
          mountPath: /host/proc
          readOnly: true
        - name: sys
          mountPath: /host/sys
          readOnly: true
        - name: root
          mountPath: /host/root
          readOnly: true
          mountPropagation: HostToContainer
        resources:
          requests:
            cpu: 10m
            memory: 32Mi
          limits:
            cpu: 100m
            memory: 64Mi
      volumes:
      - name: proc
        hostPath:
          path: /proc
      - name: sys
        hostPath:
          path: /sys
      - name: root
        hostPath:
          path: /
      tolerations:
      - operator: Exists

주요 메트릭:

promql
# CPU 메트릭
node_cpu_seconds_total{mode="idle"}
rate(node_cpu_seconds_total{mode!="idle"}[5m])

# 메모리 메트릭
node_memory_MemTotal_bytes
node_memory_MemAvailable_bytes
node_memory_Buffers_bytes
node_memory_Cached_bytes

# 디스크 메트릭
node_filesystem_size_bytes
node_filesystem_avail_bytes
node_disk_io_time_seconds_total

# 네트워크 메트릭
node_network_receive_bytes_total
node_network_transmit_bytes_total

2026년 7월 업데이트: 커스텀 메트릭 익스포터 작성 가이드

2026년 7월 14일 Kubernetes 블로그에 Building a Custom Metrics Exporter for Kubernetes 글이 게시되었습니다. kube-state-metrics나 node-exporter가 다루지 못하는 신호(큐 대기 메시지 수, 배치 작업 소요 시간, 활성 연결 수 등)를 수집해야 할 때 익스포터를 직접 작성하는 방법을 다룹니다. 핵심 요점: 익스포터는 /metrics 엔드포인트로 플레인 텍스트 메트릭을 노출하는 작은 HTTP 서버일 뿐이며, 신호 특성에 따라 메트릭 타입을 선택하고(누적 합계는 counter, 오르내리는 값은 gauge, 지연 시간 분포는 histogram), 이름은 <네임스페이스>_<이름>_<단위> 형식의 snake_case로 짓습니다. 컨테이너로 패키징해 Prometheus와 궁극적으로 HorizontalPodAutoscaler까지 연결하는 과정도 다룹니다.

PromQL 쿼리 언어

PromQL(Prometheus Query Language)은 Prometheus의 함수형 쿼리 언어입니다.

기본 쿼리

promql
# 인스턴트 벡터: 현재 시점의 값
http_requests_total

# 레이블 필터링
http_requests_total{method="GET"}
http_requests_total{status=~"2.."}           # 정규표현식 매칭
http_requests_total{status!~"5.."}           # 부정 정규표현식

# 범위 벡터: 시간 범위의 값들
http_requests_total[5m]                       # 최근 5분간의 모든 샘플
http_requests_total[1h:5m]                    # 1시간 동안 5분 간격 샘플

# 오프셋 수정자
http_requests_total offset 1h                 # 1시간 전 값
rate(http_requests_total[5m] offset 1h)       # 1시간 전의 5분간 rate

집계 연산자

promql
# sum: 합계
sum(http_requests_total)
sum by (method)(http_requests_total)          # method별 합계
sum without (instance)(http_requests_total)   # instance 제외하고 합계

# avg: 평균
avg(node_cpu_seconds_total)

# count: 개수
count(kube_pod_status_phase{phase="Running"})

# min/max: 최소/최대
max(node_memory_MemAvailable_bytes)

# topk/bottomk: 상위/하위 k개
topk(5, sum by (pod)(rate(container_cpu_usage_seconds_total[5m])))

# quantile: 분위수
quantile(0.95, http_request_duration_seconds)

# stddev/stdvar: 표준편차/분산
stddev(rate(http_requests_total[5m]))

Rate 및 증가량 함수

promql
# rate: 초당 평균 증가율 (Counter용)
rate(http_requests_total[5m])

# irate: 마지막 두 샘플 간의 즉각적인 증가율
irate(http_requests_total[5m])

# increase: 시간 범위 동안의 총 증가량
increase(http_requests_total[1h])

# delta: 범위의 처음과 끝 값의 차이 (Gauge용)
delta(temperature_celsius[1h])

# deriv: 초당 변화율 (Gauge용, 선형 회귀)
deriv(temperature_celsius[1h])

수학 함수

promql
# abs: 절대값
abs(rate(http_requests_total[5m]) - 100)

# ceil/floor/round: 올림/내림/반올림
ceil(http_request_duration_seconds)

# clamp: 값 범위 제한
clamp(cpu_usage, 0, 100)
clamp_min(temperature, 0)
clamp_max(memory_usage_percent, 100)

# sqrt/ln/log2/log10/exp: 수학 함수
sqrt(variance)
ln(http_requests_total)

시간 함수

promql
# time(): 현재 Unix 타임스탬프
time()

# timestamp(): 샘플의 타임스탬프
timestamp(up)

# day_of_week/day_of_month/hour/minute
hour(timestamp(up))

# 시간 기반 필터링 예시
http_requests_total and on() hour() >= 9 < 18  # 업무 시간만

히스토그램 함수

promql
# histogram_quantile: 히스토그램에서 분위수 계산
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# 레이블별 분위수
histogram_quantile(0.99,
  sum by (le, method)(rate(http_request_duration_seconds_bucket[5m]))
)

# 평균 지연시간 (히스토그램에서)
rate(http_request_duration_seconds_sum[5m])
/ rate(http_request_duration_seconds_count[5m])

예측 함수

promql
# predict_linear: 선형 회귀 기반 미래 값 예측
predict_linear(node_filesystem_avail_bytes[6h], 24*60*60)  # 24시간 후 예측

# 디스크 공간 소진 예측 알림
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*60*60) < 0

# holt_winters: 계절성을 고려한 예측
holt_winters(http_requests_total[1d], 0.5, 0.5)

실용적인 쿼리 예시

promql
# CPU 사용률 (%)
100 - (avg by (instance)(irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# 메모리 사용률 (%)
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)

# Pod 재시작 횟수 증가
increase(kube_pod_container_status_restarts_total[1h]) > 3

# HTTP 오류율 (%)
100 * sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))

# p95 지연시간
histogram_quantile(0.95,
  sum by (le)(rate(http_request_duration_seconds_bucket[5m]))
)

# 디스크 사용률
100 - (node_filesystem_avail_bytes{mountpoint="/"}
/ node_filesystem_size_bytes{mountpoint="/"} * 100)

# 네트워크 처리량 (bytes/sec)
rate(node_network_receive_bytes_total{device="eth0"}[5m])
+ rate(node_network_transmit_bytes_total{device="eth0"}[5m])

서비스 디스커버리

Kubernetes 서비스 디스커버리

Prometheus는 Kubernetes API를 통해 자동으로 모니터링 대상을 발견합니다.

yaml
scrape_configs:
  # Pod 자동 발견
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      # prometheus.io/scrape 어노테이션이 있는 Pod만 스크랩
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      # 커스텀 메트릭 경로
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      # 커스텀 포트
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__
      # 레이블 추가
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod

  # Service 자동 발견
  - job_name: 'kubernetes-services'
    kubernetes_sd_configs:
      - role: service
    metrics_path: /probe
    params:
      module: [http_2xx]
    relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
        action: keep
        regex: true
      - source_labels: [__address__]
        target_label: __param_target
      - target_label: __address__
        replacement: blackbox-exporter:9115
      - source_labels: [__param_target]
        target_label: instance

  # Node 자동 발견
  - job_name: 'kubernetes-nodes'
    kubernetes_sd_configs:
      - role: node
    scheme: https
    tls_config:
      ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)

Pod 어노테이션 기반 스크래핑

yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  annotations:
    prometheus.io/scrape: "true"       # 스크래핑 활성화
    prometheus.io/port: "8080"         # 메트릭 포트
    prometheus.io/path: "/metrics"     # 메트릭 경로
    prometheus.io/scheme: "http"       # http 또는 https
spec:
  containers:
  - name: app
    image: my-app:latest
    ports:
    - containerPort: 8080

Prometheus Operator

Prometheus Operator는 Kubernetes에서 Prometheus를 선언적으로 관리하기 위한 컨트롤러입니다.

커스텀 리소스 정의 (CRDs)

Prometheus Operator가 Prometheus, Alertmanager, ThanosRuler를 생성하고, Prometheus는 ServiceMonitor·PodMonitor·PrometheusRule·ScrapeConfig·Probe를, Alertmanager는 AlertmanagerConfig를 하위 커스텀 리소스로 갖는 계층 구조를 보여준다.

Prometheus 리소스

yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
  namespace: monitoring
spec:
  replicas: 2
  version: v2.48.0

  # 서비스 계정
  serviceAccountName: prometheus

  # 보안 컨텍스트
  securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000

  # 모니터 선택자
  serviceMonitorSelector:
    matchLabels:
      team: frontend
  serviceMonitorNamespaceSelector: {}  # 모든 네임스페이스

  podMonitorSelector:
    matchLabels:
      team: frontend

  # 규칙 선택자
  ruleSelector:
    matchLabels:
      role: alert-rules
  ruleNamespaceSelector: {}

  # 알림 설정
  alerting:
    alertmanagers:
    - namespace: monitoring
      name: alertmanager-main
      port: web

  # 리소스 제한
  resources:
    requests:
      memory: 1Gi
      cpu: 500m
    limits:
      memory: 4Gi
      cpu: 2000m

  # 스토리지 설정
  retention: 15d
  retentionSize: 50GB
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: gp3
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 100Gi

  # 외부 레이블
  externalLabels:
    cluster: production
    region: ap-northeast-2

  # Remote Write 설정
  remoteWrite:
  - url: http://victoriametrics:8428/api/v1/write
    queueConfig:
      maxSamplesPerSend: 10000
      batchSendDeadline: 5s

ServiceMonitor

yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: example-app
  namespace: monitoring
  labels:
    team: frontend
spec:
  # 대상 서비스 선택
  selector:
    matchLabels:
      app: example-app

  # 대상 네임스페이스
  namespaceSelector:
    matchNames:
    - production
    - staging

  # 엔드포인트 설정
  endpoints:
  - port: web
    interval: 30s
    scrapeTimeout: 10s
    path: /metrics
    scheme: http

    # TLS 설정 (HTTPS인 경우)
    # tlsConfig:
    #   insecureSkipVerify: true

    # 기본 인증
    # basicAuth:
    #   username:
    #     name: basic-auth
    #     key: username
    #   password:
    #     name: basic-auth
    #     key: password

    # 레이블 재작성
    relabelings:
    - sourceLabels: [__meta_kubernetes_pod_name]
      targetLabel: pod
    - sourceLabels: [__meta_kubernetes_namespace]
      targetLabel: namespace

    # 메트릭 필터링
    metricRelabelings:
    - sourceLabels: [__name__]
      regex: 'go_.*'
      action: drop

PodMonitor

yaml
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: example-pods
  namespace: monitoring
  labels:
    team: frontend
spec:
  selector:
    matchLabels:
      app: example-app

  namespaceSelector:
    any: true

  podMetricsEndpoints:
  - port: metrics
    interval: 30s
    path: /metrics

    relabelings:
    - sourceLabels: [__meta_kubernetes_pod_node_name]
      targetLabel: node
    - sourceLabels: [__meta_kubernetes_pod_label_version]
      targetLabel: version

PrometheusRule

yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: kubernetes-alerts
  namespace: monitoring
  labels:
    role: alert-rules
spec:
  groups:
  - name: kubernetes-system
    interval: 30s
    rules:
    # 노드 메모리 부족 알림
    - alert: NodeMemoryHigh
      expr: |
        (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
        / node_memory_MemTotal_bytes * 100 > 90
      for: 5m
      labels:
        severity: warning
        team: infrastructure
      annotations:
        summary: "Node {{ $labels.instance }} memory usage is high"
        description: "Memory usage is {{ printf \"%.2f\" $value }}%"
        runbook_url: "https://wiki.example.com/runbooks/node-memory-high"

    # Pod 재시작 알림
    - alert: PodRestartingFrequently
      expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is restarting frequently"
        description: "Pod has restarted {{ $value }} times in the last hour"

    # 디스크 공간 부족 예측
    - alert: NodeDiskWillFillIn24Hours
      expr: |
        predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*60*60) < 0
      for: 1h
      labels:
        severity: critical
      annotations:
        summary: "Disk on {{ $labels.instance }} will fill in 24 hours"
        description: "Based on current trend, disk will be full within 24 hours"

  - name: recording-rules
    rules:
    # CPU 사용률 기록 규칙
    - record: instance:node_cpu_utilization:rate5m
      expr: |
        100 - (avg by (instance)(irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

    # 메모리 사용률 기록 규칙
    - record: instance:node_memory_utilization:ratio
      expr: |
        1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes

kube-prometheus-stack 설치

kube-prometheus-stack은 Prometheus, Alertmanager, Grafana 및 관련 구성 요소를 포함하는 포괄적인 Helm 차트입니다.

Helm을 사용한 설치

bash
# Helm 저장소 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 기본 설치
helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace

# 커스텀 값으로 설치
helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  -f values.yaml

values.yaml 예시

yaml
# Prometheus 설정
prometheus:
  prometheusSpec:
    # 복제본 수
    replicas: 2

    # 보존 기간
    retention: 15d
    retentionSize: 50GB

    # 스토리지
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 100Gi

    # 리소스
    resources:
      requests:
        cpu: 500m
        memory: 2Gi
      limits:
        cpu: 2000m
        memory: 8Gi

    # Remote Write
    remoteWrite:
    - url: http://victoriametrics:8428/api/v1/write
      queueConfig:
        maxSamplesPerSend: 10000
        batchSendDeadline: 5s

    # 외부 레이블
    externalLabels:
      cluster: production

    # 추가 스크랩 설정
    additionalScrapeConfigs:
    - job_name: 'custom-endpoints'
      static_configs:
      - targets: ['custom-service:8080']

    # 모든 네임스페이스의 ServiceMonitor 수집
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false

# Alertmanager 설정
alertmanager:
  alertmanagerSpec:
    replicas: 3
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 10Gi
    resources:
      requests:
        cpu: 50m
        memory: 64Mi
      limits:
        cpu: 200m
        memory: 256Mi

# Grafana 설정
grafana:
  enabled: true
  replicas: 1

  adminPassword: "admin"  # 실제 환경에서는 시크릿 사용

  persistence:
    enabled: true
    storageClassName: gp3
    size: 10Gi

  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 512Mi

  # 추가 데이터 소스
  additionalDataSources:
  - name: VictoriaMetrics
    type: prometheus
    url: http://victoriametrics:8428
    access: proxy
    isDefault: false

  # 사이드카 설정 (대시보드 자동 로드)
  sidecar:
    dashboards:
      enabled: true
      searchNamespace: ALL
    datasources:
      enabled: true

# kube-state-metrics 설정
kubeStateMetrics:
  enabled: true

# node-exporter 설정
nodeExporter:
  enabled: true

# Prometheus Operator 설정
prometheusOperator:
  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 200m
      memory: 256Mi

Alertmanager 연동

Alertmanager 설정

yaml
apiVersion: monitoring.coreos.com/v1
kind: Alertmanager
metadata:
  name: main
  namespace: monitoring
spec:
  replicas: 3
  version: v0.26.0

  # 고가용성 설정
  clusterAdvertiseAddress: ""  # 자동 감지

  # 로그 레벨
  logLevel: info

  # 스토리지
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: gp3
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

  # 설정 선택자
  alertmanagerConfigSelector:
    matchLabels:
      alertmanagerConfig: main

  # 설정 네임스페이스 선택자
  alertmanagerConfigNamespaceSelector: {}

AlertmanagerConfig

yaml
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
  name: main-config
  namespace: monitoring
  labels:
    alertmanagerConfig: main
spec:
  # 라우팅 설정
  route:
    receiver: 'default'
    groupBy: ['alertname', 'namespace', 'severity']
    groupWait: 30s
    groupInterval: 5m
    repeatInterval: 4h

    routes:
    # Critical 알림 -> PagerDuty
    - receiver: 'pagerduty-critical'
      matchers:
      - name: severity
        matchType: =
        value: critical
      groupWait: 10s
      repeatInterval: 1h

    # Warning 알림 -> Slack
    - receiver: 'slack-warnings'
      matchers:
      - name: severity
        matchType: =
        value: warning
      groupWait: 1m
      repeatInterval: 4h

    # 특정 팀 알림
    - receiver: 'slack-team-backend'
      matchers:
      - name: team
        matchType: =
        value: backend

  # 억제 규칙
  inhibitRules:
  - sourceMatch:
    - name: severity
      matchType: =
      value: critical
    targetMatch:
    - name: severity
      matchType: =
      value: warning
    equal: ['alertname', 'namespace']

  # 수신자 설정
  receivers:
  - name: 'default'
    emailConfigs:
    - to: 'alerts@example.com'
      from: 'alertmanager@example.com'
      smarthost: 'smtp.example.com:587'
      authUsername: 'alertmanager'
      authPassword:
        name: alertmanager-smtp
        key: password
      requireTLS: true

  - name: 'slack-warnings'
    slackConfigs:
    - apiURL:
        name: alertmanager-slack
        key: webhook-url
      channel: '#alerts'
      sendResolved: true
      title: '{{ template "slack.default.title" . }}'
      text: '{{ template "slack.default.text" . }}'
      actions:
      - type: button
        text: 'Runbook'
        url: '{{ (index .Alerts 0).Annotations.runbook_url }}'
      - type: button
        text: 'Dashboard'
        url: 'https://grafana.example.com/d/{{ (index .Alerts 0).Labels.dashboard }}'

  - name: 'slack-team-backend'
    slackConfigs:
    - apiURL:
        name: alertmanager-slack
        key: webhook-url
      channel: '#team-backend-alerts'
      sendResolved: true

  - name: 'pagerduty-critical'
    pagerdutyConfigs:
    - routingKey:
        name: alertmanager-pagerduty
        key: routing-key
      sendResolved: true
      severity: '{{ if eq .GroupLabels.severity "critical" }}critical{{ else }}warning{{ end }}'
      class: '{{ .GroupLabels.alertname }}'
      component: '{{ .GroupLabels.job }}'
      group: '{{ .GroupLabels.namespace }}'

Remote Write 및 AMP 연동

2026년 7월 업데이트: AMP 워크스페이스 한도 상향

2026년 7월 21일, Amazon Managed Service for Prometheus가 워크스페이스당 최대 15억 개의 활성 메트릭 시계열과 최대 20만 개의 recording/alerting 규칙을 지원한다고 발표되었습니다. 한도 상향은 AWS Support Center 또는 AWS Service Quotas에서 서비스 할당량 증가를 요청하면 됩니다. 자세한 내용은 발표를 참고하세요.

Amazon Managed Prometheus (AMP) 연동

yaml
# Prometheus 설정
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
  namespace: monitoring
spec:
  # ... 기타 설정 ...

  # IRSA 서비스 계정
  serviceAccountName: prometheus-amp

  # Remote Write to AMP
  remoteWrite:
  - url: https://aps-workspaces.ap-northeast-2.amazonaws.com/workspaces/ws-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/api/v1/remote_write
    sigv4:
      region: ap-northeast-2
    queueConfig:
      maxSamplesPerSend: 1000
      maxShards: 200
      capacity: 2500
    writeRelabelConfigs:
    # 불필요한 메트릭 제외
    - sourceLabels: [__name__]
      regex: 'go_.*'
      action: drop

IRSA 설정

bash
# OIDC 공급자 확인
aws eks describe-cluster --name my-cluster --query "cluster.identity.oidc.issuer" --output text

# IAM 정책 생성
cat <<EOF > amp-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "aps:RemoteWrite",
                "aps:QueryMetrics",
                "aps:GetSeries",
                "aps:GetLabels",
                "aps:GetMetricMetadata"
            ],
            "Resource": "*"
        }
    ]
}
EOF

aws iam create-policy \
  --policy-name AmazonManagedPrometheusPolicy \
  --policy-document file://amp-policy.json

# 서비스 계정 생성 (eksctl 사용)
eksctl create iamserviceaccount \
  --name prometheus-amp \
  --namespace monitoring \
  --cluster my-cluster \
  --attach-policy-arn arn:aws:iam::123456789012:policy/AmazonManagedPrometheusPolicy \
  --approve

VictoriaMetrics로 Remote Write

yaml
remoteWrite:
- url: http://victoriametrics-vminsert:8480/insert/0/prometheus/api/v1/write
  queueConfig:
    maxSamplesPerSend: 10000
    maxShards: 30
    capacity: 10000
    batchSendDeadline: 5s
    minBackoff: 30ms
    maxBackoff: 5s
  writeRelabelConfigs:
  # 높은 카디널리티 메트릭 제외
  - sourceLabels: [__name__]
    regex: '(apiserver_request_duration_seconds_bucket|etcd_request_duration_seconds_bucket)'
    action: drop
  # 레이블 수정
  - sourceLabels: [__name__, le]
    regex: '.*;0\..*'
    action: drop

성능 튜닝

메모리 최적화

yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  # 메모리 제한
  resources:
    requests:
      memory: 2Gi
    limits:
      memory: 8Gi

  # 쿼리 제한
  query:
    maxConcurrency: 20          # 최대 동시 쿼리 수
    maxSamples: 50000000        # 쿼리당 최대 샘플 수
    timeout: 2m                 # 쿼리 타임아웃

  # WAL 압축
  walCompression: true

  # 청크 풀 크기
  # additionalArgs:
  # - --storage.tsdb.head-chunks-write-queue-size=1000

스크랩 최적화

yaml
scrape_configs:
- job_name: 'high-cardinality-app'
  scrape_interval: 60s           # 간격 늘리기
  scrape_timeout: 30s
  sample_limit: 10000            # 샘플 수 제한

  metric_relabel_configs:
  # 불필요한 메트릭 제거
  - source_labels: [__name__]
    regex: 'go_.*|process_.*'
    action: drop

  # 높은 카디널리티 레이블 제거
  - regex: 'pod_template_hash|controller_revision_hash'
    action: labeldrop

TSDB 튜닝

yaml
# 명령줄 인자로 TSDB 튜닝
additionalArgs:
  # 블록 크기 조정 (보존 기간의 10% 이하)
  - --storage.tsdb.max-block-duration=4h
  - --storage.tsdb.min-block-duration=2h

  # 청크 인코딩 최적화
  - --storage.tsdb.head-chunks-write-queue-size=1000

  # 메모리 맵 청크 (성능 향상)
  - --storage.tsdb.head-chunks-max-mmap-bytes=1073741824

모범 사례

고가용성 구성

yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  # 복제본 2개 운영
  replicas: 2

  # Pod 반친화성
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app.kubernetes.io/name: prometheus
        topologyKey: kubernetes.io/hostname

  # 샤딩 설정 (대규모 환경)
  shards: 2

  # 외부 레이블 (중복 제거용)
  externalLabels:
    cluster: production
    replica: $(POD_NAME)

레이블링 전략

yaml
# 일관된 레이블 체계
relabelings:
  # 환경 레이블
  - sourceLabels: [__meta_kubernetes_namespace]
    regex: '(prod|staging|dev)-.*'
    replacement: '$1'
    targetLabel: environment

  # 팀 레이블
  - sourceLabels: [__meta_kubernetes_pod_label_team]
    targetLabel: team

  # 서비스 레이블
  - sourceLabels: [__meta_kubernetes_pod_label_app]
    targetLabel: service

알림 규칙 가이드라인

yaml
# 좋은 알림 규칙 예시
- alert: HighErrorRate
  # 의미 있는 임계값
  expr: |
    sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
    / sum(rate(http_requests_total[5m])) by (service) > 0.01
  # 적절한 대기 시간 (노이즈 방지)
  for: 5m
  labels:
    # 심각도 레벨
    severity: warning
    # 담당 팀
    team: backend
  annotations:
    # 명확한 요약
    summary: "High error rate on {{ $labels.service }}"
    # 상세 설명
    description: |
      Service {{ $labels.service }} has error rate of {{ printf "%.2f" $value }}%.
      This is above the 1% threshold.
    # 런북 링크
    runbook_url: "https://wiki.example.com/runbooks/high-error-rate"
    # 대시보드 링크
    dashboard_url: "https://grafana.example.com/d/service-overview?var-service={{ $labels.service }}"

문제 해결

일반적인 문제

1. 메모리 부족 (OOMKilled)

bash
# 현재 메모리 사용량 확인
kubectl top pod -n monitoring prometheus-prometheus-0

# TSDB 상태 확인
curl -s http://prometheus:9090/api/v1/status/tsdb | jq .

# 해결: 메모리 제한 증가 또는 보존 기간 감소

2. 높은 카디널리티

promql
# 카디널리티 높은 메트릭 찾기
topk(10, count by (__name__)({__name__=~".+"}))

# 특정 메트릭의 레이블 조합 수
count(http_requests_total)

# 해결: metric_relabel_configs로 불필요한 레이블/메트릭 제거

3. 스크랩 실패

bash
# 대상 상태 확인
curl -s http://prometheus:9090/api/v1/targets | jq '.data.activeTargets[] | select(.health != "up")'

# 특정 대상 메트릭 직접 확인
kubectl exec -it prometheus-prometheus-0 -n monitoring -- \
  wget -qO- http://target-service:8080/metrics | head -20

# 해결: 네트워크 정책, RBAC, 서비스 엔드포인트 확인

4. 쿼리 타임아웃

yaml
# 쿼리 타임아웃 증가
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
spec:
  query:
    timeout: 5m
    maxSamples: 100000000

디버깅 명령어

bash
# Prometheus 로그 확인
kubectl logs -f prometheus-prometheus-0 -n monitoring

# Prometheus API 상태
curl http://prometheus:9090/api/v1/status/config
curl http://prometheus:9090/api/v1/status/flags
curl http://prometheus:9090/api/v1/status/runtimeinfo

# TSDB 상태
curl http://prometheus:9090/api/v1/status/tsdb

# 대상 메타데이터
curl http://prometheus:9090/api/v1/targets/metadata

# 규칙 상태
curl http://prometheus:9090/api/v1/rules

# 알림 상태
curl http://prometheus:9090/api/v1/alerts

참고 자료

퀴즈

이 장에서 배운 내용을 테스트하려면 Prometheus 퀴즈를 풀어보세요.