Published on

EKS에서 kubectl top이 0%일 때 Metrics API 점검

Authors

서버/파드 리소스를 보려 kubectl top nodes, kubectl top pods를 실행했는데 값이 전부 0% 또는 0Mi로만 나오거나, 아예 Metrics API not available 류의 메시지가 뜨는 경우가 있습니다. EKS에서는 대개 metrics-server가 데이터를 못 가져오거나, kubelet(10250) 접근/인증이 깨져서 발생합니다. 이 글은 “왜 0으로 보이는지”를 원인별로 분해하고, 어디부터 어떤 명령으로 확인해야 하는지를 실전 체크리스트로 정리합니다.

> 참고로 서비스가 503인 케이스처럼 “리소스는 정상인데 관측만 깨진” 상황은 디버깅 포인트가 비슷합니다. 필요하면 EKS에서 Pod는 Running인데 503가 뜰 때 점검도 함께 보시면 좋습니다.

증상 패턴 먼저 분류하기

kubectl top 문제가 전부 같은 원인은 아닙니다. 아래 중 어디에 해당하는지 먼저 분류하면 시간을 크게 줄일 수 있습니다.

A. 아예 Metrics API가 없다고 나옴

  • error: Metrics API not available
  • the server could not find the requested resource (get pods.metrics.k8s.io)

metrics-server 미설치/비정상 또는 APIService 등록 실패 가능성이 큽니다.

B. API는 응답하지만 값이 0 또는 비정상적으로 작음

  • 노드/파드 CPU, 메모리가 모두 0에 가깝게 표시
  • 특정 노드/특정 네임스페이스만 0

metrics-server가 kubelet에서 metrics를 못 긁어오거나, 일부 노드만 접근 불가일 가능성이 큽니다.

C. 간헐적으로만 0 또는 timeout

  • 피크 타임에만 kubectl top이 느리거나 실패

→ metrics-server 리소스 부족, 네트워크/보안그룹, kubelet 응답 지연 등 성능/연결성 이슈를 의심합니다.

1단계: Metrics API 리소스가 존재하는지 확인

먼저 Kubernetes에 metrics API가 등록되어 있는지 확인합니다.

kubectl get apiservice | grep metrics
# 기대: v1beta1.metrics.k8s.io  True

kubectl get --raw "/apis/metrics.k8s.io/v1beta1" | head
# 기대: APIGroup 디스커버리 JSON
  • apiservice가 없거나 False라면 metrics-server 쪽부터 봐야 합니다.

APIService가 False일 때 바로 보는 곳

kubectl describe apiservice v1beta1.metrics.k8s.io

여기서 자주 보이는 문제:

  • FailedDiscoveryCheck (서비스 엔드포인트/인증 문제)
  • x509 인증서 관련 오류
  • no endpoints available for service "metrics-server"

2단계: metrics-server 설치/상태 점검

EKS에서 metrics-server는 보통 kube-system에 설치합니다.

kubectl -n kube-system get deploy,svc,pods | grep -E "metrics-server|NAME"
kubectl -n kube-system logs deploy/metrics-server --tail=200

로그에서 자주 나오는 핵심 에러와 의미

(1) kubelet 연결 실패 (timeout/connection refused)

  • Get "https://<node-ip>:10250/...": dial tcp ... i/o timeout

의미:

  • metrics-server → 노드 kubelet(10250)로 접근이 막혔거나 라우팅이 안 됨

(2) 인증/인가 실패 (401/403)

  • Unauthorized / Forbidden

의미:

(3) 인증서 검증 실패 (x509)

  • x509: cannot validate certificate for <ip> because it doesn't contain any IP SANs

의미:

  • kubelet 인증서 SAN 문제로, metrics-server가 strict TLS 검증을 통과 못함

EKS/Managed Node Group 환경에서는 보통 metrics-server에 다음 옵션을 넣어 우회하는 경우가 많습니다(보안 트레이드오프 존재).

# (예시) metrics-server Deployment args
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP

> --kubelet-insecure-tls는 “문제를 해결하기 위한 빠른 우회”로는 유효하지만, 가능하면 kubelet 인증서/구성을 정상화하는 방향을 우선 고려하세요.

