- Published on
K8s Pod가 Pending? 스케줄링 실패 12가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스처럼 보이지만, 쿠버네티스의 Pod는 결국 어떤 Node에 올라가야만 실행됩니다. Pod가 Pending에 오래 머무른다는 건 대개 컨테이너 이미지 풀이나 앱 문제 이전에, 스케줄러가 배치 대상 노드를 찾지 못했다는 뜻입니다.
이 글은 “왜 Pending인가?”를 빠르게 좁히기 위한 실전 체크리스트입니다. 특히 kubectl describe pod의 Events에 찍히는 문구(예: 0/3 nodes are available)를 기준으로 12가지 대표 원인을 분류했습니다.
0. 먼저: Pending이 ‘스케줄링’ 문제인지 확인
Pending은 크게 두 갈래입니다.
- 스케줄링 실패: 노드가 아예 지정되지 않음 (
PodScheduled=False) - 스케줄링은 됐는데 실행 불가: 노드 지정은 됐지만 이미지 풀/볼륨 마운트 등에서 대기
아래 명령으로 먼저 갈라냅니다.
kubectl get pod -n <ns> <pod> -o wide
kubectl describe pod -n <ns> <pod>
# 조건만 빠르게 보고 싶으면
kubectl get pod -n <ns> <pod> -o jsonpath='{range .status.conditions[*]}{.type}={.status} {.reason} {.message}{"\n"}{end}'
describe의 Events에 아래 같은 문구가 있으면 이 글의 범위(스케줄링 실패)입니다.
FailedScheduling0/N nodes are available: ...
1) CPU/메모리 요청량(Requests) 과다
증상
Events에 자주 보이는 형태:
Insufficient cpuInsufficient memory
원인
resources.requests가 노드의 allocatable을 초과하거나, 클러스터 전체에 여유가 없는 상태입니다. 특히 HPA/VPA, 기본 템플릿 복붙으로 요청량이 과해지는 경우가 많습니다.
해결
- 요청량을 현실적으로 조정
- 노드 스케일 아웃(Cluster Autoscaler 등)
- QoS 정책 재검토
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
2) Pod 개수(Pod capacity) 한도 초과
증상
Too many pods또는 CNI/노드 설정에 따른 pod 수 제한으로 스케줄 불가
원인
노드당 Pod 수는 kubelet 설정 및 CNI(예: AWS VPC CNI의 IP 할당) 제약을 받습니다. 노드에 자원이 남아도 IP/Pod 슬롯이 없어서 Pending이 됩니다.
해결
- 노드 타입/ENI/IP 풀 확장
- 노드 수를 늘려 분산
- DaemonSet 과다 여부 점검
EKS에서 네트워크 제약이 얽히는 케이스는 아래 글의 점검 흐름도 함께 참고하면 좋습니다.
3) nodeSelector 불일치
증상
0/N nodes are available: N node(s) didn't match node selector.
원인
Pod가 요구하는 라벨이 실제 노드에 없거나 오타가 있습니다.
해결
- 노드 라벨 확인/수정
- selector 키/값 오타 제거
kubectl get nodes --show-labels
kubectl label node <node> workload=api
spec:
nodeSelector:
workload: api
4) Node Affinity(필수) 조건 불일치
증상
didn't match Pod's node affinity/selector
원인
requiredDuringSchedulingIgnoredDuringExecution의 조건이 너무 빡세거나, 노드 라벨 변경으로 조건이 깨졌습니다.
해결
- required를 preferred로 완화
- 라벨 체계 정리(환경/가용영역/워크로드 라벨)
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["ap-northeast-2a"]
5) Taints/Tolerations 미스매치
증상
node(s) had taint {key: value}, that the pod didn't tolerate
원인
노드가 taint로 보호되고 있는데 Pod가 toleration을 갖고 있지 않습니다. 운영에서 흔한 패턴:
- 전용 노드풀(예:
dedicated=ml:NoSchedule) - 장애/디스크 이슈 노드의
NoSchedule
해결
- 필요한 Pod에만 toleration 부여
- taint 제거는 신중히(노드풀 격리 무너짐)
kubectl describe node <node> | sed -n '/Taints/,$p'
tolerations:
- key: "dedicated"
operator: "Equal"
value: "ml"
effect: "NoSchedule"
6) Pod Anti-Affinity/TopologySpreadConstraints로 인해 배치 불가
증상
didn't match pod anti-affinity rulesconstraints violated류 메시지
원인
고가용성을 위해 같은 노드/존에 같이 못 올라가게 막아뒀는데, 실제 노드 수/존 수가 부족해 조건을 만족할 수 없습니다.
해결
- anti-affinity를
preferred로 완화 - spread constraint의
whenUnsatisfiable을ScheduleAnyway로 조정(의도 확인 필요) - 노드/존 확장
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: api
topologyKey: kubernetes.io/hostname
7) PVC(스토리지) 바인딩 대기(WaitForFirstConsumer)
증상
pod has unbound immediate PersistentVolumeClaims- PVC가
Pending이며 PV가 안 잡힘
원인
StorageClass가 WaitForFirstConsumer인 경우, 스케줄링과 볼륨 프로비저닝이 엮입니다. 또한 AZ 제약(EBS 등) 때문에 특정 존에만 볼륨을 만들 수 있는데, Pod 조건이 그 존과 충돌하면 계속 Pending이 됩니다.
해결
- PVC/PV/StorageClass 상태 확인
- topology 제약(존) 정합성 맞추기
- 잘못된 accessMode/size 요청 수정
kubectl get pvc -n <ns>
kubectl describe pvc -n <ns> <pvc>
kubectl get storageclass
8) HostPort/NodePort 충돌(포트 바인딩 불가)
증상
didn't have free ports for the requested pod ports
원인
hostPort를 쓰는 Pod가 이미 같은 노드에서 해당 포트를 점유 중입니다. DaemonSet + hostPort 조합에서 특히 자주 터집니다.
해결
- hostPort 사용 최소화(가능하면 Service로)
- 포트 변경 또는 노드 수 확장
containers:
- name: agent
image: example/agent:1
ports:
- containerPort: 8125
hostPort: 8125
protocol: UDP
9) 우선순위/선점(Preemption) 실패
증상
preemption: ... No preemption victims foundpreemption is not helpful for scheduling
원인
클러스터가 꽉 찼을 때 높은 우선순위 Pod가 낮은 우선순위 Pod를 밀어내며 들어올 수 있습니다. 하지만
- 낮은 우선순위 Pod가 없거나
- 밀어내도 자원이 충분히 안 생기거나
- PDB(중단 예산) 때문에 축출 불가
이면 선점이 실패합니다.
해결
- PriorityClass 설계 재검토
- PDB 완화(가용성 요구와 균형)
- 노드 스케일 아웃
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical
value: 100000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Critical workloads"
10) 리소스 쿼터(ResourceQuota) 또는 LimitRange 제약
증상
FailedScheduling보다는 생성 단계에서 막히는 경우도 있지만, 네임스페이스 정책이 강하면 Pending/생성 실패가 섞여 보일 수 있습니다.
exceeded quota- 기본 requests/limits 강제 부여로 결과적으로 Insufficient 발생
원인
네임스페이스에 CPU/메모리/Pod 수 쿼터가 걸려 있거나, LimitRange가 최소 requests를 강제해 노드에 못 올라가는 크기가 됩니다.
해결
kubectl describe quota,kubectl describe limitrange로 정책 확인- 쿼터 상향 또는 배포 스펙 조정
kubectl get resourcequota -n <ns>
kubectl describe resourcequota -n <ns>
kubectl get limitrange -n <ns>
kubectl describe limitrange -n <ns>
11) 노드가 NotReady / Cordon / Drain 상태
증상
node(s) were not readynode(s) were unschedulable
원인
노드가 NotReady이거나 운영자가 cordon 해둔 상태입니다. 장애/업그레이드/드레인 작업 중 흔합니다.
해결
- 노드 상태 확인 후 원인(CNI, kubelet, 디스크 압박 등) 제거
- 의도된 cordon이면 노드풀 증설 또는 해제
kubectl get nodes
kubectl describe node <node>
# 스케줄링 재개
kubectl uncordon <node>
12) 스케줄러/클러스터 정책(Admission, PSP 대체, SCC 등) 간접 영향
증상
겉으로는 Pending인데, 실제로는 정책이 스펙을 변형/거부하거나 특정 노드로만 몰리게 만들어 스케줄링이 막힙니다. 예:
- 특정 런타임 클래스/보안 정책으로 인해 일부 노드만 후보가 됨
- sidecar 주입(istio 등)으로 requests가 커져 Insufficient로 변함
원인
Admission Controller, 보안 정책(PSA, OPA/Gatekeeper, Kyverno), RuntimeClass, 서비스 메시 주입 등이 Pod 스펙을 바꾸거나 노드 후보군을 줄입니다.
해결
- 실제 적용된 Pod 스펙을 확인(배포 YAML vs 생성된 Pod)
kubectl get pod -o yaml로 변형 여부 확인- 정책 로그/리포트(OPA/Kyverno) 확인
# 생성된 Pod 스펙이 어떻게 바뀌었는지 확인
kubectl get pod -n <ns> <pod> -o yaml | less
# Mutating/ValidatingWebhook 확인
kubectl get mutatingwebhookconfigurations
kubectl get validatingwebhookconfigurations
빠른 진단 루틴(5분 컷)
운영에서 가장 효율이 좋았던 순서입니다.
kubectl describe podEvents에서 정확한 문구를 읽는다.0/N nodes are available뒤의 이유를 분류한다.- 아래 3가지는 동시에 확인한다.
- 노드 상태:
kubectl get nodes - 자원 여유:
kubectl top nodes(metrics-server 필요) - 제약 조건: nodeSelector/affinity/taints/PVC
- 노드 상태:
kubectl describe pod -n <ns> <pod> | sed -n '/Events/,$p'
kubectl get nodes
kubectl top nodes || true
kubectl get pod -n <ns> <pod> -o jsonpath='{.spec.nodeSelector}{"\n"}{.spec.affinity}{"\n"}{.spec.tolerations}{"\n"}'
Pending 이후(노드 지정은 됐는데)도 자주 헷갈리는 포인트
이번 글은 스케줄링 실패 중심이지만, 현장에서는 “Pending인 줄 알았는데 사실 ContainerCreating에서 멈춤” 같은 케이스도 많습니다. 그때는 이미지 풀, CNI, 볼륨 마운트, DNS 등을 봐야 합니다. 네트워크/엣지 진단을 빠르게 끝내는 접근은 아래 글의 ‘로그로 30분 진단’ 방식이 사고 흐름을 잡는 데 도움이 됩니다.
마무리: Events는 답을 알고 있다
Pod Pending은 막연히 “클러스터가 이상하다”가 아니라, 스케줄러가 왜 배치할 수 없는지를 Events에 거의 항상 남깁니다. 핵심은 그 메시지를 12가지 범주(자원/라벨·어피니티/테인트/스토리지/포트/우선순위/정책/노드상태) 중 어디에 넣을지 빠르게 익히는 것입니다.
다음에 Pending을 만나면, 먼저 kubectl describe pod의 FailedScheduling 한 줄을 복사해 두고 이 체크리스트에 대입해 보세요. 대부분은 5~10분 안에 원인이 좁혀집니다.