서비스는 느려졌는데 CPU, 에러율, 지연 시간 중 무엇이 먼저 나빠졌는지 아무도 모릅니다. Kubernetes에서는 파드가 자주 바뀌기 때문에 정적 모니터링 설정만으로는 놓치는 대상이 생깁니다. Prometheus는 동적인 클러스터 환경에서 메트릭을 수집하고 알림의 근거를 제공합니다.
Kubernetes 모니터링 — Prometheus Operator와 Grafana
프로덕션 클러스터에서 "API가 느린 것 같다"는 제보가 들어왔습니다. 개발자들이 추측으로 서비스를 바라보는 동안, 모니터링 대시보드가 있다면 30초 안에 원인을 찾을 수 있습니다. 어느 서비스의 응답시간 P99가 튀었는지, 어느 파드의 CPU가 포화 상태인지, 에러율이 정상 범위를 벗어난 시점이 언제인지. Kubernetes 환경에서 파드는 수시로 재시작되고 IP가 바뀝니다. Prometheus Operator는 이 동적인 환경에서 수집 대상을 자동으로 추적합니다. ServiceMonitor 하나를 만들면 해당 서비스의 파드가 몇 개로 늘어나든 자동으로 수집 대상에 포함됩니다.
kube-prometheus-stack을 처음부터 설치하고 커스텀 애플리케이션의 메트릭을 수집하여 Grafana에서 시각화합니다. 알림 규칙 작성과 Alertmanager 라우팅까지 운영 환경 수준의 모니터링 스택을 구성합니다.
- 1kube-prometheus-stack — Prometheus Operator + Grafana + Alertmanager 일괄 배포
- 2ServiceMonitor로 파드 메트릭 수집 자동화
- 3PodMonitor와 ServiceMonitor의 차이
- 4PromQL 기본 — rate(), sum(), by(), histogram_quantile()
- 5PrometheusRule로 알림 규칙 코드로 관리
- 6Grafana 대시보드 프로비저닝
kube-prometheus-stack은 Prometheus, Grafana, Alertmanager, 각종 exporter를 포함하여 약 1-2GB RAM이 필요합니다. 로컬 환경에서는 minikube를 --memory 4096 이상으로 시작하거나, kind 클러스터를 사용하세요.
kubectl cluster-infokubectl create namespace monitoringhelm repo add prometheus-community https://prometheus-community.github.io/helm-charts && helm repo updatekubectl top nodes 2>/dev/null || kubectl get nodes -o widehelm uninstall kube-prometheus-stack -n monitoring && kubectl delete namespace monitoring
kube-prometheus-stack — 표준 모니터링 스택 배포
Prometheus를 직접 설치하고 Grafana를 별도로 설치하다 보면 버전 호환성 문제가 발생하고, Alertmanager 연동, exporter 추가, scrape 설정마다 파일을 수동으로 수정해야 합니다. 클러스터 컴포넌트 수가 늘어날수록 설정 파일이 비대해지고 누락이 생깁니다. kube-prometheus-stack은 이 모든 컴포넌트를 하나의 Helm Chart로 묶어 초기 설치와 업그레이드를 단순화합니다. Prometheus Operator가 포함되어 있어 이후 수집 대상 추가와 알림 규칙 관리를 Kubernetes 리소스(CRD)로 선언적으로 처리할 수 있습니다. 이 CB에서는 kube-prometheus-stack 설치와 기본 제공 대시보드 확인까지의 절차를 다룹니다. 개별 설치보다 훨씬 빠르게 프로덕션 수준의 모니터링 스택을 구성할 수 있습니다.