3단계: kubelet(10250) 접근성 — 네트워크/보안그룹/네트워크폴리시

metrics-server는 각 노드의 kubelet 엔드포인트(기본 10250)로 접근해야 합니다. EKS에서 흔한 함정은 노드 보안그룹(Security Group) 혹은 CNI/네트워크 경로 때문에 kubelet 접근이 막히는 경우입니다.

metrics-server Pod가 어느 노드에서 뜨는지 확인

kubectl -n kube-system get pod -l k8s-app=metrics-server -o wide

metrics-server Pod에서 kubelet 포트 테스트

클러스터에 디버깅용 Pod(예: curl) 하나를 띄우고 노드 IP로 10250 접근을 시도합니다.

# 디버그 파드 실행(권장: ephemeral, 삭제 전제)
kubectl run -it --rm netdebug \
  --image=curlimages/curl:8.5.0 \
  --restart=Never -- sh

# (파드 내부) 노드 InternalIP로 kubelet 포트 접근
curl -k https://<NODE_INTERNAL_IP>:10250/healthz
  • timeout이면 네트워크/SG/NACL/라우팅 문제 가능성이 큽니다.
  • 401이면 “연결은 되는데 인증이 안 됨”입니다(다음 단계로).

> EKS 네트워킹 이슈는 NodePort/보안그룹/CNI에서 같이 터지는 경우가 많습니다. 유사한 네트워크 점검 흐름은 EKS에서 NodePort만 안 열릴 때 CNI·SG 점검도 참고하세요.

보안그룹 체크 포인트(핵심)

  • 노드 SG 인바운드에 “노드 자신/클러스터에서 10250 허용”이 필요한 구조인지 확인
  • metrics-server가 노드로 나가는 아웃바운드가 막혀 있지 않은지 확인
  • 노드가 여러 SG를 물고 있을 때 “의도치 않은 제한 SG”가 붙어 있지 않은지 확인

EKS는 구성에 따라 control-plane ↔ node 통신 규칙이 자동 구성되지만, 커스텀 SG/추가 SG/사내 보안 정책이 끼면 10250이 막히는 일이 흔합니다.

4단계: RBAC 및 Aggregated API 인증 체인 확인

metrics-server는 aggregated API 형태로 동작합니다.

metrics-server Service/Endpoints 확인

kubectl -n kube-system get svc metrics-server
kubectl -n kube-system get endpoints metrics-server
  • endpoints가 비어 있으면 selector/labels 불일치 또는 readiness 문제입니다.

APIService가 metrics-server를 제대로 바라보는지 확인

kubectl get apiservice v1beta1.metrics.k8s.io -o yaml | sed -n '1,120p'

확인할 필드:

  • spec.service.name: metrics-server
  • spec.service.namespace: kube-system
  • status.conditions의 메시지

최소 RBAC 점검

설치 매니페스트에 따라 다르지만, 일반적으로 아래가 있어야 합니다.

  • ClusterRole / ClusterRoleBinding (metrics-server)
  • RoleBinding (kube-system)

빠르게 존재 여부만 확인:

kubectl get clusterrole | grep metrics-server
kubectl get clusterrolebinding | grep metrics-server

RBAC이 깨졌다면 재설치(Helm/manifest 적용)가 가장 빠릅니다.

5단계: 값이 0으로만 보일 때 — “수집은 되는데 표시가 0” 케이스

kubectl top이 0으로만 보이면 다음 가능성이 큽니다.

(1) metrics-server가 일부 노드만 수집 실패

노드별로 수집 상태가 갈리면 kubectl top nodes에서 특정 노드만 0 또는 누락될 수 있습니다.

kubectl top nodes
kubectl describe node <NODE_NAME> | sed -n '1,120p'

이때 metrics-server 로그에 “특정 노드 IP로 timeout”이 찍히는지 확인하세요.

(2) 스케일/부하로 인해 metrics-server가 지연

클러스터가 커지거나(노드/파드 수 증가), metrics-server 리소스가 작으면 간헐적으로 0/timeout이 발생할 수 있습니다.

kubectl -n kube-system top pod -l k8s-app=metrics-server
kubectl -n kube-system describe deploy metrics-server | sed -n '1,200p'

