학습 목표
K8s 메트릭 3층(클러스터 상태 · 컨테이너 자원 · 앱 RED)을 구분해 설명할 수 있다.
kube-state-metrics와 cAdvisor(kubelet 경유)가 무엇을 노출하는지 설명할 수 있다.
metrics-server(HPA)와 관측 스택(Prometheus)의 역할 차이를 설명할 수 있다.
Prometheus kubernetes_sd·ServiceMonitor로 K8s target을 scrape하도록 구성할 수 있다.
장애 triage에서 어느 층의 메트릭·로그·트레이스를 먼저 볼지 판단할 수 있다.
문제 상황
kubectl get pods— 모두 Running — 그런데 checkout 5xx 100%- Pod phase와 앱 SLI는 다른 신호
- Grafana CPU 패널 90% — on-call이 scale out — 근본은 DB connection pool
- 인프라 메트릭만 보면 앱 장애를 놓친다
- Deployment replicas 3인데 실제 ready 1 — HPA는 Unknown
- kube-state-metrics 없으면 desired vs ready gap이 안 보인다
- Prometheus target up=0 — Pod IP 바뀔 때마다 수동 yaml 수정
- kubernetes_sd·annotation 표준 없음
- metrics-server
kubectl top은 되는데 SLO burn rate 알람 없음- HPA용 summary API ≠ 시계열 관측
Platform에서 Pod·HPA·metrics-server를 봤다. Observability 시리즈에서 Prometheus·Grafana·SLO·runbook까지 갖췄다. K8s 위에서 신호를 한곳에 모으는 마지막 본편이다.
1. K8s 메트릭 3층

| 층 | 수집원 | 대표 메트릭 | 질문 |
|---|---|---|---|
| Cluster state | kube-state-metrics | kube_pod_status_phase, kube_deployment_status_replicas_available |
desired 맞나? CrashLoop? |
| Container resources | cAdvisor (kubelet :10250/metrics/cadvisor) |
container_cpu_usage_seconds_total, container_memory_working_set_bytes |
OOM·throttle·saturation? |
| Application | 앱 /metrics |
http_requests_total, histogram |
RED·SLO·사용자 영향? |
증상별 첫 화면 (예):
page "API 5xx" → 앱 RED (층 3) → trace/log (11편)
"Pod not ready" → kube-state-metrics (층 1)
OOMKilled / throttle → cAdvisor + limits (층 2)
- node-exporter — 노드 OS·디스크·네트워크 (USE). cAdvisor와 보완
- kubelet·apiserver·etcd 메트릭 — control plane 건강 (운영 클러스터)
- 앞서 RED·USE — 층 3은 서비스, 층 2·node는 인프라
2. metrics-server vs 관측 스택
| metrics-server | Prometheus + exporters | |
|---|---|---|
| 목적 | kubectl top, HPA Resource metric |
대시보드·SLO·알람·장기 보관 |
| 데이터 | kubelet summary API (최근 스냅) | 시계열 scrape·retention |
| 주기 | ~15s (HPA loop) | scrape_interval 15~30s + recording rule |
| 라벨 | namespace·pod (top용) | PromQL·relabel·join |
- Platform HPA —
requests.cpu+ metrics-server 필수 (스케줄링 편) - Custom metrics HPA — Prometheus adapter로 QPS·queue depth (Observability 연동)
- 같은 CPU 숫자를 두 시스템이 보더라도 용도가 다르다 — 대체가 아니라 병행
3. kube-state-metrics
kube-state-metrics(KSM) — API server 객체를 watch해 상태를 Prometheus 메트릭으로 노출한다.
| 메트릭 패밀리 | 의미 |
|---|---|
kube_pod_status_phase |
Pending / Running / Failed / … |
kube_pod_container_status_restarts_total |
컨테이너 restart 누적 |
kube_deployment_status_replicas_* |
desired · available · unavailable |
kube_node_status_condition |
Ready, MemoryPressure, … |
kube_job_status_failed |
Job 실패 |
# Ready 아닌 Pod (namespace=api)
sum(kube_pod_status_phase{phase!="Running", namespace="api"}) by (pod, phase)
# Deployment available < spec replicas
(
kube_deployment_spec_replicas
- kube_deployment_status_replicas_available
) > 0
- KSM은 클러스터당 1 (또는 sharded) — 고카디널리티 주의 (8편)
kube_*라벨에namespace,pod,deployment— runbook 필터에 사용- CrashLoopBackOff — restart metric ↑ +
kubectl logs(로그 편)
4. cAdvisor · kubelet
cAdvisor — kubelet에 내장. 컨테이너별 CPU·메모리·네트워크·filesystem 사용량.
| 메트릭 | 용도 |
|---|---|
container_cpu_usage_seconds_total |
CPU 사용 (rate → cores) |
container_memory_working_set_bytes |
OOM 판단에 가까운 working set |
container_spec_memory_limit_bytes |
limits와 비교 |
container_network_* |
Pod egress/ingress |
# memory working set / limit (Burstable OOM 위험)
container_memory_working_set_bytes{namespace="api", container="api"}
/ container_spec_memory_limit_bytes > 0.85
# CPU throttle (limits 있을 때)
rate(container_cpu_cfs_throttled_seconds_total{namespace="api"}[5m]) > 0
container="POD"·pause — 쿼리에서 제외 (container!="",container!="POD")- requests/limits 없으면 saturation 해석 불가 — Platform QoS와 함께
kubectl top≈ summary API — PromQL 추세·알람은 Prometheus 경로
5. Prometheus scrape on K8s