스택 구성 요소
kube-prometheus-stack 설치 후 실행되는 컴포넌트:
├── prometheus-operator — Prometheus/Alertmanager CRD Controller
├── prometheus — 메트릭 수집 및 저장 (StatefulSet)
├── alertmanager — 알림 라우팅 및 그룹핑 (StatefulSet)
├── grafana — 메트릭 시각화 (Deployment)
├── kube-state-metrics — Kubernetes 오브젝트 상태 메트릭
├── node-exporter — 노드 시스템 메트릭 (DaemonSet)
└── prometheus-adapter — HPA용 커스텀 메트릭 API
설치
# values 파일 생성 (리소스 절약 설정)
cat > prometheus-values.yaml << 'EOF'
# Grafana 설정
grafana:
adminPassword: "admin123" # 실습용 패스워드
service:
type: NodePort # 로컬 접근용 (프로덕션에서는 Ingress 사용)
persistence:
enabled: false # 실습용 (프로덕션에서는 true)
# Prometheus 설정
prometheus:
prometheusSpec:
retention: 15d # 메트릭 보존 기간
storageSpec:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 10Gi
# 모든 네임스페이스의 ServiceMonitor를 허용
serviceMonitorSelectorNilUsesHelmValues: false
podMonitorSelectorNilUsesHelmValues: false
# Alertmanager 설정
alertmanager:
alertmanagerSpec:
storage:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 2Gi
EOF
# 스택 설치
helm install kube-prometheus-stack \
prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
-f prometheus-values.yaml \
--wait \
--timeout 10m
# 설치 확인
kubectl get pods -n monitoring
# NAME READY STATUS
# alertmanager-kube-prometheus-stack-alertmanager-0 2/2 Running
# kube-prometheus-stack-grafana-7d9f6b8d4c-xk2p9 3/3 Running
# kube-prometheus-stack-kube-state-metrics-xxx 1/1 Running
# kube-prometheus-stack-operator-xxx 1/1 Running
# prometheus-kube-prometheus-stack-prometheus-0 2/2 Running
# kube-prometheus-stack-prometheus-node-exporter-xxx 1/1 Running (각 노드)
- NAME—조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
- STATUS/READY—Running, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
- RESTARTS/EVENTS—재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.
Grafana 접근
# NodePort로 접근 (minikube/kind)
kubectl get svc -n monitoring kube-prometheus-stack-grafana
# NAME TYPE CLUSTER-IP PORT(S)
# kube-prometheus-stack-grafana NodePort 10.96.x.x 80:32xxx/TCP
# minikube의 경우
minikube service kube-prometheus-stack-grafana -n monitoring
# 또는 포트포워딩으로 접근
kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80 &
# → http://localhost:3000 접속 (admin / admin123)
기본 제공되는 대시보드 확인
Grafana 로그인 후 Dashboards → Browse:
- Kubernetes / Cluster — 클러스터 전체 리소스 사용률
- Kubernetes / Nodes — 노드별 CPU/Memory/Disk
- Kubernetes / Workloads — Deployment/DaemonSet 상태
- Kubernetes / Pods — 파드별 상세 메트릭
- Kubernetes / Namespaces — 네임스페이스별 리소스
- Node Exporter / Full — 노드 상세 시스템 메트릭
ServiceMonitor — 파드 메트릭 자동 수집 설정
새로운 마이크로서비스를 배포할 때마다 prometheus.yml의 scrape_configs를 수동으로 수정하고 Prometheus를 재시작하거나 reload를 보내야 합니다. 파드 IP가 바뀌면 수집이 끊기고, 새 파드가 추가되면 설정을 또 변경해야 합니다. 서비스가 수십 개를 넘어서면 이 방식은 유지보수가 불가능해집니다. ServiceMonitor는 이 문제를 해결합니다. Prometheus Operator의 핵심 가치는 ServiceMonitor입니다. 기존 Prometheus에서 파드를 추가하려면 prometheus.yml의 scrape_configs를 수동으로 수정하고 재로드해야 했습니다. ServiceMonitor는 이를 Kubernetes 리소스로 선언하면 Operator가 자동으로 Prometheus 설정에 반영합니다.

