/인프라/kubectl get nodes NotReady 원인 6가지 진단·복구 5분 가이드
인프라kuberneteskubelet NotReady

kubectl get nodes NotReady 원인 6가지 진단·복구 5분 가이드

워커 노드가 NotReady로 빠지고 Pod가 전부 죽었다면 kubelet 다운·CNI plugin not initialized·DiskPressure 등 6원인을 describe node Conditions로 분기하고, drain 대피부터 노드 복구까지 복붙 명령으로 5분 안에 해결하세요.

kubectl get nodes NotReady 원인 6가지 진단·복구 5분 가이드

kubectl get nodes NotReady? kubelet·CNI·디스크 6원인 5분 복구

K8s_Troubleshooting_Guide 9편

새벽에 알람이 울리고 kubectl get nodes를 쳤더니 워커 한 대가 NotReady. 그 위에 떠 있던 Pod들이 한꺼번에 Terminating을 거쳐 사라지고, 서비스 응답이 흔들리기 시작합니다. 이 글의 목표는 단 하나입니다. 이 화면을 보고 5분 안에 6가지 원인 중 하나로 분기하고, Pod를 안전하게 대피시킨 뒤 노드를 살려내는 것. 개념 설명은 최소화하고, "이 메시지가 보이면 이 명령" 식으로 바로 갑니다.

1. 패닉 멈추고 봐야 할 첫 화면

먼저 노드가 정말 NotReady인지, 언제부터인지 확인합니다.

Bash
kubectl get nodes -o wide
kubectl get node <node> -o jsonpath='{.status.conditions}' | jq

NotReady가 떴다고 해서 무조건 노드가 죽은 건 아닙니다. kubelet 하트비트가 잠깐 끊긴 일시적 상태일 수도 있고, 디스크가 꽉 차서 kubelet이 스스로 Ready를 False로 내린 것일 수도 있습니다. 그래서 가장 먼저 Conditions를 읽어 원인 후보를 좁히는 것이 핵심입니다.

2. Conditions 판독표 — 5초 만에 후보 좁히기

Bash
kubectl describe node <node> | grep -A8 Conditions

출력의 Reason/Message 원문을 아래 표와 대조하면 바로 분기됩니다.

Conditions / 메시지 원문가리키는 원인다음에 칠 명령
Ready=Unknown, NodeStatusUnknown, "Kubelet stopped posting node status"kubelet 다운 또는 노드↔API 단절systemctl status kubelet / curl -k https://<API>:6443/healthz
Ready=False, KubeletNotReady, "container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized"CNI 미초기화ls /etc/cni/net.d / journalctl -u kubelet | grep -i cni
DiskPressure=True, "kubelet has disk pressure"디스크 압박 (이미지/로그 폭증)df -h / df -i / crictl rmi --prune
MemoryPressure=True, "kubelet has insufficient memory available"메모리 압박free -m / kubectl top node
PIDPressure=True, "kubelet has insufficient PID available"프로세스/스레드 폭주ps -eLf | wc -l
Ready=False, "failed to get container runtime" / containerd 무응답컨테이너 런타임 다운systemctl status containerd / crictl info

참고로 kubelet은 기본적으로 DiskPressure를 imagefs/nodefs 가용량 15% 미만(evictionHard 기본값)에서, MemoryPressurememory.available<100Mi에서 True로 올립니다. 임계에 닿으면 노드가 NotReady로 전환되고 Pod 축출이 시작됩니다.

3. 원인 6분기 진단·복구

① kubelet 다운

Bash
systemctl status kubelet
journalctl -u kubelet -f

active (running)이 아니면 바로 재기동합니다.

Bash
systemctl restart kubelet && systemctl status kubelet

② CNI plugin not initialized

가장 흔한 함정입니다. CNI 설정/바이너리가 비어 있으면 위 메시지가 뜹니다.

Bash
journalctl -u kubelet | grep -i cni
ls /etc/cni/net.d    # 비어 있으면 CNI conf 미배포
ls /opt/cni/bin      # bridge, loopback 등 바이너리 존재 확인

설정이 비어 있다면 CNI DaemonSet을 재배포해 conf를 다시 깔아줍니다.

Bash
kubectl rollout restart ds <calico-node|cilium|aws-node> -n kube-system

③ 디스크 / 메모리 압박

Bash
df -h        # nodefs 사용률
df -i        # inode 고갈도 NotReady 유발
free -m

디스크가 원인이면 안 쓰는 이미지와 로그부터 비웁니다.