대응:

  • metrics-server replica 증가
  • CPU/메모리 requests/limits 상향
  • (가능하다면) 노드 수/파드 수 대비 적정 sizing 적용

(3) 짧은 실행 파드(Job/CronJob) 특성

아주 짧게 실행되고 끝나는 파드는 metrics가 수집되기 전에 종료되어 top에서 0 또는 미표시처럼 보일 수 있습니다. 이건 장애가 아니라 관측 윈도우의 한계입니다.

6단계: EKS/Bottlerocket 등 노드 접근이 어려울 때의 대안

노드 레벨에서 kubelet 상태/로그를 보고 싶지만, EKS에서 Bottlerocket을 쓰거나 SSH가 막혀 있으면 접근이 난감합니다. 이때는 SSM 기반 접근이 유용합니다.

노드에서 확인하고 싶은 것(가능할 때):

  • kubelet 프로세스 상태
  • kubelet 인증서/인증 설정
  • 노드의 방화벽/iptables 정책(배포 방식에 따라)

7단계: 재설치/정상화 가이드(Helm 예시)

이미 설치돼 있는데 꼬였거나, 옵션 조정이 필요하면 Helm 재적용이 실전에서 가장 안전합니다(버전/옵션 관리가 쉬움).

> 아래는 예시입니다. 실제로는 클러스터 버전과 조직 보안 정책에 맞춰 values를 조정하세요.

helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm repo update

helm upgrade --install metrics-server metrics-server/metrics-server \
  -n kube-system \
  --set args={--kubelet-preferred-address-types=InternalIP\,Hostname\,InternalDNS\,ExternalDNS\,ExternalIP,--kubelet-insecure-tls}

kubectl -n kube-system rollout status deploy/metrics-server
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl top nodes
  • --kubelet-preferred-address-types는 노드 주소 선택 이슈(예: ExternalIP로 잡혀서 접근 불가)를 줄이는 데 효과가 큽니다.
  • --kubelet-insecure-tls는 x509 SAN 이슈에서 빠른 해결책이지만, 장기적으로는 TLS 체인을 정상화하는 것이 바람직합니다.

8단계: 최종 체크리스트(원인 → 확인 명령 → 조치)

Metrics API 자체가 없음

  • 확인: kubectl get apiservice | grep metrics, kubectl get --raw "/apis/metrics.k8s.io/v1beta1"
  • 조치: metrics-server 설치/재설치

APIService가 False

  • 확인: kubectl describe apiservice v1beta1.metrics.k8s.io
  • 조치: metrics-server Service/Endpoints/인증서 문제 해결

kubelet 접근 불가(10250 timeout)

  • 확인: 디버그 파드에서 curl -k https://<node-ip>:10250/healthz
  • 조치: 노드 SG/NACL/라우팅/CNI 점검

kubelet 인증서(x509) 문제

  • 확인: metrics-server 로그의 x509 메시지
  • 조치: (단기) --kubelet-insecure-tls, (장기) kubelet 인증서 SAN 정상화

인증/인가(401/403)

  • 확인: metrics-server 로그, RBAC 리소스 존재 여부
  • 조치: RBAC/설치 매니페스트 재적용, 인증 체인 점검

수집 지연/리소스 부족

  • 확인: kubectl -n kube-system top pod -l k8s-app=metrics-server
  • 조치: replica/requests/limits 상향

마무리

EKS에서 kubectl top이 0으로만 보일 때는 “kubectl이 고장”이 아니라, 대부분 metrics-server → kubelet(10250) 수집 경로가 끊겼거나(네트워크/SG), 인증서/인증 체인이 맞지 않거나, metrics-server 자체가 불안정한 경우입니다.

가장 빠른 진단 루트는 아래 3줄입니다.

kubectl get apiservice | grep metrics
kubectl -n kube-system logs deploy/metrics-server --tail=200
kubectl run -it --rm netdebug --image=curlimages/curl:8.5.0 --restart=Never -- sh

여기서 나온 에러 메시지(Timeout vs 401 vs x509)에 따라 네트워크/인증/설치 상태로 가지를 치면, “0%의 원인”을 짧은 시간 안에 확정할 수 있습니다.