Published on

K8s Pod Pending(0/노드) - 스케줄 불가 원인·해결

Authors

서버리스가 아닌 이상, Kubernetes에서 Pod Pending은 대부분 **"스케줄러가 배치할 노드를 못 찾는다"**는 뜻입니다. 특히 이벤트에 0/3 nodes are available 같은 문구가 붙으면, 문제는 애플리케이션이 아니라 클러스터 리소스/정책/노드 상태에 있습니다.

이 글에서는 0/노드 스케줄 불가 상황을 이벤트 메시지 → 원인 분류 → 즉시 조치 → 재발 방지 순서로 정리합니다. (EKS 예시를 섞지만, 대부분의 K8s에서 동일합니다.)

1) 가장 먼저 볼 것: Pod 이벤트 한 줄이 정답이다

Pending 자체는 정보가 부족합니다. 이벤트가 핵심입니다.

kubectl get pod -n <ns>

kubectl describe pod <pod> -n <ns>
# Events 섹션에서 FailedScheduling 메시지 확인

kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -n 30

대표적인 이벤트 패턴은 아래처럼 나뉩니다.

  • 0/3 nodes are available: 3 Insufficient cpu
  • 0/3 nodes are available: 3 Insufficient memory
  • 0/3 nodes are available: 3 node(s) had taint {…} that the pod didn't tolerate
  • 0/3 nodes are available: 3 node(s) didn't match Pod's node affinity/selector
  • 0/3 nodes are available: 3 node(s) were unschedulable
  • pod has unbound immediate PersistentVolumeClaims
  • Too many pods (노드당 Pod 수 제한)

이제부터는 이 메시지를 기준으로 빠르게 해결합니다.

2) 원인 A: 리소스 부족(Insufficient cpu/memory) — requests가 현실과 다르다

증상

이벤트에 Insufficient cpu 또는 Insufficient memory가 뜹니다.

즉시 확인

kubectl top nodes
kubectl top pod -A | head

# 노드별 allocatable/requests 확인
kubectl describe node <node> | sed -n '/Allocatable:/,/Events:/p'

또는 스케줄러 관점에서 "요청(requests)"가 과한지 확인합니다.

kubectl get pod <pod> -n <ns> -o jsonpath='{range .spec.containers[*]}{.name}{"\t"}{.resources.requests}{"\n"}{end}'

해결책

  1. requests를 낮추거나(실사용에 맞게)
  2. 노드를 늘리거나(오토스케일/노드그룹 확장)
  3. 우선순위/프리엠션을 사용합니다.

예: requests 조정

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      containers:
      - name: api
        image: myrepo/api:1.0
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "1"
            memory: "1Gi"

재발 방지 팁

  • HPA는 requests 기반으로 동작하는 경우가 많습니다(메트릭/설정에 따라 다름). requests가 과하면 스케줄 자체가 막힙니다.
  • 대규모 워크로드는 Cluster Autoscaler/Karpenter가 있어도 스케줄 조건(taint/affinity/PVC) 때문에 노드가 늘지 않을 수 있습니다. Karpenter 환경이라면 노드 생성/준비 문제도 함께 점검하세요: EKS Karpenter NodeClaim NotReady 10분 진단

3) 원인 B: taint/toleration 불일치 — 노드는 있는데 “못 올라감”

증상

이벤트에 아래가 보입니다.

  • node(s) had taint {key=value:NoSchedule} that the pod didn't tolerate

즉시 확인

kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
kubectl describe node <node> | sed -n '/Taints:/,/Conditions:/p'

해결책 1) Pod에 toleration 추가

spec:
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "gpu"
    effect: "NoSchedule"

해결책 2) 의도치 않은 taint 제거(신중)

kubectl taint nodes <node> dedicated=gpu:NoSchedule-

실무 포인트

  • 운영에서 흔한 패턴: dedicated=system 같은 taint로 시스템 노드/앱 노드를 분리합니다.
  • “특정 노드에만 올려야 한다”면 taint/toleration + affinity를 함께 설계해야 합니다.

4) 원인 C: nodeSelector/affinity가 너무 빡세다 — 조건이 0개 노드에 매칭

증상

  • didn't match Pod's node affinity/selector

즉시 확인

# Pod가 요구하는 selector/affinity 확인
kubectl get pod <pod> -n <ns> -o yaml | sed -n '/nodeSelector:/,/tolerations:/p'

# 노드 라벨 확인
kubectl get nodes --show-labels | head -n 5

# 특정 라벨을 가진 노드만 보고 싶다면
kubectl get nodes -l "nodepool=blue" -o wide

해결책

  1. 노드 라벨을 실제로 붙이거나
kubectl label node <node> nodepool=blue
  1. Pod의 selector/affinity를 완화합니다.

예: required → preferred로 완화

affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 50
      preference:
        matchExpressions:
        - key: nodepool
          operator: In
          values: ["blue"]

5) 원인 D: 노드가 Unschedulable/NotReady — 스케줄러가 의도적으로 피함

증상

  • node(s) were unschedulable
  • 노드가 NotReady

즉시 확인

kubectl get nodes -o wide
kubectl describe node <node> | tail -n 50

