- Published on
EKS 노드 NotReady? CNI·IP 고갈 10분 진단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
EKS 운영 중 가장 당황스러운 알람 중 하나가 NodeNotReady입니다. 특히 워커 노드가 갑자기 NotReady로 바뀌면 신규 파드 스케줄링이 막히고, 기존 파드도 네트워크가 불안정해지면서 장애가 연쇄적으로 커집니다.
이 글은 “노드가 NotReady가 됐다”는 증상만 보고도 10분 안에 CNI 문제인지, IP 고갈인지를 가르는 실전 진단 루틴을 제공합니다. 결론부터 말하면, EKS에서 NotReady는 실제로는 kubelet이 컨트롤 플레인과 정상적으로 통신하지 못하거나, CNI가 파드 네트워킹을 구성하지 못해 노드 상태 점검이 실패하는 케이스가 많습니다.
운영 관점에서 비슷한 “10분 진단” 플레이북이 필요하다면, 네트워크 계층 장애를 다룬 AWS ALB 502/504 10분 타임아웃 진단 가이드도 함께 참고하면 원인 분리 사고방식에 도움이 됩니다.
0. 10분 진단 로드맵
아래 순서대로 진행하면 됩니다.
kubectl get nodes로 영향 범위 확인 (전체 vs 특정 노드)kubectl describe node로NotReady사유/이벤트 확인- 노드에서
kubelet상태와 로그 확인 aws-node(Amazon VPC CNI) 상태/로그 확인- IP 고갈 여부를
ipamd로그와 ENI/IP 할당 현황으로 판별 - 즉시 완화(노드 격리, 파드 재스케줄, CNI 재기동) 후 재발 방지 설정 적용
1. 1분: 영향 범위와 패턴 확인
먼저 영향 범위를 봅니다.
kubectl get nodes -o wide
kubectl get pods -A -o wide | head -n 50
패턴 A: 특정 노드만 NotReady
- 해당 노드의 kubelet/CNI/IP 문제가 유력
- 노드 자체(디스크, CPU, 커널, 컨테이너 런타임) 문제일 수도 있음
패턴 B: 여러 노드가 동시에 NotReady
- VPC 라우팅/보안그룹/NACL 변경, DNS 장애, 컨트롤 플레인 접근 문제
- CNI DaemonSet 롤아웃 실패(잘못된 설정 배포)도 흔함
2. 2분: describe node로 바로 단서 찾기
NODE=ip-10-0-12-34.ap-northeast-2.compute.internal
kubectl describe node "$NODE"
여기서 집중해서 볼 것:
Conditions섹션의Ready가False로 바뀐 시각과 이유Events섹션의 반복 메시지
자주 보이는 단서 예시:
NetworkPluginNotReady또는CNI not initializedFailed to create pod sandboxNodeStatusUnknown(노드가 API 서버와 통신 불가)
NetworkPluginNotReady가 보이면 거의 바로 CNI 축으로 들어가면 됩니다.
3. 3분: 노드에서 kubelet이 죽었는지부터 확인
노드에 접속(SSM 또는 SSH)해서 kubelet과 런타임을 확인합니다.
sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -n 200 --no-pager
다음 키워드가 보이면 CNI 또는 IP 고갈 가능성이 큽니다.
Container runtime network not readycni config uninitializedfailed to setup network for sandbox
kubelet이 재시작을 반복한다면, 증상 자체는 다르지만 접근법은 유사합니다. 재시작 루프를 체계적으로 파고드는 방법은 systemd 서비스가 자꾸 재시작될 때 7단계 진단 흐름을 그대로 적용할 수 있습니다.
컨테이너 런타임도 같이 봅니다.
sudo systemctl status containerd --no-pager
sudo journalctl -u containerd -n 200 --no-pager
4. 5분: CNI(aws-node) 상태 확인
EKS 기본 CNI는 Amazon VPC CNI(aws-node) DaemonSet입니다.
kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
여기서 체크 포인트:
- 문제 노드에
aws-node파드가 떠 있는가 CrashLoopBackOff또는Init에서 멈춰 있는가READY가1/1인지
문제 노드의 aws-node 로그를 봅니다.
POD=$(kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | awk 'NR==2{print $1}')
kubectl -n kube-system logs "$POD" -c aws-node --tail=200
CNI 문제의 전형적인 로그 힌트
ipamd관련 에러 (IP 할당 실패)failed to assign an IP address to containerdatastore업데이트 실패throttling또는 AWS API 호출 실패(권한/STS/네트워크)
참고로, 워커 노드 IAM 또는 STS 관련 이슈가 섞이면 CNI가 AWS API를 못 때리는 상황이 생길 수 있습니다. 파드에서 STS 오류를 겪는 케이스지만 원인 분해에 도움이 되는 글로 EKS Pod에서 STS 403 SignatureDoesNotMatch 해결도 같이 보면 좋습니다.
5. 7분: “IP 고갈”인지 빠르게 판별하는 법
EKS에서 IP 고갈은 대개 다음 중 하나로 나타납니다.
- 노드는 살아있는데 새 파드가 계속
Pending - 파드 생성 시
FailedCreatePodSandBox가 늘어남 aws-node로그에 IP 할당 실패가 반복
5.1 ipamd 로그에서 즉시 확인
aws-node 컨테이너 로그에 ipamd 메시지가 같이 찍히는 경우가 많습니다. 아래 같은 문구가 반복되면 IP 고갈 가능성이 큽니다.
InsufficientCidrBlocksno available IP addressesfailed to allocate a private IP address
더 확실히 보려면 문제 노드의 aws-node 파드를 특정해서 봐야 합니다.
# 문제 노드에 뜬 aws-node 파드 찾기
kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | grep "${NODE}" || true
# 위에서 찾은 파드명으로 로그 확인
kubectl -n kube-system logs <aws-node-pod-name> -c aws-node --tail=300
주의: 위 명령에서 <aws-node-pod-name>처럼 부등호가 들어간 표기는 MDX에서 빌드 에러를 유발할 수 있어, 실제 문서/런북에는 반드시 인라인 코드로 쓰거나 변수로 치환해 두는 습관이 안전합니다.
5.2 ENI/IP 할당이 “설계상” 부족한지 계산
VPC CNI는 노드의 ENI에 붙은 secondary IP를 파드에 할당합니다. 즉, 인스턴스 타입별 ENI 수/각 ENI당 IP 수에 따라 노드당 파드 상한이 사실상 결정됩니다.
바로 확인할 것:
- 해당 노드 인스턴스 타입의 ENI/IP 한도
- 현재 노드의 파드 수가 그 상한에 근접했는지
쿠버네티스 측에서 현재 파드 밀도를 보려면:
kubectl get pods -A -o wide --field-selector spec.nodeName="$NODE" | wc -l
AWS 측에서 ENI와 IP 사용량을 보려면(운영 환경에서는 AWS CLI 권한 필요):
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \
--query 'Reservations[0].Instances[0].NetworkInterfaces[*].{ENI:NetworkInterfaceId,IPs:PrivateIpAddresses[*].PrivateIpAddress}' \
--output json
여기서 IP가 꽉 차 있고, aws-node 로그가 IP 할당 실패를 말해주면 “IP 고갈”로 결론 내릴 수 있습니다.
6. 10분 내 즉시 완화(서비스 살리기)
원인을 확정하기 전에라도, 장애 확산을 막는 조치가 필요합니다.
6.1 문제 노드 격리(cordon) 후 드레인
kubectl cordon "$NODE"
# DaemonSet 파드는 남기고, emptyDir 데이터는 상황에 따라
kubectl drain "$NODE" --ignore-daemonsets --delete-emptydir-data --grace-period=60
NotReady노드에 트래픽/파드가 더 붙지 않게 막습니다.- 드레인이 안 되면(노드 통신 불가) 오토스케일링 그룹에서 인스턴스 교체가 더 빠를 때도 많습니다.
6.2 aws-node 재기동(신중하게)
CNI가 꼬였거나 데몬이 비정상 상태라면 aws-node를 재시작하는 것만으로 회복되는 경우가 있습니다.
kubectl -n kube-system rollout restart ds/aws-node
kubectl -n kube-system rollout status ds/aws-node --timeout=120s
주의: 클러스터 전체에 영향을 줄 수 있으니, 장애 범위가 작을 때는 특정 노드의 aws-node 파드만 삭제해서 재생성되게 하는 방식이 더 안전할 수 있습니다.
kubectl -n kube-system delete pod <aws-node-pod-name>
7. 재발 방지: CNI·IP 고갈을 “구조적으로” 줄이는 설정
여기부터는 장애가 진정된 뒤 적용할 설계/설정입니다.
7.1 서브넷 여유 IP부터 점검(가장 흔한 근본 원인)
서브넷의 사용 가능 IP가 부족하면 노드가 늘어도 파드 IP를 못 만듭니다.
- 서브넷 CIDR이 너무 작지 않은지
- 워크로드가 몰리는 AZ의 서브넷만 유독 작은지
- 노드 그룹이 특정 서브넷에만 붙도록 제한되어 있지 않은지
운영 팁:
- 노드 그룹을 여러 서브넷/AZ로 분산
- 확장 가능한 CIDR 설계(초기에 작게 잡으면 나중에 비용이 큼)
7.2 VPC CNI Prefix Delegation 사용 고려
IP 고갈이 잦고 파드 밀도가 높은 클러스터라면 Prefix Delegation이 효과적일 수 있습니다(지원 여부는 환경/버전에 따라 다름).
핵심은 “개별 IP” 대신 프리픽스를 위임받아 IP 할당 효율을 높이는 것입니다.
적용은 보통 aws-node의 환경변수로 제어합니다(예: ENABLE_PREFIX_DELEGATION). 실제 적용 가능 여부는 사용 중인 EKS 버전과 CNI 버전, 인스턴스 타입 제약을 함께 확인해야 합니다.
7.3 파드 밀도 제어: 인스턴스 타입과 maxPods 현실화
“노드에 파드가 너무 많이 붙어서 IP가 모자람”은 설계 문제인 경우가 많습니다.
- 인스턴스 타입을 ENI/IP 여유가 큰 것으로 변경
- 노드 그룹을 더 쪼개서 워크로드 분산
- 필요 시
maxPods를 보수적으로 설정해 과밀도를 사전에 차단
7.4 관측(Observability): 알람을 “노드 NotReady 이전”으로 당기기
NotReady는 이미 늦은 알람일 때가 많습니다. 다음을 미리 모니터링하면 선제 대응이 가능합니다.
- 서브넷
AvailableIpAddressCount(CloudWatch) - 노드별 파드 수(노드 과밀)
aws-node에러 로그 카운트- CNI 관련 이벤트 증가(
FailedCreatePodSandBox)
8. 체크리스트 요약(현장용)
kubectl describe node에서NetworkPluginNotReady또는CNI not initialized가 보이면 CNI 우선aws-node가 문제 노드에서 죽어 있거나 로그에 IP 할당 실패가 반복되면 IP 고갈 의심- 서브넷 여유 IP, 인스턴스 ENI/IP 한도, 노드 파드 밀도를 함께 봐야 “진짜 원인”이 드러남
- 즉시 완화는
cordon/drain로 확산 차단 후, 필요 시aws-node재기동 및 노드 교체
원하면, 사용 중인 인스턴스 타입과 대략적인 파드 수(노드당 평균), 서브넷 CIDR(예: 10.0.0.0/20)만 알려주면 “현재 구조에서 IP 고갈이 언제 터질지”를 대략 계산하는 방식으로 용량 계획(capacity planning)까지 같이 정리해 드릴 수 있습니다.