Bash
crictl rmi --prune
journalctl --vacuum-size=200M

확보 후 kubelet이 자동으로 DiskPressure를 해제하고 Ready로 돌아옵니다.

④ 컨테이너 런타임 다운 (containerd)

dockershim 제거 이후 표준은 containerd입니다.

Bash
systemctl status containerd
crictl ps
crictl info

무응답이면 재기동 → kubelet 순으로 복구합니다.

Bash
systemctl restart containerd && systemctl restart kubelet

⑤ 인증서 / 노드 등록 문제

Bash
journalctl -u kubelet | grep -i certificate
kubectl get csr

Pending CSR이 보이면 승인합니다.

Bash
kubectl certificate approve <csr-name>

⑥ 노드 ↔ API 네트워크 단절

노드에 SSH로 들어가 API 서버 도달성을 확인합니다.

Bash
curl -k https://<API_SERVER>:6443/healthz   # ok 안 나오면 네트워크/SG/방화벽
ss -tnp | grep 6443

보안그룹·라우팅·DNS를 점검하세요. 노드는 멀쩡한데 컨트롤플레인과 못 닿는 경우가 의외로 많습니다.

4. Pod 안전 대피와 노드 복귀

복구 작업 전에 새 Pod가 그 노드로 더 안 떨어지게 막고, 기존 Pod를 비웁니다.

Bash
kubectl cordon <node>
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
# 복구 작업 수행 후
kubectl uncordon <node>

drain이 PDB(PodDisruptionBudget)나 emptyDir 때문에 막히면, PDB의 minAvailable을 잠시 조정하거나 --disable-eviction(API 미지원 환경)으로 강제 삭제합니다. 단, 강제 삭제는 데이터 유실 위험이 있으니 stateful 워크로드에는 신중히 적용하세요.

실무 한마디

제 경험상 새벽 NotReady의 70%는 디스크 압박CNI 미초기화 둘로 수렴합니다. 그래서 알람이 오면 저는 describe Conditions를 본 뒤 묻지도 따지지도 않고 df -hls /etc/cni/net.d부터 칩니다. 이 두 줄로 대부분 분기가 끝나고, 나머지 시간은 drain하면서 천천히 봅니다. EKS·GKE 같은 관리형은 노드 auto-repair가 NotReady 노드를 알아서 교체해 주지만, 셀프매니지드 클러스터는 결국 사람이 위 6단계를 직접 돌려야 한다는 차이를 기억해 두세요.

결론: NotReady 5분 복구 체크리스트

  1. kubectl describe node Conditions로 원인 후보 좁히기
  2. kubelet / containerd 상태 확인 → 필요 시 restart
  3. CNI conf·bin 존재 확인 → DaemonSet rollout restart
  4. df -h·df -i·free -m로 압박 여부 확인 → 이미지/로그 정리
  5. CSR·API healthz 확인
  6. cordondrain → 복구 → uncordon

자주 묻는 질문 (FAQ)

Q. 잠깐 NotReady였다가 알아서 Ready로 돌아왔는데 왜 그런가요? A. kubelet의 하트비트(nodeStatusUpdateFrequency, 기본 10초)가 일시 지연되면 컨트롤러가 node-monitor-grace-period(기본 40초) 후 Ready=Unknown으로 표시합니다. GC, 일시적 부하, 네트워크 지터로 하트비트가 밀렸다 회복되면 자동으로 Ready로 복귀합니다. 반복된다면 노드 부하나 etcd/API 지연을 의심하세요.

Q. 노드가 NotReady 되자마자 Pod가 다 빠지는 걸 막을 수 있나요? A. 노드가 NotReady가 되면 node.kubernetes.io/not-ready(또는 unreachable) taint가 붙고, 기본 tolerationSeconds: 300 후 Pod가 축출됩니다. 일시 장애에 민감하지 않은 워크로드는 toleration의 tolerationSeconds를 늘려 즉시 축출을 늦추거나, 반대로 빠른 페일오버가 필요하면 값을 줄여 조정합니다.

Q. CNI plugin not initialized인데 DaemonSet은 Running입니다? A. Pod는 Running이어도 /etc/cni/net.d에 conf가 안 깔렸을 수 있습니다. ls /etc/cni/net.dls /opt/cni/bin을 직접 확인하고, 비어 있으면 rollout restart로 conf 재배포를 강제하세요.

다음 편(10편)에서는 CrashLoopBackOff와 OOMKilled — Pod가 무한 재시작할 때의 원인 추적을 다룹니다.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 13일

댓글

불러오는 중...