Published on

EKS Pod NotReady(NetworkPlugin cni) 10분 해결

Authors

서버리스가 아닌 이상, EKS 장애의 절반은 결국 네트워크에서 터집니다. 그중에서도 Pod가 Running인데 Ready가 안 되고, 이벤트에 NetworkPlugin cni가 찍히는 케이스는 AWS VPC CNI(aws-node) 쪽에서 IP 할당/ENI attach/iptables 설정이 막힌 상황이 대부분입니다. 이 글은 “10분 안에” 원인을 좁히고, 실제로 복구까지 가는 루트를 우선순위대로 제공합니다.

아래 증상 중 하나라도 해당하면 그대로 따라오면 됩니다.

  • kubectl get pod에서 0/1 NotReady가 오래 지속
  • kubectl describe pod 이벤트에 NetworkPlugin cni failed to set up pod network / cni / add cmd 계열 메시지
  • 노드가 새로 붙은 직후, 특정 노드에만 NotReady가 몰림

0) 1분 진단: “Pod 문제”가 아니라 “노드/CNI 문제”인지 확인

먼저 문제 Pod 하나를 집어서 이벤트를 봅니다.

NS=default
POD=my-pod
kubectl -n $NS describe pod $POD | sed -n '/Events:/,$p'

여기서 NetworkPlugin cni / FailedCreatePodSandBox / failed to assign an IP address to container 같은 문구가 보이면, 대부분 Pod 스펙 문제가 아니라 노드의 CNI 또는 IP/ENI 리소스 문제입니다.

다음으로 “특정 노드에만” 몰리는지 확인합니다.

kubectl -n $NS get pod -o wide | grep -E "${POD}|NAME"
NODE=$(kubectl -n $NS get pod $POD -o jsonpath='{.spec.nodeName}')
echo $NODE

특정 노드에서만 재현되면, 그 노드의 aws-node(VPC CNI) 또는 노드 네트워크(보안그룹/NACL/라우팅/IMDS) 이슈일 가능성이 큽니다.


1) 3분 해결 1순위: VPC CNI(aws-node) DaemonSet 상태부터 본다

EKS에서 CNI는 보통 kube-systemaws-node DaemonSet이 담당합니다.

kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pod -l k8s-app=aws-node -o wide

여기서 확인 포인트:

  • DESIRED != READY 이면 CNI가 일부 노드에서 제대로 안 뜬 상태
  • 문제 노드에 aws-node Pod가 없거나 CrashLoopBackOff, ImagePullBackOff면 그게 원인

문제 노드의 aws-node 로그를 바로 봅니다.

# 문제 노드에 떠 있는 aws-node Pod 찾기
kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | grep "$NODE"

AWSNODE_POD=$(kubectl -n kube-system get pod -l k8s-app=aws-node -o wide | awk -v n="$NODE" '$0 ~ n {print $1; exit}')

kubectl -n kube-system logs $AWSNODE_POD --tail=200

로그에서 자주 나오는 핵심 키워드:

  • failed to assign an IP addressIP 고갈 또는 ENI attach 실패
  • UnauthorizedOperation / AccessDenied노드 IAM 권한 문제
  • i/o timeout / context deadline exceededAWS API 호출이 막힘(NACL/라우팅/프록시/엔드포인트)

> CNI 자체가 CrashLoopBackOff라면, 원인 분석 흐름은 CrashLoop 디버깅과 동일합니다. 필요하면 Kubernetes CrashLoopBackOff 원인별 로그·Probe·리소스 디버깅도 같이 보세요.


2) 5분 해결 2순위: “IP 고갈”인지 가장 빨리 확인하는 법

NetworkPlugin cni에서 제일 흔한 건 노드에 할당 가능한 Pod IP가 부족한 상황입니다. EKS VPC CNI는 노드(EC2)에 붙은 ENI의 secondary IP를 Pod에 할당합니다.

2-1) 노드에서 할당 가능한 IP가 남았는지 확인

가장 빠른 방법은 aws-node가 남기는 상태/이벤트와 함께, 노드에 Pod가 과밀인지 보는 겁니다.

kubectl describe node $NODE | sed -n '/Non-terminated Pods:/,/Allocated resources:/p'

Pod가 많고, 특히 작은 인스턴스 타입(t3.small 등)에서 자주 터집니다.

2-2) 해결 옵션(즉시성 우선)

  1. 문제 노드만 cordon/drain 후 교체 (가장 빠른 응급조치)
kubectl cordon $NODE
kubectl drain $NODE --ignore-daemonsets --delete-emptydir-data --force

노드그룹이 관리형이면 새 노드가 붙으면서 IP 풀도 새로 생겨 즉시 완화됩니다.

  1. 노드 인스턴스 타입 상향 (ENI/IP 한도 증가)
  • 인스턴스 타입마다 ENI 개수/ENI당 IP 수가 달라 Pod 수 상한이 달라집니다.
  1. VPC CNI 설정 튜닝 (장기 처방)

대표적으로 아래 환경변수들이 영향을 줍니다.

  • WARM_IP_TARGET, MINIMUM_IP_TARGET: 미리 확보할 IP 수
  • WARM_ENI_TARGET: 미리 붙여둘 ENI 수

> 다만 “지금 NotReady를 10분 내 해결”이 목표라면, 튜닝은 재발 방지 단계에서 하세요. 당장 급하면 노드 교체가 제일 빠릅니다.


3) 7분 해결 3순위: 노드 IAM/IMDS 문제로 CNI가 AWS API를 못 치는 경우

aws-node는 ENI attach, secondary IP 할당 등을 위해 AWS API를 호출합니다. 여기서 막히면 Pod 네트워크 설정이 실패합니다.

3-1) 로그에서 권한 에러 확인