메트릭을 노출하는 샘플 애플리케이션 배포
# sample-app.yaml — /metrics 엔드포인트가 있는 nginx
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-app
namespace: default
labels:
app: sample-app
spec:
replicas: 2
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
version: "1.0"
spec:
containers:
- name: app
image: quay.io/brancz/prometheus-example-app:v0.3.0
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: 50m
memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
name: sample-app
namespace: default
labels:
app: sample-app # ServiceMonitor가 이 레이블로 찾음
spec:
selector:
app: sample-app
ports:
- name: http # 포트 이름이 ServiceMonitor의 port와 일치해야 함
port: 8080
targetPort: 8080
ServiceMonitor 정의
# servicemonitor-sample.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: sample-app
namespace: default
labels:
release: kube-prometheus-stack # Prometheus가 이 레이블로 ServiceMonitor를 선택
spec:
# scrape할 Service를 레이블로 선택
selector:
matchLabels:
app: sample-app # Service의 labels.app: sample-app
# 여러 네임스페이스에 걸친 Service 선택 (기본: 동일 네임스페이스)
namespaceSelector:
matchNames:
- default
- production
# scrape 엔드포인트 설정
endpoints:
- port: http # Service의 port 이름과 일치
path: /metrics # 메트릭 경로 (기본값)
interval: 30s # scrape 간격
scrapeTimeout: 10s # 타임아웃
# TLS 설정 (HTTPS 엔드포인트의 경우)
# tlsConfig:
# insecureSkipVerify: true
kubectl apply -f sample-app.yaml
kubectl apply -f servicemonitor-sample.yaml
# Prometheus UI에서 타겟 확인 (약 1분 후)
kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-stack-prometheus 9090:9090 &
# → http://localhost:9090/targets 접속
# status=UP으로 표시되면 수집 중
PodMonitor vs ServiceMonitor
# Service 없이 파드를 직접 scrape할 때 PodMonitor 사용
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: batch-jobs
namespace: default
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app: batch-processor
podMetricsEndpoints:
- port: metrics # 파드 containerPort 이름
path: /metrics
interval: 60s # 배치 잡은 긴 간격
# ServiceMonitor: Service → Endpoints를 통해 파드 자동 추적
# PodMonitor: Service 없이 파드를 직접 레이블로 선택
PromQL 기본 — 메트릭 조회와 집계
Grafana에서 메트릭을 보려는데 쿼리 박스 앞에서 막힙니다. Counter는 계속 증가하는 숫자라 그냥 쓰면 의미가 없고, Histogram에서 P99를 뽑으려면 특별한 함수가 필요한데 어디서 시작해야 할지 모릅니다. PromQL을 모르면 Prometheus는 데이터 창고에 불과하고 실제 운영 판단에 쓰이지 못합니다. Prometheus는 자체 쿼리 언어 PromQL을 사용하며, 시계열 데이터에 특화된 몇 가지 핵심 함수를 익히면 대부분의 운영 질문에 답할 수 있습니다. 이 CB에서는 Counter, Gauge, Histogram 타입별 쿼리 패턴과 집계 함수 사용법을 다룹니다. 시계열 데이터에 특화되어 있어 "지난 5분간 초당 에러율", "노드별 메모리 사용률 상위 5개" 같은 쿼리를 간결하게 표현합니다.
메트릭 타입별 쿼리 패턴
# --- Gauge (현재 상태값) ---
# 현재 메모리 사용률 (%)
100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100)
# 파드별 메모리 사용량 (MiB)
container_memory_working_set_bytes{container!=""} / 1024 / 1024
# --- Counter (단조 증가값) — rate()로 변화율 계산 ---
# 초당 HTTP 요청 수
rate(http_requests_total[5m])
# 초당 에러 요청 수 (5xx)
rate(http_requests_total{status=~"5.."}[5m])
# --- Histogram — 분위수 계산 ---
# HTTP 응답시간 P99 (99th percentile)
histogram_quantile(
0.99,
rate(http_request_duration_seconds_bucket[5m])
)
# HTTP 응답시간 P50, P95, P99 비교
histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
집계 함수 (by, without)
# 서비스별 초당 요청 수 합계
sum(rate(http_requests_total[5m])) by (service)
# 결과:
# {service="auth"} → 45.2
# {service="payment"} → 12.1
# {service="catalog"} → 150.3
# 네임스페이스별 파드 수
count(kube_pod_info) by (namespace)
# 파드별 CPU 사용률 상위 5개
topk(5, rate(container_cpu_usage_seconds_total{container!=""}[5m]))
# 노드별 CPU 사용률 (전체 대비 %)
100 * (1 - avg by (instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
))
Kubernetes 환경에서 자주 쓰는 쿼리
# Deployment 파드 가용률
kube_deployment_status_replicas_available / kube_deployment_spec_replicas
# 파드 재시작 횟수 증가율 (크래시 루프 감지)
rate(kube_pod_container_status_restarts_total[15m]) * 60
# OOMKilled 파드 감지
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}
# PersistentVolume 사용률
(
kubelet_volume_stats_used_bytes /
kubelet_volume_stats_capacity_bytes
) * 100
# 노드별 할당 가능 CPU 대비 요청 비율
sum(kube_pod_container_resource_requests{resource="cpu"}) by (node) /
sum(kube_node_status_allocatable{resource="cpu"}) by (node) * 100
Grafana에서 PromQL 사용
1. Grafana → Explore (나침반 아이콘) → Prometheus 데이터 소스 선택
2. 쿼리 입력 후 Run Query
3. 시각화: Graph, Stat, Table, Heatmap 등 선택
4. Dashboard에 패널 추가: "Add to Dashboard" 버튼
PrometheusRule — 알림 규칙 코드로 관리
알림 규칙을 UI에서 직접 설정하면 추적이 안 됩니다. PrometheusRule CRD로 알림 규칙을 YAML 파일로 관리하면 Git에서 히스토리를 추적하고, 코드 리뷰를 거치고, CI/CD로 배포할 수 있습니다.
PrometheusRule 정의
# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: app-alerts
namespace: default
labels:
release: kube-prometheus-stack # Prometheus가 선택하는 레이블
spec:
groups:
- name: availability # 알림 그룹 이름
interval: 30s # 평가 간격
rules:
# 파드 크래시 루프 감지
- alert: PodCrashLooping
expr: |
rate(kube_pod_container_status_restarts_total[15m]) * 60 > 0
for: 5m # 5분간 지속될 때만 발화
labels:
severity: critical
team: platform
annotations:
summary: "파드 {{ $labels.pod }}가 크래시 루프 중"
description: |
네임스페이스 {{ $labels.namespace }}의 파드
{{ $labels.pod }} (컨테이너: {{ $labels.container }})가
지난 15분간 {{ printf "%.1f" $value }}회/분 재시작되고 있습니다.
# 고에러율 감지
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
> 0.05
for: 2m
labels:
severity: warning
team: backend
annotations:
summary: "{{ $labels.service }} 에러율 {{ printf \"%.1f\" $value | humanizePercentage }}"
description: "서비스 {{ $labels.service }}의 5xx 에러율이 5%를 초과했습니다."
# 높은 응답시간 감지
- alert: HighResponseTime
expr: |
histogram_quantile(
0.99,
rate(http_request_duration_seconds_bucket[5m])
) > 1.0
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.service }} P99 응답시간 1초 초과"
description: "{{ $labels.service }}의 P99 응답시간이 {{ printf \"%.2f\" $value }}초입니다."
- name: resources
rules:
# 노드 메모리 고갈 임박
- alert: NodeMemoryPressure
expr: |
100 - (
node_memory_MemAvailable_bytes /
node_memory_MemTotal_bytes * 100
) > 85
for: 10m
labels:
severity: warning
annotations:
summary: "노드 {{ $labels.instance }} 메모리 부족"
description: "메모리 사용률 {{ printf \"%.1f\" $value }}%"
# PV 용량 임박
- alert: PersistentVolumeAlmostFull
expr: |
(
kubelet_volume_stats_used_bytes /
kubelet_volume_stats_capacity_bytes
) * 100 > 80
for: 15m
labels:
severity: warning
annotations:
summary: "PV {{ $labels.persistentvolumeclaim }} 용량 {{ printf \"%.1f\" $value }}% 사용"
Alertmanager 라우팅 설정
# alertmanager-config.yaml (Helm values에 포함하거나 Secret으로 배포)
alertmanager:
config:
global:
resolve_timeout: 5m
slack_api_url: "https://hooks.slack.com/services/xxx/yyy/zzz"
route:
group_by: ["alertname", "namespace"]
group_wait: 30s # 그룹 내 첫 알림 대기 (더 쌓이길 기다림)
group_interval: 5m # 같은 그룹 알림 간격
repeat_interval: 12h # 해결 안 됐을 때 반복 간격
receiver: "slack-general"
routes:
- match:
severity: critical
receiver: "pagerduty-oncall"
- match:
team: backend
receiver: "slack-backend"
receivers:
- name: "slack-general"
slack_configs:
- channel: "#alerts"
title: "{{ .GroupLabels.alertname }}"
text: "{{ range .Alerts }}{{ .Annotations.description }}{{ end }}"
- name: "slack-backend"
slack_configs:
- channel: "#backend-alerts"
send_resolved: true
- name: "pagerduty-oncall"
pagerduty_configs:
- routing_key: "YOUR_INTEGRATION_KEY"
severity: "{{ .CommonLabels.severity }}"
kubectl apply -f prometheus-rules.yaml
# 알림 규칙이 Prometheus에 반영됐는지 확인
# http://localhost:9090/rules → "app-alerts" 그룹 확인
실습 — 전체 수집 파이프라인 구성
1단계: kube-prometheus-stack 설치
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus-stack \
prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set grafana.adminPassword=admin123 \
--set grafana.service.type=NodePort \
--set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \
--wait
kubectl get pods -n monitoring
2단계: 샘플 앱 배포
kubectl apply -f - << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-app
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: app
image: quay.io/brancz/prometheus-example-app:v0.3.0
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: sample-app
namespace: default
labels:
app: sample-app
spec:
selector:
app: sample-app
ports:
- name: http
port: 8080
targetPort: 8080
EOF
3단계: ServiceMonitor 적용 및 수집 확인
kubectl apply -f - << 'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: sample-app
namespace: default
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app: sample-app
endpoints:
- port: http
path: /metrics
interval: 15s
EOF
# Prometheus 포트포워딩 후 타겟 확인
kubectl port-forward -n monitoring \
svc/prometheus-operated 9090:9090 &
# 1-2분 후 http://localhost:9090/targets에서
# "default/sample-app/0" 타겟이 UP 상태인지 확인
4단계: PromQL로 메트릭 조회
# Prometheus UI (http://localhost:9090/graph)에서 쿼리 실행:
# 샘플 앱의 초당 요청 수
rate(http_requests_total[1m])
# 클러스터 전체 파드 수 (네임스페이스별)
count(kube_pod_info) by (namespace)
# 노드 CPU 사용률
100 * (1 - avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])))
5단계: Grafana에서 대시보드 확인
# Grafana 포트포워딩
kubectl port-forward -n monitoring \
svc/kube-prometheus-stack-grafana 3000:80 &
# http://localhost:3000 접속 (admin / admin123)
# Dashboards → Kubernetes / Workloads → namespace=default 선택
# → sample-app Deployment 메트릭 확인
문제 상황
# ServiceMonitor를 적용했는데 Prometheus UI의 /targets에서 보이지 않거나
# "Unknown" 또는 "0/0" 타겟으로 표시됨
# Prometheus Configuration 확인
# http://localhost:9090/config 에서 job_name 목록에 서비스가 없음
원인 1: ServiceMonitor의 레이블이 Prometheus selector와 불일치 (가장 흔함)
# Prometheus가 ServiceMonitor를 선택하는 방식 확인
kubectl get prometheus -n monitoring -o yaml | \
grep -A 10 "serviceMonitorSelector:"
# 출력 예시:
# serviceMonitorSelector:
# matchLabels:
# release: kube-prometheus-stack ← 이 레이블이 ServiceMonitor에 있어야 함
# ServiceMonitor의 레이블 확인
kubectl get servicemonitor sample-app -n default -o yaml | grep "labels:" -A 5
# labels:
# release: kube-prometheus-stack ← 일치 여부 확인
# 해결: ServiceMonitor에 올바른 레이블 추가
kubectl patch servicemonitor sample-app -n default \
--type=merge \
-p '{"metadata":{"labels":{"release":"kube-prometheus-stack"}}}'
원인 2: ServiceMonitor의 selector가 Service 레이블과 불일치
# Service의 실제 레이블 확인
kubectl get svc sample-app -n default --show-labels
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
# sample-app ClusterIP 10.96.x.x <none> 8080/TCP 5m app=sample-app
# ServiceMonitor의 selector 확인
kubectl get servicemonitor sample-app -n default -o jsonpath='{.spec.selector}'
# {"matchLabels":{"app":"sample-app-v2"}} ← "app=sample-app"과 불일치!
# 해결: selector 수정
kubectl patch servicemonitor sample-app -n default \
--type=merge \
-p '{"spec":{"selector":{"matchLabels":{"app":"sample-app"}}}}'
원인 3: ServiceMonitor의 port 이름이 Service port 이름과 불일치
# Service의 port 이름 확인
kubectl get svc sample-app -n default -o yaml | grep -A 5 "ports:"
# ports:
# - name: http ← Service port 이름
# port: 8080
# ServiceMonitor의 endpoint port 이름 확인
kubectl get servicemonitor sample-app -n default -o yaml | grep -A 3 "endpoints:"
# endpoints:
# - port: web ← "http"와 불일치!
# 해결: ServiceMonitor endpoint port 이름을 "http"로 수정
원인 4: 애플리케이션이 /metrics를 노출하지 않음
# 파드에서 직접 /metrics 엔드포인트 테스트
kubectl exec -n default \
$(kubectl get pod -n default -l app=sample-app -o name | head -1) \
-- wget -qO- http://localhost:8080/metrics | head -20
# 출력이 없거나 404이면 앱이 메트릭을 노출하지 않음
# 해결: 앱에 Prometheus 메트릭 라이브러리 추가 필요
Prometheus UI에서 디버깅
1. http://localhost:9090/targets
→ "Unhealthy" 탭에서 오류 메시지 확인
→ "scrape_error: dial tcp ...: connection refused" = 앱이 포트를 안 엶
→ "scrape_error: 404" = /metrics 경로 없음
2. http://localhost:9090/service-discovery
→ "Discovered" 목록에서 ServiceMonitor가 발견됐는지 확인
→ "Dropped" 목록에서 제외된 레이블 확인
3. http://localhost:9090/config
→ ServiceMonitor가 Prometheus 설정으로 변환됐는지 확인
배경
플랫폼팀 가이드: 팀에서 새 마이크로서비스를 배포할 때마다 모니터링 설정이 빠지는 일이 반복됐습니다. 배포 후 장애가 나면 메트릭이 없어 대응이 느렸습니다. 신규 서비스 배포 체크리스트를 만들어 표준화했습니다.
서비스 측 준비 (개발팀)
[ ] 애플리케이션에 Prometheus 메트릭 라이브러리 추가
- Node.js: prom-client
- Python: prometheus-client
- Go: prometheus/client_golang
- Java: micrometer-prometheus
[ ] /metrics 엔드포인트 노출 (기본 포트 또는 별도 포트)
[ ] 최소 메트릭 세트 정의:
- http_requests_total (Counter, status/method/path 레이블)
- http_request_duration_seconds (Histogram)
- 비즈니스 메트릭 (주문 수, 결제 성공률 등)
[ ] 컨테이너 포트에 이름 부여 (ServiceMonitor에서 이름으로 참조)
ports:
- name: http ← 이름 필수
containerPort: 8080
- name: metrics ← 메트릭 포트 별도 분리 시
containerPort: 9090
인프라 측 준비 (플랫폼팀)
# 서비스당 최소 3개 리소스 배포:
# 1. Service (레이블 포함)
metadata:
labels:
app: new-service
team: backend
# 2. ServiceMonitor
spec:
selector:
matchLabels:
app: new-service
endpoints:
- port: metrics
# 3. PrometheusRule (최소 알림 규칙)
rules:
- alert: NewServiceDown
expr: up{job="new-service"} == 0
for: 2m
- alert: NewServiceHighErrorRate
expr: rate(http_requests_total{status=~"5..",service="new-service"}[5m]) > 0.1
for: 5m
Grafana 대시보드 프로비저닝
# ConfigMap으로 대시보드 자동 프로비저닝
apiVersion: v1
kind: ConfigMap
metadata:
name: new-service-dashboard
namespace: monitoring
labels:
grafana_dashboard: "1" # grafana sidecar가 자동으로 로드
data:
new-service.json: |
{
"title": "New Service Metrics",
"panels": [...]
}
이 체크리스트를 Helm Chart에 포함하면 서비스 배포와 모니터링 설정이 동시에 이루어집니다.
핵심 요약
| 개념 | 설명 |
|---|---|
| kube-prometheus-stack | Prometheus + Grafana + Alertmanager + exporter 일괄 Helm Chart |
| Prometheus Operator | ServiceMonitor/PrometheusRule CRD를 감시하고 Prometheus 설정을 자동 업데이트 |
| ServiceMonitor | labelSelector로 Service를 선택하여 파드 scrape 대상 자동 등록 |
| PodMonitor | Service 없이 파드를 직접 레이블로 선택하여 scrape |
rate(counter[window]) | Counter의 초당 변화율 계산 — Counter는 항상 rate()로 사용 |
histogram_quantile(φ, ...) | Histogram에서 분위수(P50/P95/P99) 계산 |
sum() by (label) | 레이블 기준으로 메트릭 집계 |
| PrometheusRule | 알림 규칙을 YAML로 코드화 — Git 관리 및 코드 리뷰 가능 |
| Alertmanager | 알림 수신 → 그룹핑 → 라우팅 → Slack/PagerDuty 전송 |
| 타겟 디버깅 | /targets, /service-discovery, /config 순으로 Prometheus UI 확인 |