[K8s] Kubernetes Node가 NotReady 상태일 때: 실전 트러블슈팅 가이드
요약 (Abstract) 인프라 엔지니어의 새벽잠을 깨우는 주범, Kubernetes Node의 NotReady 상태. 17년 차 엔지니어의 시각으로 문제의 핵심 원인(Kubelet, CNI, Runtime)을 분석하고, kubectl을 활용한 진단부터 자동 복구 스크립트 구축까지, 실전에서 즉시 적용 가능한 해결책을 정리합니다.
1. 들어가며: 인프라 엔지니어의 숙명
레거시 환경에서 서버의 Health Check 실패가 단순히 Ping이 빠지는 문제였다면, 쿠버네티스(Kubernetes) 환경에서의 Node NotReady는 훨씬 복합적인 의미를 가집니다.
이는 단순히 서버가 죽은 것일 수도 있지만, Kubelet 프로세스의 오동작, 컨테이너 런타임(Docker/Containerd)의 행, 혹은 CNI(Network Interface) 설정 꼬임 등 다양한 원인이 얽혀 있습니다. 이번 포스트에서는 실제 운영 환경에서 겪었던 사례를 바탕으로, 당황하지 않고 문제를 해결하는 체계적인 트러블슈팅 프로세스를 공유합니다.
2. 🚨 문제 상황: "Node가 준비되지 않았습니다"
운영 중인 클러스터에서 갑자기 노드 하나가 NotReady 상태로 변하면, 스케줄러는 해당 노드에 새로운 파드(Pod)를 할당하지 않으며, 기존 파드들의 상태도 불안정해질 수 있습니다.
Bash
# kubectl get nodes 결과
NAME STATUS ROLES AGE VERSION
master-1 Ready master 30d v1.28.2
worker-1 Ready worker 30d v1.28.2
worker-2 NotReady worker 30d v1.28.2 <-- 문제 발생
worker-3 Ready worker 30d v1.28.2
가장 먼저 해야 할 일은 describe 명령어로 상세 원인을 파악하는 것입니다.
Bash
kubectl describe node worker-2
주요 에러 메시지 예시:
KubeletNotReady: Kubelet이 응답하지 않음SystemOOM: 시스템 메모리 부족NetworkPluginNotReady: CNI 플러그인 초기화 실패 (가장 흔함)
3. 🔍 핵심 원인 분석 (The Root Causes)
경험상 NotReady 상태의 원인은 크게 4가지 영역으로 좁혀집니다.
- Kubelet 서비스 문제: 노드의 에이전트인 Kubelet이 죽었거나 API 서버와 통신하지 못하는 경우.
- CNI (Container Network Interface) 문제: Flannel, Calico 등의 네트워크 플러그인 파드가 죽거나 설정이 꼬인 경우.
- 컨테이너 런타임 (Runtime) 문제: Docker 데몬이나 Containerd가 응답 불가(Hang) 상태인 경우.
- 시스템 리소스 고갈: Disk Pressure, Memory Pressure 등으로 인해 노드가 스스로를 보호하기 위해 셧다운된 경우.
4. 🛠️ 단계별 해결 솔루션
4.1. Kubelet 서비스 심폐소생술
가장 빈번한 원인입니다. Kubelet 서비스의 로그를 확인하고 재시작합니다.
Bash
# 1. Kubelet 상태 확인
sudo systemctl status kubelet
# 2. 로그 확인 (최근 10분)
sudo journalctl -u kubelet -f
# 3. 서비스 재시작
sudo systemctl restart kubelet
Tip: 만약 인증서(Certificate) 만료 문제라면 /var/lib/kubelet/pki/ 경로의 인증서를 확인하고 갱신해야 합니다.4.2. CNI 플러그인 점검
NetworkPluginNotReady 메시지가 보인다면 CNI 문제입니다.
Bash
# 1. kube-system 네임스페이스의 CNI 파드 확인
kubectl get pods -n kube-system -o wide | grep -E 'flannel|calico'
# 2. 문제가 있는 파드 로그 확인
kubectl logs -n kube-system <cni-pod-name>
# 3. (필요시) CNI 데몬셋 재시작
kubectl rollout restart daemonset/kube-flannel-ds -n kube-system
4.3. 컨테이너 런타임 (Docker/Containerd) 점검
런타임이 멈추면 Kubelet도 동작하지 않습니다.
Bash
# Docker를 사용하는 경우
sudo systemctl status docker
sudo systemctl restart docker
# Containerd를 사용하는 경우 (최신 K8s)
sudo systemctl status containerd
sudo systemctl restart containerd
5. 🚀 고급: 자동 복구 및 모니터링 (Auto-Healing)
매번 사람이 붙어서 해결할 수는 없습니다. DevOps 엔지니어라면 자동화를 고민해야 합니다.
5.1. Prometheus & Grafana 알림 설정
Prometheus 룰을 설정하여 노드 장애 발생 시 즉시 알림을 받습니다.
YAML
groups:
- name: kubernetes-nodes
rules:
- alert: NodeNotReady
expr: kube_node_status_condition{condition="Ready",status="true"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.node }} is Down!"
5.2. 자동 복구 스크립트 (Self-Healing Script)
Cronjob이나 모니터링 시스템의 Webhook과 연동하여 사용할 수 있는 간단한 복구 스크립트 예제입니다.
Bash
#!/bin/bash
NODE_NAME=$1
echo "🔍 Diagnosing Node: $NODE_NAME"
# Kubelet 재시작 시도
ssh $NODE_NAME "sudo systemctl restart kubelet"
sleep 10
# 상태 재확인 후 실패 시 런타임 재시작
if [ $(kubectl get node $NODE_NAME -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}') != "True" ]; then
echo "⚠️ Kubelet restart failed. Restarting Runtime..."
ssh $NODE_NAME "sudo systemctl restart containerd"
fi
6. 마치며: 장애는 막을 수 없지만, 대응은 줄일 수 있다
17년 동안 인프라를 만지며 느낀 점은 **"장애가 없는 시스템은 없다"**는 것입니다. 하지만 **"장애 복구 시간을 단축시키는 시스템"**은 만들 수 있습니다.
단순히 systemctl restart를 반복하는 것을 넘어, 위에서 소개한 모니터링과 자동화 스크립트를 통해 여러분의 클러스터가 스스로 치유되는(Self-Healing) 환경을 구축해 보시기 바랍니다.
다음 포스트에서는 이 노드들을 관리하는 **"K8s 클러스터 업그레이드 전략(Zero Downtime)"**에 대해 다뤄보겠습니다.