kubectl -n kube-system logs $AWSNODE_POD --tail=300 | egrep -i "accessdenied|unauthorized|not authorized|assume|sts"

보이면 다음을 점검:

  • 워커 노드 IAM Role에 AmazonEKS_CNI_Policy(또는 동등 권한)가 붙어 있는지
  • IRSA를 쓰는 구성이라면 aws-node ServiceAccount에 올바른 Role annotation이 있는지
kubectl -n kube-system get sa aws-node -o yaml | sed -n '/annotations:/,/secrets:/p'

3-2) IMDS(169.254.169.254) 접근 차단 이슈

노드가 메타데이터(IMDS)에서 자격증명을 못 가져오면 CNI가 AWS API 호출에 실패합니다.

  • 노드 보안그룹/iptables에서 IMDS로 가는 트래픽을 막았는지
  • IMDSv2 강제 환경에서 hop limit/토큰 설정이 꼬였는지

이 케이스는 특정 AMI/런치 템플릿 변경 직후 갑자기 터지는 경우가 많습니다.


4) 8분 해결 4순위: 보안그룹/NACL/라우팅으로 “필수 트래픽”이 막힌 경우

CNI는 크게 두 종류의 통신이 필요합니다.

  1. AWS API 호출(EC2, STS 등)
  2. 클러스터 내부 통신(노드↔노드, 노드↔Control Plane, DNS 등)

프라이빗 서브넷에서 NAT 없이 운영한다면 VPC Endpoint(Interface/Gateway)가 필요하고, NACL이 빡세면 i/o timeout이 납니다.

특히 “DNS는 되는데 HTTPS만 실패” 같은 형태로도 나타날 수 있는데, 이건 네트워크 디버깅 관점이 유사합니다. 같이 보면 도움이 됩니다: EKS Pod DNS는 되는데 HTTPS만 실패할 때 점검

4-1) 빠른 체크: CoreDNS/클러스터 기본 통신도 같이 죽었나?

kubectl -n kube-system get pod -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50

CoreDNS까지 같이 이상하면, CNI 단독 문제가 아니라 노드 네트워크 레벨 장애일 확률이 올라갑니다.


5) 9분 해결 5순위: CNI 버전/애드온 상태 불일치(업데이트 후 흔함)

EKS에서는 VPC CNI를 애드온으로 관리하는 경우가 많습니다. 클러스터 업그레이드 후 CNI가 오래된 버전이거나, 애드온이 꼬이면 특정 노드에서만 CNI가 제대로 동작하지 않을 수 있습니다.

5-1) 애드온 버전 확인 및 정렬

(콘솔/terraform로 보는 게 보통이지만, 현장에서는 kubectl로도 힌트를 얻습니다.)

kubectl -n kube-system describe ds aws-node | sed -n '/Image:/p'

이미지가 너무 구버전이면, EKS 권장 버전으로 올리는 것을 고려하세요.

  • 단, 운영 중 즉시 업그레이드는 리스크가 있으니 먼저 문제 노드 교체로 불을 끄고, 점검 창에 계획적으로 진행하는 편이 안전합니다.

6) 10분 응급 복구 플랜(현장에서 제일 많이 쓰는 순서)

여기까지 점검했는데도 당장 서비스가 급하면, 아래 “응급 플랜”이 가장 성공률이 높습니다.

6-1) 문제 노드 격리 → 재스케줄

kubectl cordon $NODE
kubectl drain $NODE --ignore-daemonsets --delete-emptydir-data --force
  • NotReady Pod가 다른 노드로 옮겨가며 정상화되는지 확인
kubectl -n $NS get pod -o wide

6-2) 노드그룹 스케일 아웃으로 IP/ENI 압력 완화

  • 노드 수를 늘려 Pod 분산
  • IP 고갈/ENI 한도 문제일 때 효과가 즉각적

6-3) (가능하면) 문제 노드 종료로 강제 교체

  • 관리형 노드그룹이면 자동으로 replacement가 붙습니다.

재발 방지 체크리스트(원인별로 딱 1개씩)

  • IP 고갈: 인스턴스 타입 상향 또는 노드 스케일 아웃 + WARM_IP_TARGET/MINIMUM_IP_TARGET 정책 정리
  • IAM/IMDS: 노드 Role/IRSA 권한 표준화, 런치 템플릿 변경 시 IMDS 옵션 검증
  • 네트워크 차단: 프라이빗 환경이면 NAT 또는 VPC Endpoint 설계 재검토, NACL 최소화
  • 애드온 불일치: 클러스터 업그레이드 절차에 VPC CNI/CoreDNS/kube-proxy 버전 정렬 포함

추가로, 네트워크 문제가 터지면 디버깅 중 kubectl port-forward가 끊기거나 hang 되는 부수 증상도 자주 같이 옵니다. 그런 케이스는 EKS에서 kubectl port-forward 끊김·hang 해결도 참고하면 원인 분리(로컬 문제 vs 클러스터 네트워크 문제)에 도움이 됩니다.


마무리: 핵심은 “CNI 로그 + IP/ENI + AWS API 경로” 3가지

Pod NotReady(NetworkPlugin cni)는 보기엔 Pod 문제 같지만, 실제로는 노드가 Pod IP를 만들지 못하는 상태입니다. 10분 내 해결하려면 아래 3가지만 순서대로 보면 됩니다.

  1. aws-node가 문제 노드에서 정상인가?
  2. IP/ENI가 고갈되지 않았나?
  3. CNI가 AWS API를 호출할 수 있는 IAM/네트워크 경로가 열려 있나?

이 3가지를 커맨드로 확인하면, 대부분은 “노드 교체로 즉시 복구 + 원인에 맞는 재발 방지”까지 한 번에 정리됩니다.