- Published on
EKS Pod Pending - CNI IP 고갈 원인과 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
EKS를 운영하다 보면 어느 순간부터 새로 뜨는 Pod가 Pending에 멈추고, 노드 리소스는 남아 있는데도 스케줄링이 진행되지 않는 상황을 만납니다. 이때 가장 흔한 원인 중 하나가 AWS VPC CNI의 Pod IP 고갈입니다. 즉, 노드가 CPU/메모리는 충분하지만 Pod에 줄 IPv4 주소가 더 이상 없어 네트워크 인터페이스 할당 단계에서 막히는 케이스입니다.
이 글에서는 “왜 IP가 고갈되는지”를 AWS VPC CNI의 동작 원리부터 짚고, 현장에서 바로 적용 가능한 진단 커맨드와 해결 옵션(설정 튜닝, Prefix Delegation, 노드/서브넷 설계 변경)을 정리합니다.
관련해서 EKS 장애 트러블슈팅을 더 모아보고 싶다면, ECR 권한/엔드포인트 정책으로 ImagePullBackOff가 나는 케이스도 자주 함께 발생하니 아래 글도 참고할 만합니다.
증상: Pod는 Pending, 노드는 여유, 이벤트에 IP 부족
대표 증상은 다음과 같습니다.
kubectl get pod -A에서 특정 워크로드가 계속Pendingkubectl describe pod ...이벤트에FailedCreatePodSandBox또는 CNI 관련 에러- 노드
Ready상태는 정상인데 새 Pod만 안 뜸
우선 Pod 이벤트부터 확인합니다.
kubectl describe pod -n <namespace> <pod-name>
여기서 <namespace>, <pod-name> 같은 표기는 MDX에서 태그로 오인될 수 있으니 실제로는 여러분 환경 값으로 치환해서 실행하세요.
자주 보이는 단서는 다음과 같습니다.
- CNI가 IP를 할당하지 못했다는 메시지
failed to assign an IP address to containerInsufficient pods라는 노드 스케줄링 에러(리소스가 아닌podscapacity 부족)
노드가 “Pod 개수 제한”에 걸린 것처럼 보이기도 하는데, EKS에서 이 제한은 대개 노드 인스턴스 타입별 ENI/IP 한도와 직결됩니다.
원리: AWS VPC CNI는 Pod에 VPC IP를 직접 할당한다
EKS의 기본 네트워킹인 AWS VPC CNI는 Pod에 오버레이 IP를 주는 대신, VPC 서브넷의 실제 IPv4를 Pod에 직접 붙입니다.
- 노드에는 하나 이상의 ENI(Elastic Network Interface)가 붙음
- ENI에는 여러 개의 secondary IPv4를 붙일 수 있음
- CNI는 이 secondary IPv4를 Pod에 할당
따라서 다음 중 하나라도 부족해지면 Pod가 뜨지 않습니다.
- 노드가 더 붙일 수 있는 ENI 수가 한도에 도달
- ENI에 더 붙일 수 있는 secondary IPv4 수가 한도에 도달
- 서브넷에 남은 IPv4 자체가 부족
- CNI warm pool 설정이 비효율적으로 잡혀 IP가 “대기 풀”에서 낭비
특히 3번은 서브넷 크기(/24 같은 작은 CIDR)에서 흔하고, 4번은 트래픽 변동이 큰 환경에서 과도한 WARM_IP_TARGET 등으로 쉽게 발생합니다.
1차 진단: 이벤트, 노드 Pod 수, CNI 로그
1) 노드별 Pod 수와 한도 확인
kubectl get nodes
kubectl describe node <node-name> | sed -n '/Capacity:/,/Allocatable:/p'
여기서 pods: capacity를 확인합니다. 이 값이 생각보다 낮다면(예: 17, 29, 58 등) 인스턴스 타입의 ENI/IP 한도에 의해 결정된 값일 가능성이 큽니다.
또한 실제로 Pod가 얼마나 찼는지 보려면:
kubectl get pods -A -o wide --field-selector spec.nodeName=<node-name>
2) aws-node(CNI) DaemonSet 로그 확인
AWS VPC CNI는 kube-system 네임스페이스의 aws-node로 동작합니다.
kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
kubectl -n kube-system logs <aws-node-pod> -c aws-node --tail=200
로그에서 IP 할당 실패, ENI attach 실패, insufficient IP 같은 키워드를 찾습니다.
3) 서브넷 IP 여유량 확인
클러스터가 사용하는 서브넷이 어디인지부터 확인해야 합니다.
- 관리형 노드그룹이라면 노드그룹이 붙는 서브넷
- Karpenter라면 NodeClass/Provisioner 설정의 서브넷 셀렉터
AWS CLI로 서브넷의 남은 IP를 빠르게 확인할 수 있습니다.
aws ec2 describe-subnets \
--subnet-ids <subnet-id-1> <subnet-id-2> \
--query 'Subnets[].{SubnetId:SubnetId,AvailableIpAddressCount:AvailableIpAddressCount,CidrBlock:CidrBlock}' \
--output table
AvailableIpAddressCount가 낮다면(예: 수십 단위) 곧바로 IP 고갈로 이어질 수 있습니다.
해결 전략 개요: “더 큰 풀” vs “더 효율적인 할당”
해결은 크게 두 갈래입니다.
- 공급 확대: 더 큰/더 많은 서브넷, 노드 수 확장, 인스턴스 타입 변경
- 효율 개선: Prefix Delegation, warm pool 튜닝, 불필요한 Pod 감소
운영 관점에서는 “지금 당장 서비스 복구”와 “재발 방지”를 분리하는 것이 좋습니다.
- 즉시 복구: 노드 스케일 아웃, 더 큰 인스턴스로 교체, 서브넷 추가
- 재발 방지: Prefix Delegation 활성화, warm pool 최적화, 서브넷 CIDR 재설계
즉시 복구 1: 노드 스케일 아웃 또는 인스턴스 타입 상향
가장 빠른 응급 처치는 노드를 늘리는 것입니다.
- Managed Node Group desired size 증가
- Karpenter라면 provisioner 제약 완화(가용 인스턴스 타입 풀 확대)
다만 이 방법은 서브넷 IP 자체가 고갈이라면 효과가 없습니다. 노드를 더 띄워도 붙일 IP가 없기 때문입니다.
또한 인스턴스 타입별로 ENI/IP 한도가 달라 Pod 수 상한이 달라집니다. 예를 들어 작은 인스턴스는 Pod 상한이 매우 낮을 수 있습니다. “노드 하나에 Pod를 많이”가 목표라면 인스턴스 타입 변경이 더 효과적일 때도 많습니다.
즉시 복구 2: 서브넷 추가 또는 CIDR 확장
서브넷의 AvailableIpAddressCount가 바닥이라면 근본 원인은 서브넷 크기입니다.
선택지는 다음입니다.
- (권장) 새로운 더 큰 서브넷을 추가하고 노드가 그 서브넷을 사용하도록 변경
- VPC에 Secondary CIDR 추가 후 서브넷을 더 생성
- 기존 서브넷 CIDR 확장은 제약이 많아 계획적으로 접근 필요
EKS 자체는 여러 서브넷을 활용할 수 있으므로, 노드그룹/오토스케일러가 새 서브넷을 선택하도록 구성하는 방식이 일반적입니다.
재발 방지 1: Prefix Delegation 활성화
AWS VPC CNI의 Prefix Delegation은 ENI에 개별 IP를 여러 개 붙이는 대신, IP 프리픽스 단위로 위임해 Pod IP 확장성을 크게 개선합니다.
- IP 할당 효율 증가
- ENI attach/secondary IP 관리 오버헤드 감소
- 고밀도 Pod 배치에서 특히 유리
활성화는 aws-node의 환경 변수로 제어합니다. 일반적으로 DaemonSet에 아래 값을 추가합니다.
kubectl -n kube-system set env daemonset aws-node \
ENABLE_PREFIX_DELEGATION=true \
WARM_PREFIX_TARGET=1
적용 후에는 롤아웃을 확인합니다.
kubectl -n kube-system rollout status ds/aws-node
주의할 점:
- Prefix Delegation이 항상 만능은 아닙니다. 서브넷 IP 자체가 너무 작으면 결국 고갈됩니다.
- 일부 환경에서는 워크로드 특성상 warm prefix를 너무 크게 잡으면 “대기 프리픽스”가 낭비가 될 수 있습니다.
재발 방지 2: WARM_IP_TARGET / MINIMUM_IP_TARGET 튜닝
CNI는 “Pod가 필요로 하기 전에” 미리 IP를 확보해두는 warm pool 개념이 있습니다. 트래픽이 급증할 때 Pod가 빠르게 뜨도록 돕지만, 설정이 과하면 IP를 과도하게 선점합니다.
자주 쓰는 환경 변수:
WARM_IP_TARGET: 미리 확보할 여분 IP 개수 목표MINIMUM_IP_TARGET: 최소 확보 IP 개수
예시로, 불필요하게 큰 warm pool을 줄이는 식의 튜닝이 가능합니다.
kubectl -n kube-system set env daemonset aws-node \
WARM_IP_TARGET=5 \
MINIMUM_IP_TARGET=5
어떤 값이 정답인지는 워크로드 스케일 패턴에 따라 다릅니다.
- 배포가 잦고 순간적으로 Pod가 많이 늘면 warm을 어느 정도 유지
- 항상 일정한 트래픽이면 warm을 낮춰 IP 낭비를 줄임
튜닝 후에는 실제로 Pending이 줄었는지, 서브넷 가용 IP가 안정적으로 유지되는지 관측해야 합니다.
재발 방지 3: 불필요한 Pod 확장을 줄이는 오토스케일링 점검
IP 고갈은 “네트워크 문제”처럼 보이지만, 사실상 Pod 수가 급증하면 가장 먼저 터지는 병목 중 하나입니다.
- HPA가 외부 지표 이상치로 폭주
- 큐 기반 워커가 순간적으로 수천 Pod까지 증가
- CronJob이 겹쳐서 동시 실행이 폭증
이런 경우 네트워크를 키우는 것만으로는 비용이 계속 증가합니다. 오토스케일링 자체를 안정화하는 접근이 필요합니다.
큐 기반 스케일링으로 HPA 폭주를 완화하는 패턴은 아래 글이 참고가 됩니다.
실전 체크리스트: 원인별 빠른 분기
운영 중에는 아래 순서로 분기하면 빠릅니다.
A) 서브넷 AvailableIpAddressCount가 낮다
- 결론: 서브넷이 작거나 이미 과밀
- 대응: 서브넷 추가, 더 큰 CIDR 설계, 워크로드 분산
B) 서브넷 IP는 충분한데 노드에서 pods capacity가 낮다
- 결론: 인스턴스 타입 ENI/IP 한도 문제일 가능성
- 대응: 인스턴스 타입 상향, Prefix Delegation, 노드 수 증가
C) 평소엔 괜찮다가 배포/스파이크 때만 Pending이 뜬다
- 결론: warm pool 부족 또는 순간 Pod 폭증
- 대응:
WARM_IP_TARGET또는WARM_PREFIX_TARGET조정, 오토스케일링 안정화
관측: Pending 재발을 막는 메트릭/알람 포인트
재발 방지를 위해서는 “Pod Pending”만 알람 걸면 늦습니다. 다음을 같이 보세요.
- 서브넷
AvailableIpAddressCount추이 - 노드별 Pod 수, 노드 증가 속도
aws-node로그 에러 카운트- 워크로드별 replica 급증 이벤트(HPA/KEDA)
CloudWatch, Prometheus, Grafana 무엇을 쓰든 상관없지만, 최소한 서브넷 가용 IP는 조기 경보로 매우 유용합니다.
마무리: IP 고갈은 설계 이슈로 돌아온다
EKS에서 Pod가 Pending으로 멈추는 IP 고갈 문제는 “CNI 버그”라기보다, 대부분 서브넷 크기, 인스턴스 타입 한도, 오토스케일링 패턴이 합쳐져 발생합니다.
- 단기 복구는 노드/서브넷 확장
- 중장기 해법은 Prefix Delegation과 warm pool 최적화
- 궁극적으로는 Pod 수가 폭주하지 않도록 스케일링 정책을 안정화
위의 진단 커맨드로 원인을 먼저 분류한 다음, 여러분 환경에 맞는 조합으로 적용하면 Pending 상태를 빠르게 해소하고 재발 가능성도 크게 낮출 수 있습니다.