SchedulingDisabled라면 cordon 상태입니다.

kubectl uncordon <node>

NotReady라면 원인은 다양하지만, EKS에서는 CNI, 디스크 압박, kubelet 문제, 보안그룹/라우팅 등이 흔합니다. 특히 노드가 Ready여도 Pod IP 고갈로 신규 Pod가 Pending/ContainerCreating에서 막히는 경우가 있어 VPC CNI 상태도 같이 확인하는 편이 안전합니다: EKS VPC CNI IP 누수로 Pod IP 고갈 해결하기

6) 원인 E: PVC 바인딩 실패(unbound immediate PersistentVolumeClaims)

증상

이벤트에 다음이 보입니다.

  • pod has unbound immediate PersistentVolumeClaims

이는 스케줄러가 **볼륨이 붙을 노드/존(Zone)**을 확정할 수 없어서 대기하는 전형적인 상황입니다.

즉시 확인

kubectl get pvc -n <ns>
kubectl describe pvc <pvc> -n <ns>

kubectl get pv
kubectl get storageclass
kubectl describe storageclass <sc>

해결책 체크리스트

  • StorageClass의 volumeBindingModeImmediate인지 WaitForFirstConsumer인지 확인
  • (클라우드 환경) PV가 생성될 AZ/Zone과 노드가 존재하는 AZ/Zone이 맞는지 확인
  • CSI 드라이버(예: EBS CSI)가 정상인지 확인

예: StorageClass를 WaitForFirstConsumer로(일반적으로 멀티 AZ에서 안전)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-wffc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3

7) 원인 F: 노드당 Pod 수 제한(Too many pods) — ENI/IP 또는 kubelet maxPods

증상

  • 이벤트에 Too many pods 또는 사실상 신규 Pod가 계속 Pending
  • 노드 리소스(cpu/mem)는 남는데도 안 올라감

즉시 확인

# 노드에 이미 몇 개의 Pod가 붙었는지
kubectl get pod -A -o wide --field-selector spec.nodeName=<node> | wc -l

# 노드의 allocatable pods 확인
kubectl describe node <node> | sed -n '/Allocatable:/,/System Info:/p'

EKS에서는 인스턴스 타입별 ENI/IP 한계와 maxPods 설정이 함께 영향을 줍니다. IP 고갈/누수까지 겹치면 더 자주 발생합니다(앞서 소개한 VPC CNI 글 참고).

해결책

  • 노드를 증설하거나
  • 더 큰 인스턴스 타입(더 많은 ENI/IP)으로 교체하거나
  • CNI 설정(프리 할당 IP, prefix delegation 등)을 조정합니다.

8) 빠른 분류를 위한 “1분 진단” 커맨드 묶음

현장에서 가장 시간을 아끼는 루틴입니다.

# 1) Pending Pod 이벤트 확인
kubectl describe pod <pod> -n <ns> | sed -n '/Events:/,$p'

# 2) 노드 상태/리소스
kubectl get nodes -o wide
kubectl top nodes

# 3) taint/label 확인
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
kubectl get nodes --show-labels | head

# 4) PVC가 있으면 바인딩 확인
kubectl get pvc -n <ns>

# 5) 스케줄러 로그(옵션: 관리형 환경이면 접근 제한)
# self-managed control-plane인 경우에만
# kubectl -n kube-system logs deploy/kube-scheduler

9) “해결은 됐는데 또 터진다”를 막는 설계 포인트

9.1 requests/limits를 관측 기반으로 관리

  • 초기에는 conservative하게 시작하되, 메트릭 기반으로 조정
  • requests는 스케줄의 문턱, limits는 런타임 상한

9.2 노드풀/워크로드 분리를 명확히

  • 시스템/배치/GPU/고우선순위 서비스는 taint/toleration로 분리
  • affinity는 가능하면 preferred를 먼저 고려

9.3 오토스케일러가 “노드를 늘릴 수 있는 조건”인지 점검

  • Karpenter/Cluster Autoscaler가 있어도
    • PVC/Zone 제약
    • taint/selector 제약
    • IAM/네트워크 문제 로 인해 노드가 늘지 않거나 Ready가 안 될 수 있습니다.

10) 마무리: Pending은 앱 문제가 아니라 “배치 조건” 문제다

Pod Pending: 0/노드 스케줄 불가는 대부분 아래 6가지로 귀결됩니다.

  1. requests 과다로 인한 CPU/메모리 부족
  2. taint/toleration 불일치
  3. nodeSelector/affinity 불일치
  4. 노드 NotReady/cordon
  5. PVC 바인딩/Zone 문제
  6. 노드당 Pod 수 제한(IP/ENI, maxPods)

이벤트 한 줄을 정확히 읽고, 위 체크리스트대로 좁혀가면 “왜 안 뜨지?”에서 “어디를 고치면 되는지”로 바로 넘어갈 수 있습니다.

추가로, Pod가 Running인데도 트래픽이 0인 네트워크 계층 문제는 별도의 진단 축이 필요합니다: EKS Pod는 뜨는데 트래픽 0 - NetPol·SG·CNI 10분 진단