수집 경로
| Target | job / Operator | 포트·경로 |
|---|---|---|
| kube-state-metrics | static 또는 ServiceMonitor | :8080/metrics |
| node-exporter | DaemonSet + ServiceMonitor | :9100/metrics |
| kubelet/cAdvisor | kubernetes-nodes-cadvisor job |
apiserver proxy 또는 kubelet auth |
| 앱 Pod | kubernetes-pods SD 또는 PodMonitor |
annotation 또는 port name |
# Pod annotation (발췌) — code/pod-annotations.example
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
# kubernetes_sd — code/prometheus-k8s-scrape.yaml (발췌)
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
Prometheus Operator
운영에서는 kube-prometheus-stack — ServiceMonitor·PodMonitor CRD로 scrape 선언.
# code/servicemonitor-api.yaml (발췌)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: api
labels:
release: prometheus # Operator selector
spec:
selector:
matchLabels:
app: api
endpoints:
- port: metrics
interval: 15s
path: /metrics
| 방식 | 장점 |
|---|---|
| annotation + SD | 앱 팀이 Pod yaml만 수정 |
| ServiceMonitor | RBAC·네임스페이스별 분리, GitOps |
| PodMonitor | Service 없이 직접 Pod port |
- scrape 실패 →
up{job="..."} == 0— 앞서 Prometheus - relabel으로
pod,node,app— SLO·runbook 변수와 맞춤
6. 대시보드 · 알람 연결
Grafana 변수: $namespace, $deployment, $pod — kube-state + 앱 RED 한 보드.
| 패널 | PromQL / 소스 |
|---|---|
| Availability | 앱 RED error rate (12편 SLO) |
| Replicas gap | spec - available (KSM) |
| Restarts | increase(kube_pod_container_status_restarts_total[1h]) |
| Memory headroom | working set / limit (cAdvisor) |
| Burn rate | recording rule (12편) |
# alert annotation — runbook 연결 (13편)
annotations:
summary: "Deployment api ready replicas below spec for 10m"
runbook_url: "https://wiki.example/runbooks/deployment-not-ready"
dashboard: "https://grafana/d/k8s-api"
- 앞서 alert-runbook — K8s alert도 증상·first steps 필수
- Pod restart 알람 단독 page — cardinality·flapping (8편) — 임계값·
for:조정
더 많은 쿼리: code/k8s-promql.promql
7. 장애 triage 흐름

| 사용자 증상 | 1순위 | 2순위 | 3순위 |
|---|---|---|---|
| 5xx·latency | 앱 RED + trace (11편) | 최근 deploy annotation | Pod ready (KSM) |
| Intermittent fail | trace + log correlation | readiness probe | node NotReady |
| Slow only | histogram p99 | CPU throttle (cAdvisor) | node disk |
| Total outage | SLO burn (12편) | replicas available | ingress·Service (Platform) |
Mitigate (13편):
replicas 0 → rollback Deployment / scale previous
node drain → cordon + reschedule
OOM loop → limits↑ 임시 + traffic shift
- Infrastructure alert(CPU 80%) vs SLO page — severity impact 기준 (13편)
- control plane(api server down) — kubectl 자체 실패 — managed 클러스터 runbook 별도
8. 실무 체크리스트
| 항목 | 권장 |
|---|---|
| 3층 | KSM + cAdvisor + 앱 RED — 하나라도 빠지면 blind spot |
| HPA | metrics-server 유지 — Prometheus와 역할 분리 |
| scrape | ServiceMonitor 표준 + up 알람 |
| labels | namespace, app, pod — runbook·Grafana 변수 통일 |
| limits | requests/limits 설정 — cAdvisor 해석 전제 |
| cardinality | pod 라벨 join — high-cardinality alert 금지 (8편) |
| OTel | DaemonSet agent — 앱 trace + infra log (10편) |
kubectl top pods -n api # metrics-server (HPA 경로)
kubectl get --raw /api/v1/nodes/NODE/proxy/metrics/cadvisor | head
curl -s kube-state-metrics:8080/metrics | grep kube_deployment
promtool check rules rules/*.yaml
9. 정리
- K8s 관측은 cluster state(KSM) · container(cAdvisor) · app RED 3층
- metrics-server는 HPA·top — SLO·알람은 Prometheus 관측 스택
- scrape는 kubernetes_sd 또는 ServiceMonitor — Pod IP 변경에 내성
- triage는 사용자 impact(SLO) 먼저 — 인프라 CPU는 보조
- 다음 — Observability Series를 마치며 (에필로그)
다음에 다룰 것
- 시리즈 전체 개념 지도
- Observability 확장·학습 경로
해당 내용은 Observability Engineering (Charity Majors, Liz Fong-Jones, George Miranda), Prometheus Operator Documentation (prometheus-operator.dev), Kubernetes Documentation 의 내용을 기반으로 합니다.
'관측성' 카테고리의 다른 글
| Observability Series를 마치며 (0) | 2026.07.02 |
|---|---|
| 온콜과 인시던트 (0) | 2026.06.30 |
| SLO와 에러 버짓 (0) | 2026.06.29 |
| 트레이스와 로그·메트릭 연계 (0) | 2026.06.28 |
| OpenTelemetry (0) | 2026.06.27 |