/인프라/Pod Pending FailedScheduling 0/3 nodes 30초 진단·복구 런북
인프라FailedSchedulingPod Pending

Pod Pending FailedScheduling 0/3 nodes 30초 진단·복구 런북

Pod가 FailedScheduling 0/3 nodes(insufficient cpu/memory·taint·node selector)로 Pending에 멈췄나요? Events 한 줄로 원인을 분기하고 requests 축소·toleration·노드 증설을 결정 트리로 즉시 복구하는 실전 런북입니다.

Pod Pending FailedScheduling 0/3 nodes 30초 진단·복구 런북

Pod Pending FailedScheduling 0/3 nodes 30초 진단·복구 런북

K8s_Troubleshooting_Guide 4편. 배포했는데 STATUS가 계속 Pending이고, kubectl get events에는 FailedScheduling 0/3 nodes are available만 찍힌다. 이 글은 그 한 줄을 보고 30초 안에 원인을 가르고 복붙으로 복구하는 런북입니다.

"Running이 안 되고 계속 Pending이에요"

Pod가 Pending이라는 건 스케줄러가 이 Pod를 받아줄 노드를 못 찾았다는 뜻입니다. 컨테이너가 죽은 게 아니라 아예 노드에 배치조차 안 된 상태죠. 스케줄러가 노드를 거부하는 시그널은 크게 4가지입니다.

  1. 리소스 부족insufficient cpu / insufficient memory
  2. taint 거부node(s) had untolerated taint
  3. 라벨/affinity 불일치didn't match Pod's node affinity/selector
  4. 볼륨 zone 불일치had volume node affinity conflict

이 글은 1~3번(리소스/노드 매칭 거부)에 집중합니다. PVC 자체가 Pending인 스토리지 바인딩 문제는 별도 편에서 다룹니다. 핵심은 단 하나, "정말 자리가 없는가(리소스) vs 매칭이 안 되는가(taint/라벨)" 를 가르는 겁니다.

30초 진단표: Events 한 줄로 원인 분기

가장 먼저 칠 명령어는 이것 하나입니다.

Bash
kubectl describe pod <pod> | grep -A20 Events

그 한 줄을 아래 표에 대입하세요.

Events 메시지원인즉시 확인 명령어복구 액션
insufficient cpu / insufficient memory노드 가용 리소스 < Pod requestskubectl describe nodes | grep -A5 "Allocated resources"requests 축소 또는 노드 증설
node(s) had untolerated taint {key: value}노드에 taint, Pod에 toleration 없음kubectl describe node <node> | grep TaintsPod에 toleration 추가
didn't match Pod's node affinity/selectornodeSelector/affinity 라벨 불일치kubectl get nodes --show-labels노드 라벨링 또는 매니페스트 수정
had volume node affinity conflictPV가 묶인 zone에 가용 노드 없음kubectl get pv <pv> -o yaml | grep -A5 nodeAffinityPV zone과 노드 zone 정렬

0/3 nodes are available: 2 Insufficient cpu, 1 node(s) had untolerated taint 처럼 콤마로 사유가 여러 개 찍히기도 합니다. 숫자를 더하면 전체 노드 수(여기선 3)가 됩니다. 즉 "2대는 CPU 부족, 1대는 taint"라는 뜻이죠. 읽는 법은 FAQ에서 다룹니다.

가용 리소스·노드 상태 확인

리소스 부족 메시지가 나왔다면, 진짜 부족한지 먼저 확인합니다. requests 합계 기준으로 거부됐을 뿐 실제 사용량은 여유로운 경우가 흔하기 때문입니다.

Bash
# 노드별 requests/limits 할당 현황 (스케줄러가 보는 값)
kubectl describe nodes | grep -A5 "Allocated resources"

# 실제 사용량 (metrics-server 필요)
kubectl top nodes

# 노드 라벨 한눈에 (nodeSelector 디버깅)
kubectl get nodes --show-labels

# 특정 노드 taint 조회
kubectl describe node <node> | grep Taints

Allocated resources의 CPU Requests가 95%인데 kubectl top nodes의 실사용은 30%라면 — 자리가 없는 게 아니라 requests가 과대 설정된 것입니다. 이땐 노드 증설이 아니라 requests 축소가 정답입니다.

원인별 복구 명령어

① requests 과대 설정 축소

가장 흔한 케이스. 실사용 대비 requests가 너무 커서 스케줄링이 막힌 경우입니다.

Bash
kubectl patch deployment <name> --type='json' -p='[
  {"op":"replace","path":"/spec/template/spec/containers/0/resources/requests/cpu","value":"250m"},
  {"op":"replace","path":"/spec/template/spec/containers/0/resources/requests/memory","value":"256Mi"}
]'

