본문으로 건너뛰기

10.2 메트릭

클러스터의 메트릭 질문은 두 갈래로 나뉜다. “이 컨테이너가 CPU와 메모리를 얼마나 쓰고 있는가"라는 사용량 질문과, “이 Deployment의 replica가 몇 개 준비됐고 이 Pod가 몇 번 재시작했는가"라는 상태 질문이다. Kubernetes 생태계는 이 둘을 서로 다른 컴포넌트로 답한다. 사용량은 cAdvisor와 metrics-server의 계보가, 상태는 kube-state-metrics가 담당한다. 이 구분을 알아야 어떤 지표를 어디서 가져올지 헤매지 않는다.

리소스 메트릭 파이프라인

사용량 수치의 원천은 cgroup이다. kubelet에 내장된 cAdvisor가 노드의 cgroup 파일시스템에서 컨테이너별 CPU 사용 시간과 메모리 워킹셋을 읽어, kubelet의 /metrics/cadvisor/metrics/resource 엔드포인트로 노출한다. container_cpu_usage_seconds_total, container_memory_working_set_bytes 같은 지표가 여기서 나온다.

metrics-server는 이 kubelet 엔드포인트를 주기적으로(기본 15초) 수집해 리소스 메트릭 API(metrics.k8s.io)로 제공하는 클러스터 애드온이다. kubectl top이 바로 이 API의 소비자다.

kubectl top nodes
kubectl top pods -n production --containers
    flowchart TD
  CG["cgroup"] --> CA["cAdvisor<br/>(kubelet 내장)"]
  CA --> MS["metrics-server"]
  MS --> API["리소스 메트릭 API"]
  API --> TOP["kubectl top"]
  API --> HPA["HPA"]
  

metrics-server의 성격을 정확히 아는 것이 중요하다. 최신 값만 메모리에 유지하고 이력을 남기지 않으므로, “지금 얼마나 쓰는가"에는 답해도 “어제 이 시간에는 얼마였나"에는 답하지 못한다. 공식 문서도 metrics-server를 autoscaling 이외의 용도로 쓰지 말고 모니터링 솔루션의 지표 원천으로 삼지 말라고 안내한다. 이력이 필요한 모니터링은 Prometheus 같은 시계열 시스템이 kubelet 엔드포인트를 직접 수집하는 구성으로 해결한다.

kube-state-metrics: 오브젝트 상태를 메트릭으로

kube-state-metrics는 이름 때문에 metrics-server와 자주 혼동되지만 하는 일이 전혀 다르다. API server를 watch하면서 Kubernetes 오브젝트의 상태를 Prometheus 형식 메트릭으로 변환하는 서비스이며, 리소스 사용량은 다루지 않는다.

kube_pod_status_phase{phase="Pending"}           # Pod가 어느 phase에 있는가
kube_pod_container_status_restarts_total         # 컨테이너 재시작 누계
kube_deployment_status_replicas_available        # Deployment의 가용 replica 수
kube_node_status_condition{condition="Ready"}    # 노드 Ready 조건

두 컴포넌트를 대조하면 다음과 같다.

metrics-serverkube-state-metrics
데이터 원천kubelet(cAdvisor)API server watch
다루는 것CPU/메모리 사용량오브젝트 상태
저장인메모리 최신 값저장 없음(변환만)
주 소비자kubectl top, HPAPrometheus

kube-state-metrics 자체는 아무것도 저장하지 않는다. /metrics 엔드포인트를 노출할 뿐이고, 축적과 질의는 이를 수집하는 Prometheus의 몫이다.

Prometheus 스크레이프와 ServiceMonitor

Prometheus는 pull 모델로 동작한다. 대상이 /metrics HTTP 엔드포인트로 지표를 노출하면 Prometheus가 주기적으로 가져간다(스크레이프). Kubernetes 환경에서 이 모델의 관건은 대상 목록의 유지다. Pod는 계속 생기고 사라지므로 정적 설정으로는 따라갈 수 없고, Prometheus의 kubernetes_sd_configs가 API server에서 Pod와 Service를 조회해 스크레이프 대상을 동적으로 발견한다.

Prometheus Operator를 쓰는 환경에서는 이 발견 규칙을 ServiceMonitor라는 CRD로 선언한다. label selector로 Service를 선택하면 Operator가 해당하는 스크레이프 설정을 생성해 Prometheus에 반영한다.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: web-app
spec:
  selector:
    matchLabels:
      app: web
  endpoints:
    - port: metrics
      interval: 30s

애플리케이션 배포와 모니터링 등록이 같은 문법(label과 selector)으로 묶이므로, 새 서비스를 추가할 때 Prometheus 설정 파일을 직접 수정할 일이 없어진다.

무엇을 경보로 삼나

지표가 수천 개 쌓여도 경보 없이는 아무도 보지 않는다. 반대로 모든 것에 경보를 걸면 노이즈에 묻혀 진짜 장애를 놓친다. 우선순위가 높은 것은 세 부류다.

첫째는 용량이다. 노드 CPU/메모리 포화, PVC 사용률, 노드별 Pod 수 한도가 여기 속한다. 용량 문제는 한 번 터지면 여러 워크로드가 동시에 영향을 받고, eviction이 연쇄되면 수습 비용이 커진다. 여유가 남아 있을 때 미리 알아야 하는 대표적인 지표다.

둘째는 재시작 횟수다. kube_pod_container_status_restarts_total의 증가율에 경보를 걸면 CrashLoopBackOff로 굳어지기 전의 간헐적 크래시가 드러난다. 재시작 자체는 Kubernetes가 복구해 주므로 서비스 증상이 없을 때가 많은데, 그래서 오히려 경보 없이는 아무도 모른 채 지나간다.

셋째는 Pending 체류다. kube_pod_status_phase{phase="Pending"}이 일정 시간 이상 지속되면 스케줄링이 막힌 것이다. 자원 부족이든 taint 불일치든 PVC 대기든 사람이 개입해야 풀리는 경우가 대부분이라 경보 가치가 높다. requests와 스케줄링의 배경은 Part VIII. 스케줄링과 자원 관리에서 다뤘고, 패턴별 진단 절차는 이 Part의 마지막 챕터에 있다.

정리

사용량은 cgroup에서 cAdvisor와 metrics-server를 거쳐 kubectl top으로 이어지는 짧은 파이프라인이고, 이력이 없는 실시간 값이다. 상태는 kube-state-metrics가 API server의 오브젝트를 메트릭으로 변환하고 Prometheus가 수집해 축적한다. 경보는 용량, 재시작 증가율, Pending 체류처럼 사람이 개입해야 하는 신호부터 건다.