- Published on
EKS Pod Pending 0/XX nodes available 원인별 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스처럼 보이는 쿠버네티스도 결국 스케줄러가 올릴 수 있는 노드가 있어야 Pod가 뜹니다. EKS에서 흔히 보는 에러가 바로 Pod Pending + 0/XX nodes available인데, 이 문구 자체는 “모든 노드를 검사했지만 조건을 만족하는 노드가 0개”라는 뜻일 뿐, 진짜 원인은 이벤트 메시지에 숨어 있습니다.
이 글은 kubectl describe pod의 이벤트를 기준으로 원인을 빠르게 분류하고, EKS(Managed Node Group/Cluster Autoscaler/Karpenter, EBS CSI 등) 환경에서 자주 터지는 케이스를 재현 가능한 커맨드와 함께 정리합니다.
> 참고로 IP 고갈로 Pending이 나는 케이스는 증상이 비슷하지만 해결책이 꽤 다릅니다. CNI/서브넷 IP 관점의 진단은 별도 글인 Kubernetes CNI IP 부족으로 Pod Pending 해결 가이드를 함께 보시면 빠릅니다.
1) 가장 먼저: 이벤트 한 줄로 원인 분류하기
Pod가 Pending이면, “노드가 없나?”부터 보기보다 이벤트에서 스케줄러가 거절한 이유를 확인하는 게 가장 빠릅니다.
kubectl get pod -n <ns> <pod> -o wide
kubectl describe pod -n <ns> <pod>
# 이벤트만 보고 싶으면
kubectl get events -n <ns> --sort-by=.metadata.creationTimestamp | tail -n 50
describe의 Events에서 아래처럼 나옵니다.
0/10 nodes are available: 10 Insufficient cpu.0/10 nodes are available: 10 Insufficient memory.0/10 nodes are available: 10 node(s) had taint {dedicated=..., NoSchedule}.0/10 nodes are available: 10 node(s) didn't match node selector.0/10 nodes are available: 10 node(s) didn't match Pod's node affinity.0/10 nodes are available: 10 node(s) had volume node affinity conflict.pod has unbound immediate PersistentVolumeClaims
이제부터는 이 문구별로 해결합니다.
2) Insufficient cpu/memory/ephemeral-storage: 리소스 요청이 너무 큼
증상
이벤트에 보통 이렇게 찍힙니다.
Insufficient cpuInsufficient memoryInsufficient ephemeral-storage
EKS에서 특히 자주 하는 실수는 limits만 올리고 requests를 과하게 잡는 것입니다. 스케줄링은 기본적으로 requests 기준으로 이루어집니다.
2-1) 현재 노드의 allocatable/사용량 확인
kubectl get nodes
kubectl describe node <node> | egrep -A5 "Allocatable|Capacity"
# metrics-server가 있다면
kubectl top nodes
kubectl top pod -A | head
kubectl top이 0이거나 비정상이라면 HPA/스케줄링 진단도 꼬일 수 있습니다. 메트릭이 0으로 나오는 문제는 Kubernetes HPA가 안 늘 때 metrics-server 0값 해결도 참고하세요.
2-2) requests/limits 재조정 (예시)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: myrepo/api:1.0
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
- requests는 현실적인 최소치로 잡아 스케줄 가능성을 높이고
- limits는 과도한 폭주를 막는 상한으로 둡니다.
2-3) 노드 증설(오토스케일러) 확인
리소스가 정말 부족한 상황이라면 노드가 늘어야 합니다.
- Managed Node Group + Cluster Autoscaler 사용 시
- ASG 최대치(
maxSize)가 낮아서 늘지 않는 경우가 흔함
- ASG 최대치(
- Karpenter 사용 시
- Provisioner/NodePool 제약(인스턴스 타입, AZ, 용량 타입) 때문에 안 늘 수 있음
확인 포인트:
# cluster-autoscaler 로그
kubectl -n kube-system logs deploy/cluster-autoscaler | tail -n 200
# karpenter 로그(설치 방식에 따라 이름 다름)
kubectl -n karpenter logs deploy/karpenter | tail -n 200
3) node(s) had taint ... NoSchedule: 태인트/톨러레이션 불일치
증상
이벤트:
node(s) had taint {dedicated=xxx: NoSchedule}
노드가 특정 워크로드 전용으로 태인트되어 있는데, Pod가 이를 허용하는 톨러레이션이 없으면 스케줄 불가입니다.
3-1) 노드 태인트 확인
kubectl describe node <node> | sed -n '/Taints:/,/Conditions:/p'
# 전체 노드 태인트를 간단히
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
3-2) 해결: 톨러레이션 추가 또는 태인트 제거
톨러레이션을 Pod에 추가:
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "batch"
effect: "NoSchedule"
혹은 노드에서 태인트 제거(운영 정책에 맞을 때만):
kubectl taint nodes <node> dedicated=batch:NoSchedule-
4) didn't match node selector / node affinity: 라벨/어피니티 조건 불일치
증상
이벤트:
didn't match node selectordidn't match Pod's node affinity
EKS에서는 노드그룹별로 라벨을 달아 워크로드를 분리하는데, 라벨이 없거나 오타가 있으면 전체 노드가 후보에서 제외됩니다.
4-1) Pod의 스케줄 제약 확인
kubectl get pod -n <ns> <pod> -o yaml | yq '.spec.nodeSelector, .spec.affinity'
4-2) 노드 라벨 확인
kubectl get nodes --show-labels
# 특정 키만 보고 싶으면
kubectl get nodes -L nodegroup,topology.kubernetes.io/zone,karpenter.sh/capacity-type
4-3) 해결
- Pod의
nodeSelector/nodeAffinity를 실제 라벨에 맞게 수정 - 또는 노드에 라벨 추가
kubectl label node <node> workload=api
5) pod has unbound immediate PersistentVolumeClaims: PVC 바인딩 실패
증상
이벤트:
pod has unbound immediate PersistentVolumeClaims
EKS에서는 보통 EBS CSI Driver로 동적 프로비저닝을 하는데, StorageClass/권한/AZ/볼륨 제약 때문에 PV가 안 만들어지면 Pod는 계속 Pending입니다.
5-1) PVC 상태 확인
kubectl get pvc -n <ns>
kubectl describe pvc -n <ns> <pvc>
kubectl get sc
kubectl describe sc <storageclass>
5-2) 자주 터지는 원인
- StorageClass에
volumeBindingMode: Immediate인데, 노드 AZ와 볼륨 AZ가 꼬이는 설계- 멀티 AZ 노드풀에서 특히 빈번
- EBS CSI Driver 미설치/비정상
- CSI가 AWS API를 못 호출(권한 문제)
- IRSA/OIDC/TrustPolicy 문제로
AccessDenied가 나면 CSI가 볼륨을 못 만듭니다.
- IRSA/OIDC/TrustPolicy 문제로
IRSA 점검은 EKS IRSA인데 AccessDenied? OIDC·TrustPolicy·SA 점검을 참고하면, “왜 권한이 있는데도 안 되지?”를 빠르게 정리할 수 있습니다.
5-3) 해결 방향
- 가능한 경우
WaitForFirstConsumer를 사용해 스케줄러가 노드를 고른 뒤 그 AZ에 볼륨을 만들게 함
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-wffc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
6) volume node affinity conflict: PV/노드 AZ 불일치(특히 EBS)
증상
이벤트:
had volume node affinity conflict
이미 존재하는 PV가 특정 AZ에 묶여 있는데(예: EBS), 스케줄러가 그 AZ의 노드에 Pod를 배치할 수 없으면 발생합니다.
체크
kubectl describe pv <pv>
# Node Affinity 섹션에서 topology.kubernetes.io/zone 확인
kubectl get nodes -L topology.kubernetes.io/zone
해결
- 해당 AZ에 노드가 존재/증설 가능하도록 노드그룹/카펜터 제약 수정
- 또는 스토리지를 AZ 종속이 덜한 형태(EFS 등)로 재검토
- Stateful workload라면 “데이터 위치가 곧 스케줄 제약”임을 전제로 설계
7) Too many pods / Insufficient pods: 노드의 Pod 수 한도(ENI/IP) 도달
증상
이벤트가 아래처럼 나올 수 있습니다.
Too many pods- 또는 간접적으로
0/XX nodes available만 보이고, 노드에는 여유 CPU/메모리가 있는데도 안 올라감
EKS의 VPC CNI는 인스턴스 타입별로 ENI/IP 기반 Pod 최대치가 사실상 정해집니다.
확인
kubectl describe node <node> | egrep -A3 "Non-terminated Pods|Allocated resources"
# aws-node 데몬셋 로그도 힌트가 됨
kubectl -n kube-system logs ds/aws-node --tail=200
해결 방향
- 노드 타입을 Pod 밀도에 맞게 조정(ENI가 많은 타입)
- 서브넷 IP 여유 확보, CNI 설정(프리 할당 등) 튜닝
- 근본 원인/해결은 IP 고갈/할당 구조와 연결되므로, 자세한 절차는 위에서 언급한 CNI IP 글을 권장
8) (덜 흔하지만 치명적) PDB/스케줄링 정책/우선순위로 막히는 경우
- PDB(PodDisruptionBudget)는 보통 eviction/드레인에 영향을 주지만, 특정 운영 자동화(업그레이드/노드 교체)와 결합되면 “노드가 비는데도 새 Pod가 못 뜨는” 상황이 길어질 수 있습니다.
- PriorityClass가 없어서 중요한 Pod가 리소스 경쟁에서 계속 밀리는 경우도 있습니다.
확인:
kubectl get pdb -A
kubectl get priorityclass
kubectl describe pod -n <ns> <pod> | egrep -i "priority|preempt"
해결은 워크로드 중요도에 맞게 PriorityClass를 정의하고, PDB는 롤링/장애 대응 시나리오를 기준으로 재설계합니다.
9) 빠른 결론: 이벤트 문자열별 “즉시 액션” 표
| 이벤트 키워드 | 1차 확인 | 즉시 해결책 |
|---|---|---|
Insufficient cpu/memory | requests/노드 allocatable | requests 낮추기 또는 노드 증설(오토스케일러/ASG max) |
had taint ... NoSchedule | node taints | tolerations 추가 또는 taint 제거 |
didn't match node selector/affinity | pod nodeSelector/affinity, node labels | 라벨/셀렉터 정합성 맞추기 |
unbound immediate PVC | PVC/SC/CSI 이벤트 | SC 수정(WFFC), CSI 상태/권한/쿼터 점검 |
volume node affinity conflict | PV AZ, 노드 AZ | 해당 AZ에 노드 확보 또는 스토리지 재설계 |
Too many pods | 노드 Pod 한도/ENI/IP | 노드 타입/서브넷/CNI 튜닝 |
10) 운영에서 재발을 줄이는 체크리스트
- 배포 전 requests sanity check: PR 단계에서 requests가 노드 타입 대비 비현실적으로 큰지 검사
- 노드풀 분리 시 라벨/태인트 표준화:
workload=,dedicated=키 네이밍을 조직 표준으로 고정 - StorageClass 기본값 검토: 멀티 AZ라면
WaitForFirstConsumer가 기본으로 더 안전한 경우가 많음 - 오토스케일러 관측성: Cluster Autoscaler/Karpenter 로그를 중앙 수집하고 “왜 scale-out이 안 됐는지”를 알람화
- CNI/IP 용량 계획: 서브넷/노드 타입/Pod 수요를 함께 계획(특히 트래픽 이벤트/배치 러시)
0/XX nodes available은 결론이 아니라 분기점입니다. describe pod 이벤트 한 줄을 기준으로 원인을 분류하면, 대부분은 10~20분 내로 “리소스/제약/스토리지/IP” 중 어디가 병목인지 확정할 수 있습니다. 그 다음부터는 해당 축을 확실히 고쳐서, Pending을 “가끔 생기는 미스터리”가 아니라 예측 가능한 용량/정책 문제로 바꾸는 게 핵심입니다.