Prometheus + Grafana

Kubernetes 클러스터를 운영하면서 모니터링 없이 버티는 것은 계기판 없이 비행기를 조종하는 것과 같습니다. Prometheus는 메트릭 수집·저장·쿼리를, Grafana는 시각화·알림을 담당하며, 이 조합은 Kubernetes 모니터링의 사실상 표준입니다.

이 글에서는 Prometheus의 Pull 기반 아키텍처를 이해하고, kube-prometheus-stack으로 한 번에 설치하며, PromQL 실전 쿼리로 핵심 지표를 추출하고, Grafana 대시보드 설계Alertmanager 알림 라우팅까지 운영 수준으로 정리합니다.

1. Prometheus 아키텍처: Pull 모델의 장점

대부분의 모니터링 시스템이 에이전트가 데이터를 중앙 서버로 보내는 Push 방식인 반면, Prometheus는 타겟의 /metrics 엔드포인트를 주기적으로 긁어오는(scrape) Pull 방식을 씁니다.

비교 항목 Push 모델 Pull 모델 (Prometheus)
타겟 장애 감지 데이터가 안 오면 추정 scrape 실패로 즉시 감지 (up == 0)
서비스 디스커버리 에이전트가 서버 주소를 알아야 함 K8s API로 Pod/Service 자동 발견
로컬 디버깅 서버 없으면 확인 불가 curl localhost:8080/metrics로 즉시 확인
부하 제어 에이전트 수에 비례해 서버 부하 증가 scrape interval로 Prometheus가 부하 제어

2. kube-prometheus-stack 설치: 한 번에 끝내기

Prometheus, Grafana, Alertmanager, node-exporter, kube-state-metrics를 개별 설치하면 설정이 복잡합니다. kube-prometheus-stack Helm 차트는 이 모든 것을 하나로 묶어 제공합니다.

# 네임스페이스 + Helm 설치
helm repo add prometheus-community 
  https://prometheus-community.github.io/helm-charts
helm repo update

helm install monitoring prometheus-community/kube-prometheus-stack 
  --namespace monitoring --create-namespace 
  -f values-monitoring.yaml
# values-monitoring.yaml 핵심 설정
prometheus:
  prometheusSpec:
    retention: 15d
    storageSpec:
      volumeClaimTemplate:
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 50Gi

grafana:
  adminPassword: "변경필수"
  persistence:
    enabled: true
    size: 10Gi

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          resources:
            requests:
              storage: 5Gi

설치 후 포함되는 컴포넌트:

  • Prometheus Operator: ServiceMonitor·PodMonitor CRD로 scrape 대상을 선언적 관리
  • node-exporter: 노드별 CPU, 메모리, 디스크, 네트워크 메트릭
  • kube-state-metrics: Pod, Deployment, StatefulSet 등 K8s 오브젝트 상태 메트릭
  • Grafana: 사전 구성된 대시보드 20개 이상 포함

3. ServiceMonitor: 앱 메트릭 자동 수집

애플리케이션이 /metrics 엔드포인트를 노출하면, ServiceMonitor를 생성하는 것만으로 Prometheus가 자동으로 scrape합니다.

# 앱 Service
apiVersion: v1
kind: Service
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  ports:
    - name: http
      port: 3000
    - name: metrics
      port: 9090
  selector:
    app: my-app

---
# ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
  labels:
    release: monitoring  # kube-prometheus-stack 릴리스 이름과 일치
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics
      interval: 15s
      path: /metrics

주의: ServiceMonitor의 labelsrelease: monitoring을 반드시 추가해야 합니다. Prometheus Operator는 이 레이블로 어떤 ServiceMonitor를 수집할지 필터링합니다. 이 레이블이 없으면 메트릭이 수집되지 않는데, 가장 흔한 트러블슈팅 포인트입니다.

4. PromQL 실전 쿼리: 핵심 지표 5가지

PromQL은 Prometheus의 쿼리 언어입니다. 운영에서 반드시 모니터링해야 할 핵심 쿼리 다섯 가지를 정리합니다.

지표 PromQL 의미
CPU 사용률 rate(container_cpu_usage_seconds_total{namespace="my-app"}[5m]) 최근 5분간 초당 CPU 사용량
메모리 사용률 container_memory_working_set_bytes{namespace="my-app"} / container_spec_memory_limit_bytes * 100 Limit 대비 실제 사용 비율
HTTP 에러율 sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 5xx 비율 (SLO 기준)
응답 지연 P99 histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) 99번째 백분위 응답 시간
Pod 재시작 횟수 increase(kube_pod_container_status_restarts_total[1h]) > 0 최근 1시간 내 재시작된 컨테이너

rate()는 counter 타입 메트릭의 초당 증가율을 계산합니다. counter는 항상 증가하는 값(요청 수, 에러 수)이므로, 원시 값보다 rate()로 변환해야 의미 있는 그래프가 됩니다.

5. Grafana 대시보드 설계 원칙

kube-prometheus-stack은 20개 이상의 대시보드를 기본 제공하지만, 앱 전용 대시보드는 직접 만들어야 합니다. RED 메서드(Rate, Errors, Duration)를 기준으로 설계하면 핵심 지표를 놓치지 않습니다.