실무 팁: 저는 장애 현장에서 일단 requests를 실사용 + 30% 수준으로 낮춰 Pod를 띄운 뒤, 안정화되면 kubectl top pods로 며칠치 데이터를 보고 정식 값을 정합니다. "일단 Running" → "그 다음 튜닝" 순서가 복구 속도를 가릅니다.

② taint에 toleration 추가

GPU/스팟 노드는 보통 taint로 보호됩니다. 그 노드에 의도적으로 띄우려면 toleration을 추가합니다.

YAML
spec:
  template:
    spec:
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"

③ nodeSelector/affinity 라벨 교정

disktype=ssd를 요구하는데 노드에 그 라벨이 없는 경우. 노드에 라벨을 붙이거나,

Bash
kubectl label nodes <node> disktype=ssd

매니페스트의 affinity를 실제 라벨에 맞춰 고칩니다.

YAML
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: disktype
          operator: In
          values: ["ssd"]

④ 노드 증설 / Autoscaler 확인

requests 축소가 불가능하고(이미 적정값) 정말 자리가 없으면 증설입니다. Cluster Autoscaler/Karpenter가 있다면 동작 여부를 확인하세요.

Bash
kubectl -n kube-system logs deploy/cluster-autoscaler | tail -30

결정 트리: 증설 vs 축소 vs toleration

CODE
FailedScheduling 발생
   │
   ├─ 리소스가 실제 부족? (top nodes로 확인)
   │     ├─ Y → requests 축소 가능?
   │     │        ├─ Y → requests 축소 (①)
   │     │        └─ N → 노드 증설 / Autoscaler (④)
   │     └─ N (매칭 문제)
   │            ├─ taint 거부? → toleration 추가 (②)
   │            └─ nodeSelector 불일치? → 라벨 교정 (③)

재발 방지

  • LimitRange + 기본 requests: 네임스페이스에 기본 requests를 강제해 "requests 미설정 → 과대/과소" 양극단을 막습니다.
  • 2026 트렌드: K8s 1.30+ 환경에선 In-place Pod resize와 Karpenter 기반 자동 증설이 보편화됐습니다. 다만 자동 증설도 결국 requests를 신뢰값으로 잡아야 의미가 있으니, requests 관리가 모든 것의 출발점입니다.

자주 묻는 질문 (FAQ)

Q. requests를 0으로 두면 스케줄링이 무조건 되나요? A. 됩니다. 하지만 위험합니다. requests가 0이면 스케줄러가 리소스 보장 없이 노드에 욱여넣어 노드 과밀(over-commit)을 유발하고, 메모리 압박 시 OOMKilled나 노드 NotReady로 번집니다. 임시 복구엔 쓰되 정식 값은 실사용 기반으로 잡으세요.

Q. control-plane 노드에 Pod를 띄워도 되나요? A. control-plane에는 보통 node-role.kubernetes.io/control-plane:NoSchedule taint가 있습니다. 단일 노드 테스트 클러스터라면 toleration을 추가해 띄울 수 있지만, 운영 클러스터에서는 권장하지 않습니다. 워크로드용 노드를 따로 두세요.

Q. 0/3 nodes: 2 Insufficient cpu, 1 had taint처럼 사유가 섞이면? A. 숫자 합(2+1=3)이 전체 노드 수입니다. 노드마다 거부 사유가 다른 것이니, 가장 큰 비중부터 처리하세요. 위 예시는 CPU 부족이 2대로 주된 원인이므로 requests 축소를 먼저 시도하고, 그래도 안 되면 taint 노드에 toleration을 추가하는 순서로 접근합니다.

Q. Cluster Autoscaler/Karpenter가 있는데도 왜 Pending인가요? A. ① 노드 그룹 max 한도에 도달, ② Pod requests가 어떤 인스턴스 타입에도 안 맞음(예: 8 CPU 요청인데 4 CPU 노드만 프로비저닝), ③ 스팟 용량 부족, ④ Autoscaler가 nodeSelector/taint를 만족하는 노드 그룹을 못 찾음 등이 원인입니다. Autoscaler 로그를 먼저 보면 no node group can be scaled up 같은 구체 사유가 나옵니다.


다음 편 예고 — 노드가 NotReady로 빠질 때(NodeNotReady·kubelet) 진단 런북. Pending이 아니라 노드 자체가 죽는 상황을 다룹니다.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

초안 · AI (Content Reviewer)·검토 · Nodelog 편집자·발행 ·
관련 공식 문서Kubernetes 공식 문서

댓글

첫 번째 댓글을 남겨보세요.