- Published on
EKS Pod Pending - CNI IP 고갈 원인과 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS에서 새로 배포한 Pod가 Pending 상태에서 움직이지 않고, 이벤트에 failed to assign an IP address to container 또는 Insufficient pods 같은 메시지가 찍히는 경우가 있습니다. 노드 CPU/메모리는 충분해 보이는데도 스케줄링이 안 된다면, 의외로 AWS VPC CNI가 할당할 수 있는 IP가 고갈된 상황일 가능성이 큽니다.
이 글에서는 “Pod Pending = IP 고갈”을 빠르게 확정하는 방법, VPC CNI의 IP/ENI 모델을 기반으로 한 용량 계산, 그리고 운영에서 바로 적용 가능한 완화책과 구조적 해결책을 단계적으로 정리합니다.
관련해서 EKS에서 다른 유형의 장애(노드 부트스트랩 실패, DiskPressure, CoreDNS 장애 등)도 자주 섞여 보이므로, 원인 분리를 위해 아래 글도 함께 참고하면 좋습니다.
- EKS 노드가 Join 못할 때 - bootstrap.sh 실패 진단
- EKS DiskPressure로 Pod Evicted 폭주 해결 10가지
- AWS EKS CoreDNS CrashLoopBackOff와 DNS 타임아웃 해결
증상: Pending인데 리소스는 충분한데?
대표 증상은 다음과 같습니다.
kubectl get pod에서 특정 Deployment/Job의 Pod가 계속Pendingkubectl describe pod이벤트에 IP 할당 실패/ENI 부족 관련 메시지- 노드에
Ready는 떠 있고 CPU/Memory 사용률도 낮음 - 동일 노드그룹에서 특정 시점 이후 새 Pod만 안 뜸(스케일 아웃에도 계속 Pending)
먼저 Pod 이벤트를 확인합니다.
kubectl describe pod <pod-name> -n <ns>
이벤트에서 다음과 같은 문구가 보이면 거의 확정입니다.
failed to assign an IP address to containerAddNetwork failedInsufficient pods(Kubernetes 스케줄러 관점에서 “이 노드에 더 이상 Pod를 수용할 수 없음”으로 표시되며, 근본 원인이 CNI IP 제한일 수 있음)
원인: AWS VPC CNI의 IP 할당 모델(ENI 기반)
EKS 기본 네트워킹인 AWS VPC CNI는 Pod에 VPC의 사설 IP를 직접 할당합니다. 즉, Pod는 “오버레이”가 아니라 VPC IP를 소비합니다.
노드(EC2)는 다음 리소스에 의해 Pod IP 수용량이 제한됩니다.
- 인스턴스 타입별로 붙일 수 있는 ENI 개수
- ENI당 할당 가능한 IPv4 주소 개수
- VPC/서브넷에 남아있는 가용 IP 수
따라서 “클러스터 전체 IP(서브넷)”가 부족하거나, “노드당 IP(ENI/secondary IP)”가 부족해도 Pod가 Pending이 됩니다.
빠른 판단 포인트
- 한 서브넷에 노드가 몰려 있다 → 서브넷 IP 고갈 가능성 상승
- 노드가 작은 인스턴스(t3.small 등) → 노드당 ENI/IP 한계가 낮아 Pod 수가 빨리 막힘
- 대량 스케일 아웃/Job 폭주/롤링 업데이트 → 순간적으로 IP 수요가 급증
1단계: IP 고갈인지 “확정”하는 진단 절차
1) Pod 이벤트에서 CNI 오류 확인
kubectl describe pod <pod> -n <ns> | sed -n '/Events/,$p'
2) aws-node DaemonSet 로그 확인
aws-node는 VPC CNI 플러그인입니다.
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
kubectl -n kube-system logs <aws-node-pod> --tail=200
로그에서 다음 키워드를 찾습니다.
IPAMD(IP Address Management Daemon)InsufficientCidrBlocks/no available IP addressesfailed to allocate/AssignPodIPv4Address
3) 노드별 “할당 가능한 Pod 수” 확인
Kubernetes는 노드에 Allocatable로 pods 값을 노출합니다. 이 값이 낮거나 이미 꽉 찼다면 CNI 한계일 수 있습니다.
kubectl describe node <node-name> | sed -n '/Allocatable/,$p'
또는 전체 노드의 pod capacity를 훑습니다.
kubectl get nodes -o custom-columns=NAME:.metadata.name,PODS-CAP:.status.capacity.pods,PODS-ALLOC:.status.allocatable.pods
4) 서브넷 가용 IP 확인
서브넷 IP가 진짜로 고갈이면, 어떤 인스턴스 타입을 쓰든 신규 Pod/노드가 어려워집니다.
aws ec2 describe-subnets \
--subnet-ids subnet-xxxx subnet-yyyy \
--query 'Subnets[].{SubnetId:SubnetId,AZ:AvailabilityZone,CIDR:CidrBlock,AvailableIp:AvailableIpAddressCount}' \
--output table
AvailableIpAddressCount가 수십~수백 단위로 낮다면(워크로드 규모에 따라 다르지만), 롤링 업데이트/스케일 시 바로 Pending이 터질 수 있습니다.
2단계: 왜 Pending이 되는지 “수량”으로 이해하기(용량 계산)
VPC CNI는 노드에 ENI를 붙이고, ENI에 secondary IP를 붙여 Pod에 할당합니다.
- 노드가 가질 수 있는 최대 Pod 수는 대략적으로
ENI 수 * (ENI당 IP 수 - 1)수준(기본 IP는 노드 자체가 사용)- 실제 값은 인스턴스 타입에 따라 다르고, EKS는
max-pods계산 로직을 적용합니다.
EKS AMI는 보통 부트스트랩에서 인스턴스 타입별 권장 --max-pods를 설정합니다. 이 값이 너무 낮으면 IP 여유가 있어도 스케줄링이 막힐 수 있고(보수적 설정), 반대로 너무 높으면 실제 IP가 모자라 CNI 에러가 납니다.
노드의 maxPods는 kubelet 설정에서 확인할 수 있습니다(노드에 SSH가 가능하다면).
# 노드에서
ps -ef | grep kubelet | grep max-pods
cat /etc/eks/bootstrap.sh | head
운영에서는 보통 “서브넷 IP”와 “노드당 IP” 두 축으로 나눠서 봐야 합니다.
- 서브넷 IP 고갈: 전체 풀 자체가 부족
- 노드당 IP 고갈: 서브넷은 남았는데 특정 노드/인스턴스 타입이 Pod를 더 못 받음
3단계: 즉시 완화(장애 복구 목적)
장애 상황에서는 “근본 해결”보다 “빨리 Pending을 해소”하는 게 우선입니다.
1) 롤링 업데이트/대량 Job을 일시 중지
동시에 많은 Pod가 뜨면 순간 IP 수요가 폭증합니다.
kubectl rollout pause deploy/<name> -n <ns>
# 또는 Job/cronjob 일시 중지
kubectl patch cronjob/<name> -n <ns> -p '{"spec":{"suspend":true}}'
2) 불필요한 Pod 정리(특히 Completed/CrashLoopBackOff)
kubectl get pods -A --field-selector=status.phase=Succeeded
kubectl delete pod <pod> -n <ns>
3) 노드 스케일 아웃(단, 서브넷 IP가 남아있을 때만)
노드 수를 늘려 “노드당 IP 한계”를 분산합니다. 하지만 서브넷이 고갈이면 스케일 아웃 자체가 막히거나, 새 노드가 떠도 Pod IP가 없어 똑같이 Pending입니다.
4) aws-node 재시작은 최후의 수단
간혹 IPAM 상태가 꼬였다고 의심될 때 aws-node 재시작이 도움이 되기도 하지만, 근본적으로 IP가 없으면 의미가 없습니다.
kubectl -n kube-system rollout restart ds/aws-node
4단계: 근본 해결 1 — 서브넷 CIDR 확장(가장 확실)
서브넷 IP가 부족하다면 정답은 결국 “IP 풀을 늘리는 것”입니다.
현실적인 옵션은 다음 중 하나입니다.
- 더 큰 CIDR의 서브넷을 새로 만들고, 노드그룹을 해당 서브넷으로 이동/추가
- 기존 서브넷에 secondary CIDR(VPC 추가 CIDR) 기반으로 새 서브넷을 추가
주의할 점:
- EKS 노드그룹(특히 Managed Node Group)은 서브넷 변경이 제약될 수 있어, 새 노드그룹을 만들고 워크로드를 이동하는 패턴이 안전합니다.
- NAT, 라우팅 테이블, 보안그룹, 엔드포인트(PrivateLink) 등 네트워크 구성도 함께 점검해야 합니다.
5단계: 근본 해결 2 — Prefix Delegation(IPv4 /28 프리픽스) 활성화
AWS VPC CNI는 Prefix Delegation(PD) 을 통해 ENI에 개별 IP를 여러 개 붙이는 대신, /28 프리픽스(16개 IP 블록) 를 위임받아 Pod에 할당할 수 있습니다. 이 방식은 대규모 클러스터에서 IP 할당 효율과 성능을 개선하고, IP 관리 오버헤드를 줄여줍니다.
활성화는 보통 aws-node에 환경변수로 설정합니다(클러스터/버전에 따라 지원 여부 확인 필요).
kubectl -n kube-system set env daemonset/aws-node ENABLE_PREFIX_DELEGATION=true
kubectl -n kube-system set env daemonset/aws-node WARM_PREFIX_TARGET=1
WARM_PREFIX_TARGET: 노드에 미리 확보해둘 프리픽스 개수
적용 후에는 aws-node 롤아웃이 발생하고, 새로 뜨는 노드/Pod부터 정책이 반영됩니다(이미 할당된 IP가 즉시 재구성되진 않을 수 있음).
PD를 켠다고 “서브넷 총 IP가 무한”이 되는 건 아닙니다. 서브넷이 작으면 결국 고갈됩니다. 다만 노드당 할당/관리 효율이 좋아져 “노드당 IP 한계”로 인한 Pending을 줄이는 데 도움이 됩니다.
6단계: 근본 해결 3 — 인스턴스 타입/노드 설계 재검토
노드당 Pod 수가 자주 상한에 닿는다면(특히 작은 인스턴스), 인스턴스 타입 변경이 가장 간단한 해법일 수 있습니다.
- 더 큰 인스턴스는 일반적으로 ENI 수/ENI당 IP 수가 증가
- 같은 워크로드를 더 적은 노드에 밀집시키면, Pod당 오버헤드(daemonset 등)도 줄어드는 효과가 있음
또한 다음을 함께 점검하세요.
- DaemonSet이 과도하게 많아 “노드당 기본 Pod 점유”가 큰지
- HPA/Cluster Autoscaler가 동시에 반응해 “스파이크”를 만드는지
7단계: 운영 설정 팁 — WARM_IP_TARGET/WARM_ENI_TARGET 튜닝
VPC CNI는 미리 IP를 당겨두는(warm pool) 설정이 있습니다.
WARM_IP_TARGET: 노드에 미리 확보해둘 “여분 IP” 개수WARM_ENI_TARGET: 노드에 미리 붙여둘 ENI 개수
트래픽이 급증해 Pod가 한꺼번에 뜨는 환경에서는 warm pool이 너무 작으면 순간적으로 IP 할당이 늦거나 실패할 수 있습니다. 반대로 너무 크게 잡으면 서브넷 IP를 과도하게 선점해 전체 고갈을 앞당길 수 있습니다.
설정 예시는 아래와 같습니다.
# 예: 여분 IP를 10개 유지
kubectl -n kube-system set env ds/aws-node WARM_IP_TARGET=10
# 예: 여분 ENI 1개 유지(환경에 따라 비권장일 수 있음)
kubectl -n kube-system set env ds/aws-node WARM_ENI_TARGET=1
권장 접근:
- “서브넷 IP가 넉넉한가?”가 먼저입니다. 넉넉하지 않다면 warm pool을 키우는 건 독이 됩니다.
- 배포/스케일 이벤트 직후 Pending이 자주 발생한다면
WARM_IP_TARGET을 소폭 올려 효과를 관찰합니다.
8단계: 재발 방지 체크리스트(모니터링/알람)
IP 고갈은 대부분 “사후 대응”으로 끝나면 반복됩니다. 아래 지표를 최소한으로 잡아두면 조기 감지가 가능합니다.
- 서브넷
AvailableIpAddressCount주기 수집 및 임계치 알람 - 노드별
Allocatable pods대비 현재 Pod 수 비율 aws-node로그에서 IP 할당 실패 카운트(로그 기반 메트릭)
간단한 서브넷 가용 IP 점검 스크립트 예시는 다음과 같습니다.
#!/usr/bin/env bash
set -euo pipefail
SUBNETS=(subnet-aaaa subnet-bbbb)
aws ec2 describe-subnets --subnet-ids "${SUBNETS[@]}" \
--query 'Subnets[].{SubnetId:SubnetId,AZ:AvailabilityZone,AvailableIp:AvailableIpAddressCount}' \
--output table
결론
EKS에서 Pod가 Pending으로 멈출 때, CPU/메모리만 보고 있으면 시간을 크게 낭비하기 쉽습니다. AWS VPC CNI는 Pod가 VPC IP를 직접 소비하기 때문에, 서브넷 IP와 노드의 ENI/IP 한계가 곧 “스케줄링 가능 Pod 수”를 결정합니다.
정리하면 우선순위는 다음이 실전에서 가장 효과적입니다.
describe pod이벤트와aws-node로그로 IP 할당 실패를 확정- 서브넷
AvailableIpAddressCount로 “전체 풀 고갈” 여부 판단 - 즉시 완화(롤아웃 중지/불필요 Pod 정리/스케일 아웃)
- 근본 해결(서브넷 확장 + 필요 시 Prefix Delegation + 노드 설계 재검토)
이 흐름대로 접근하면 “Pending 지옥”에서 빠르게 벗어나고, 재발도 크게 줄일 수 있습니다.