# ConfigMap으로 Grafana 대시보드 프로비저닝
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "1"  # Grafana sidecar가 자동 로드
data:
  my-app.json: |
    {
      "dashboard": {
        "title": "My App - RED Metrics",
        "panels": [
          {
            "title": "Request Rate",
            "type": "timeseries",
            "targets": [{
              "expr": "sum(rate(http_requests_total{app="my-app"}[5m]))"
            }]
          },
          {
            "title": "Error Rate (%)",
            "type": "stat",
            "targets": [{
              "expr": "sum(rate(http_requests_total{app="my-app",status=~"5.."}[5m])) / sum(rate(http_requests_total{app="my-app"}[5m])) * 100"
            }]
          },
          {
            "title": "P99 Latency",
            "type": "timeseries",
            "targets": [{
              "expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{app="my-app"}[5m])) by (le))"
            }]
          }
        ]
      }
    }

grafana_dashboard: "1" 레이블을 가진 ConfigMap은 Grafana sidecar가 자동으로 감지하고 대시보드로 로드합니다. JSON 파일을 수동으로 업로드할 필요가 없습니다.

6. Alertmanager 알림 라우팅 설정

PrometheusRule로 알림 조건을 정의하고, Alertmanager가 Slack, PagerDuty, 이메일 등으로 라우팅합니다.

# PrometheusRule: 알림 조건 정의
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: my-app-alerts
  labels:
    release: monitoring
spec:
  groups:
    - name: my-app
      rules:
        - alert: HighErrorRate
          expr: |
            sum(rate(http_requests_total{app="my-app",status=~"5.."}[5m]))
            / sum(rate(http_requests_total{app="my-app"}[5m])) > 0.05
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "5xx 에러율 5% 초과"
            description: "{{ $labels.namespace }}/{{ $labels.pod }}: 에러율 {{ $value | humanizePercentage }}"

        - alert: PodCrashLooping
          expr: increase(kube_pod_container_status_restarts_total[1h]) > 3
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Pod 반복 재시작 감지"
# Alertmanager 라우팅 (values-monitoring.yaml)
alertmanager:
  config:
    route:
      receiver: "slack-default"
      group_by: ["alertname", "namespace"]
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 4h
      routes:
        - receiver: "pagerduty-critical"
          match:
            severity: critical
    receivers:
      - name: "slack-default"
        slack_configs:
          - api_url: "https://hooks.slack.com/services/..."
            channel: "#alerts"
            title: "{{ .GroupLabels.alertname }}"
            text: "{{ range .Alerts }}{{ .Annotations.summary }}n{{ end }}"
      - name: "pagerduty-critical"
        pagerduty_configs:
          - service_key: "your-pagerduty-key"

group_by는 같은 alertname과 namespace의 알림을 묶어 하나의 메시지로 보냅니다. Pod가 100개인 서비스에서 동시에 에러가 나면, 100건이 아니라 1건의 그룹 알림만 전송됩니다.

7. 운영에서 자주 하는 실수 5가지

  1. retention과 스토리지 미설정 — 기본 retention은 15일이지만 PV 없이 emptyDir를 쓰면 Pod 재시작 시 전체 메트릭이 날아갑니다. 반드시 PersistentVolume을 설정하세요.
  2. ServiceMonitor 레이블 불일치release: monitoring 레이블이 없으면 Prometheus가 ServiceMonitor를 무시합니다. 메트릭이 안 들어올 때 가장 먼저 확인할 포인트입니다.
  3. cardinality 폭발 — 사용자 ID나 요청 URL을 레이블로 넣으면 메트릭 시리즈가 기하급수적으로 증가하고, Prometheus 메모리가 터집니다. 레이블 값은 반드시 유한한 집합이어야 합니다.
  4. 알림 임계값을 너무 민감하게 설정 — CPU 50% 초과로 알림을 걸면 매일 수십 건의 알림이 오고, 팀이 알림을 무시하게 됩니다. SLO 기반으로 에러율, 지연시간 중심의 알림을 설계하세요.
  5. Grafana 대시보드를 UI에서만 수정 — Pod 재시작이나 업그레이드 시 사라집니다. ConfigMap이나 Git으로 대시보드를 관리하고, Grafana sidecar로 자동 프로비저닝하세요.

마무리

Prometheus + Grafana는 Kubernetes 모니터링의 사실상 표준이며, kube-prometheus-stack으로 빠르게 구축할 수 있습니다. ServiceMonitor로 앱 메트릭을 자동 수집하고, PromQL로 핵심 지표를 추출하며, Alertmanager로 적절한 채널에 알림을 라우팅하면 운영 가시성을 체계적으로 확보할 수 있습니다.

Kubernetes 운영을 더 깊이 다루고 싶다면, ArgoCD GitOps 실전 가이드Kubernetes HPA 심화 가이드도 함께 참고해보세요.

Prometheus 알림 룰 + Grafana 대시보드 템플릿이 필요하신가요?
이 글에서 다룬 RED 메트릭 대시보드 JSON, PrometheusRule YAML, Alertmanager 라우팅 설정을 바로 적용할 수 있는 템플릿 세트를 준비했습니다. 아래 댓글로 “모니터링 템플릿”이라고 남겨주시면 보내드립니다.

📥 관련 무료 이북

쿠버네티스 입문 가이드 — 실전 가이드 무료 제공

무료로 받기 →

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux