- Published on
K8s NodeNotReady - CNI 플러그인 장애 복구 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
운영 중인 Kubernetes 클러스터에서 노드가 갑자기 NotReady로 떨어지면, 대부분은 kubelet/컨테이너 런타임/네트워크(CNI) 중 하나가 무너진 상황입니다. 특히 CNI 플러그인이 깨지면 새 Pod가 IP를 못 받고, kubelet이 네트워크 초기화를 못 끝내며, 결과적으로 노드가 Ready로 복귀하지 못하는 경우가 많습니다.
이 글은 “NodeNotReady + CNI 플러그인 장애”를 전제로, 증상 확인 → 원인 분기 → 복구 시나리오(클러스터 단위/노드 단위) → 재발 방지 체크리스트 순서로 정리합니다. EKS 예시가 포함되지만, Calico/Flannel/Cilium 등에서도 동일한 디버깅 패턴이 적용됩니다.
관련해서 kubelet 로그에 CNI plugin not initialized가 찍히는 케이스는 아래 글도 함께 참고하면 좋습니다.
1) 먼저 “정말 CNI 문제인지” 3분 안에 판별
1-1. 노드 상태/이벤트에서 힌트 뽑기
kubectl get nodes -o wide
kubectl describe node <NODE_NAME> | sed -n '/Conditions:/,/Addresses:/p'
kubectl describe node <NODE_NAME> | sed -n '/Events:/,$p'
다음 신호가 보이면 CNI 가능성이 큽니다.
Ready가False로 바뀌며NetworkUnavailable=True- 이벤트에
Container runtime network not ready또는CNI plugin not initialized - 해당 노드에 스케줄링된 Pod들이
ContainerCreating에서 오래 멈춤
1-2. 시스템 DaemonSet이 같이 죽었는지 확인
CNI는 보통 DaemonSet으로 깔립니다(예: aws-node, calico-node, cilium).
kubectl -n kube-system get ds
kubectl -n kube-system get pods -o wide | egrep 'aws-node|calico|cilium|flannel'
- 특정 노드에서만 CNI Pod가
CrashLoopBackOff/Error면 노드 로컬 문제일 확률이 큽니다. - 모든 노드에서 동시에 흔들리면 CNI 배포/설정/컨트롤플레인 연계 문제(예: RBAC, CRD, IPAM, ENI 제한) 가능성이 큽니다.
1-3. (중요) “Pod는 Running인데 트래픽이 0”도 CNI 장애의 변형
노드가 Ready로 보이더라도 CNI/정책/보안그룹/라우팅이 깨지면 “떠 있는데 안 통하는” 상태가 나옵니다. 아래 글의 10분 진단 루틴이 CNI 장애 분기에도 그대로 도움이 됩니다.
2) 장애 원인 분기: CNI가 왜 깨졌나
CNI 장애는 크게 4가지 축으로 나뉩니다.
2-1. CNI 바이너리/설정 파일이 노드에서 사라짐 또는 손상
kubelet은 보통 다음 경로를 봅니다.
- CNI config:
/etc/cni/net.d/ - CNI binaries:
/opt/cni/bin/
노드에 SSH/SSM 접근이 가능하다면 아래를 확인합니다.
sudo ls -al /etc/cni/net.d/
sudo ls -al /opt/cni/bin/ | head
- 디렉터리가 비어 있거나,
.conf/.conflist가 없으면 kubelet이 네트워크 초기화를 못합니다. - AMI 업데이트/보안 에이전트/하드닝 스크립트가 해당 경로를 건드리는 경우가 있습니다.
2-2. CNI DaemonSet 자체가 크래시(권한/RBAC/CRD/커널 모듈)
CNI Pod 로그를 우선 확인합니다.
kubectl -n kube-system logs ds/aws-node --tail=200
# 또는
kubectl -n kube-system logs -l k8s-app=calico-node --tail=200
자주 보이는 패턴:
Unauthorized/AccessDenied: RBAC 또는 클라우드 IAM(특히 EKS) 문제failed to setup iptables/modprobe: 커널/iptables/nftables 호환 문제CrashLoopBackOff와 함께OOMKilled: 리소스 부족(노드 메모리 압박)
2-3. IP 고갈(IPAM)로 인해 Pod 네트워크 할당 실패
EKS의 경우 특히 ENI/IP 할당 한계 또는 Prefix Delegation 설정 불일치로 IP가 고갈되면, CNI는 살아 있어도 Pod가 IP를 못 받아 “네트워크 초기화 실패”처럼 보일 수 있습니다.
kubectl -n kube-system logs ds/aws-node --tail=200 | egrep -i 'ip|eni|prefix|exhaust|assign'
이 케이스는 아래 글이 원인/해결을 더 깊게 다룹니다.
2-4. 노드 로컬 네트워크 스택/iptables 꼬임
노드 단위로만 NotReady가 반복되면, 다음이 흔합니다.
- iptables 규칙이 비정상적으로 누적/충돌
- CNI가 생성한 veth/bridge가 꼬임
- 컨테이너 런타임이 네트워크 namespace 정리를 못함
이 경우 “노드 재부팅”이 가장 빠른 해결책이기도 하지만, 운영 환경에서는 순서가 중요합니다(Drain → 복구 → Uncordon).
3) 복구 전략: 안전한 순서(클러스터 영향 최소화)
복구는 노드 단위 응급처치와 CNI 재배포/롤링 업데이트 두 갈래로 나뉩니다.
3-1. 0단계: 해당 노드 격리(서비스 보호)
노드가 불안정하면 우선 스케줄링을 막고 워크로드를 다른 노드로 이동시킵니다.
kubectl cordon <NODE_NAME>
# 안전하게 비우기(데몬셋 제외)
kubectl drain <NODE_NAME> --ignore-daemonsets --delete-emptydir-data --grace-period=60 --timeout=10m
--delete-emptydir-data는 워크로드 특성에 따라 위험할 수 있으니(캐시/임시 데이터) 영향도를 확인하세요.
3-2. 1단계: CNI DaemonSet 롤링 재시작(가장 흔한 1차 처방)
CNI Pod가 일시적으로 꼬였거나, 노드의 CNI 구성 재동기화가 필요한 경우 DaemonSet 재시작만으로 복구되는 경우가 많습니다.
# 예: AWS VPC CNI
kubectl -n kube-system rollout restart ds/aws-node
kubectl -n kube-system rollout status ds/aws-node --timeout=5m
# 예: Calico
kubectl -n kube-system rollout restart ds/calico-node
kubectl -n kube-system rollout status ds/calico-node --timeout=5m
재시작 후에도 특정 노드에서만 CNI Pod가 계속 죽으면, 그 노드는 로컬 파일/커널/디스크 문제로 좁혀집니다.
3-3. 2단계: 노드에서 CNI 파일 존재/권한 복구(노드 로컬)
CNI config가 비어 있는 경우, 원인은 대개 “DaemonSet이 파일을 못 씀”입니다(디스크 read-only, 권한, 보안 에이전트).
노드에서 다음을 확인합니다.
# 디스크/파일시스템 문제
sudo dmesg | tail -n 50
sudo mount | egrep ' / |/etc|/opt'
# CNI 경로 권한
sudo stat /etc/cni /etc/cni/net.d /opt/cni /opt/cni/bin
- 파일시스템이 read-only로 올라가 있거나 디스크 오류가 있으면, CNI만 고쳐서는 해결이 안 됩니다. 이 경우 노드 교체가 정답입니다.
3-4. 3단계: kubelet/컨테이너 런타임 재시작(노드 로컬)
CNI가 정상인데 kubelet이 “network not ready” 상태를 계속 들고 있는 경우, kubelet이 이전 상태를 못 빠져나오는 케이스가 있습니다.
# systemd 기반
sudo systemctl restart containerd || true
sudo systemctl restart kubelet
# 상태 확인
sudo systemctl --no-pager --full status kubelet | head -n 50
sudo journalctl -u kubelet -n 200 --no-pager
- 재시작 후에도
/etc/cni/net.d가 비어 있으면 kubelet은 계속 NotReady일 수밖에 없습니다.
3-5. 4단계: 최후의 수단 — 노드 재부팅 또는 노드 교체
운영에서 가장 “확실한” 복구는 노드 교체입니다.
- Managed Node Group(ASG)라면 **문제 노드를 종료(terminate)**하고 새 노드로 대체
- Karpenter라면 해당 노드를 삭제하고 재프로비저닝
노드가 간헐적으로만 깨진다면, 재부팅으로 잠깐 살아나도 재발할 수 있습니다. 반복된다면 AMI/커널/보안 에이전트/iptables 호환성을 근본 원인으로 보고 교체/업데이트 전략을 세우는 편이 낫습니다.
4) 복구 후 검증: “Ready”만 보면 안 되는 이유
노드가 Ready로 돌아와도 실제 데이터 플레인이 정상인지 확인해야 합니다.
4-1. CNI Pod/노드 상태
kubectl get nodes
kubectl -n kube-system get pods -o wide | egrep 'aws-node|calico|cilium|flannel'
4-2. DNS/서비스 라우팅/Pod 간 통신 스모크 테스트
테스트 Pod를 하나 띄워서 DNS/ClusterIP/외부 egress를 확인합니다.
kubectl run netcheck --image=ghcr.io/nicolaka/netshoot:latest -it --rm --restart=Never -- bash
# (Pod 내부)
nslookup kubernetes.default.svc.cluster.local
curl -sS http://kubernetes.default.svc
ip route
- DNS가 흔들리면 CoreDNS/노드 로컬 DNS 캐시/네트워크 정책 문제일 수 있습니다. 간헐적 NXDOMAIN/SERVFAIL은 다음 글의 체크리스트가 유용합니다.
5) 재발 방지 체크리스트(운영 관점)
5-1. CNI/노드 지표 알람
다음 조건은 조기 탐지에 효과적입니다.
NodeReady가 1~2분 이상Falsekube-system네임스페이스에서 CNI DaemonSet의Unavailable증가- Pod 생성 실패 이벤트 급증:
FailedCreatePodSandBox
Prometheus를 쓴다면(예시):
# NotReady 노드 수
count(kube_node_status_condition{condition="Ready",status="true"} == 0)
5-2. 변경관리: CNI 업그레이드/노드 AMI 롤링을 분리
- 노드 AMI 업데이트와 CNI 버전 업데이트를 같은 배포 윈도우에 묶으면, 원인 추적이 어려워집니다.
- 특히 iptables/nftables 전환, 커널 버전 변경은 CNI와 궁합 문제가 날 수 있어 점진 롤링 + 카나리 노드가 안전합니다.
5-3. IP 용량 계획(특히 EKS)
- 서브넷 여유 IP, 인스턴스 타입당 ENI/IP 한계, Prefix Delegation 적용 여부를 정기 점검
- “노드는 Ready인데 새 Pod만 계속 실패”는 IPAM 고갈의 전형적인 신호입니다.
결론
NodeNotReady는 증상이고, CNI 장애는 그중에서도 가장 빠르게 확산되는 원인입니다. 복구의 핵심은 다음 순서를 지키는 것입니다.
- 노드 격리(cordon/drain)로 서비스 보호
- CNI DaemonSet 롤링 재시작으로 빠른 정상화 시도
- 노드 로컬의
/etc/cni/net.d,/opt/cni/bin, kubelet/containerd 상태 확인 - 반복 재발 시 노드 교체 및 AMI/커널/보안 에이전트/iptables 호환성 점검
위 절차대로 하면 “일단 살리는 복구”와 “재발을 줄이는 개선”을 함께 가져갈 수 있습니다.