- Published on
EKS Pod Pending - Insufficient cpu·taint 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스처럼 보이는 쿠버네티스도 결국은 노드 자원과 스케줄링 규칙 위에서 움직입니다. EKS에서 배포는 성공했는데 Pod가 계속 Pending 상태로 남아 있다면, 대부분은 스케줄러가 “올릴 노드가 없다”고 판단한 상황입니다.
특히 운영에서 자주 마주치는 케이스가 두 가지입니다.
Insufficient cpu: 노드들에 남은 CPU가 없거나, Pod의requests.cpu가 너무 커서 어떤 노드에도 들어가지 못함taint관련: 노드에taint가 걸려 있어 Pod가 허용(toleration)되지 않음
이 글에서는 EKS 기준으로 원인 확인 명령어 → 이벤트 해석 → 해결책 선택(자원 조정/노드 확장/taint 해제 또는 toleration 추가) 흐름으로 정리합니다.
관련해서 스케일링이 기대대로 동작하지 않을 때의 배경 지식은 아래 글도 함께 보면 도움이 됩니다.
1) Pending 원인: 스케줄러 이벤트부터 본다
가장 먼저 확인할 것은 Pod 이벤트입니다. describe 한 번이면 Insufficient cpu인지 taint인지 대부분 드러납니다.
kubectl get pod -n <namespace>
kubectl describe pod <pod-name> -n <namespace>
Events 섹션에서 아래 같은 문구를 찾습니다.
0/6 nodes are available: 6 Insufficient cpu.0/6 nodes are available: 2 node(s) had taint {dedicated: gpu}, that the pod didn't tolerate.0/6 nodes are available: 6 node(s) didn't match Pod's node affinity/selector.
여기서 핵심은 “스케줄러가 왜 배치에 실패했는지”를 문자열로 알려준다는 점입니다.
추가로 클러스터 전체 이벤트도 보면, 노드 추가/제거(오토스케일)와 맞물려 원인을 더 빨리 좁힐 수 있습니다.
kubectl get events -n <namespace> --sort-by=.lastTimestamp
2) Insufficient cpu의 의미: 실제 사용량이 아니라 requests가 기준
Insufficient cpu는 노드의 CPU 사용률이 높다는 뜻이라기보다, 스케줄링 시점에 남아있는 allocatable CPU 대비 Pod의 requests.cpu를 충족할 수 없다는 의미입니다.
즉, 노드에 CPU가 “남아 보이는데도” Pending이 발생할 수 있습니다.
- 이미 배치된 다른 Pod들의
requests.cpu합이 노드 allocatable을 꽉 채움 - 새 Pod의
requests.cpu가 너무 큼 DaemonSet(CNI, kube-proxy 등) 오버헤드로 실제 allocatable이 줄어듦
2-1) 노드 allocatable과 현재 requests 합을 빠르게 보기
노드의 allocatable과 현재 requests/limits 합계는 아래로 확인합니다.
kubectl describe node <node-name>
출력 중 Allocatable과 Allocated resources 섹션을 봅니다.
Allocatable은 “스케줄러가 Pod에 줄 수 있는 최대치”Allocated resources는 “현재 Pod들이 선언한 requests/limits의 합”
EKS에서는 인스턴스 타입이 작거나(예: t3.small) 시스템 데몬 오버헤드가 상대적으로 커서, 생각보다 빨리 Insufficient cpu가 납니다.
2-2) requests가 과한지 확인하는 체크리스트
Pod 또는 Deployment YAML에서 아래를 확인합니다.
resources:
requests:
cpu: "1000m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "512Mi"
requests.cpu가 실제 평균 사용량 대비 과하게 잡혀 있지 않은가limits를requests와 동일하게 고정해 CPU 버스트가 필요한 워크로드를 막고 있지 않은가- 사이드카(Envoy, Fluent Bit 등)도 requests를 잡아먹고 있지 않은가
운영에서는 “안전빵”으로 requests를 크게 잡았다가 노드 스케줄링이 막히는 경우가 매우 흔합니다.
3) Insufficient cpu 해결 옵션 4가지
옵션 A) Pod의 requests.cpu를 현실화한다
가장 비용 효율적인 해법은 requests를 실제 사용량에 맞게 줄이는 것입니다. 예를 들어 평균 50~100m 수준인데 1000m로 잡았다면, 스케줄러 관점에서 이 Pod는 “항상 1 vCPU가 필요한 큰 작업”이 됩니다.
resources:
requests:
cpu: "150m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
- CPU는
limits를 높여 버스트를 허용하고 requests는 최소 보장치로 현실화
단, CPU throttling이 민감한 서비스라면 limits를 너무 낮추지 말고, 부하 패턴에 맞춰 단계적으로 조정합니다.
옵션 B) 노드를 늘린다: Cluster Autoscaler 또는 Karpenter
Pod가 정상적인 requests를 가지고 있고, 실제로 용량이 부족하다면 노드를 늘려야 합니다.
- Managed Node Group을 쓰면 desired size를 올리거나
- Cluster Autoscaler 또는 Karpenter로 자동 증설을 붙입니다.
Cluster Autoscaler가 있는데도 안 늘어난다면 다음을 의심합니다.
- 최대 노드 수(max size) 제한
- 노드 그룹 라벨/taint 때문에 해당 Pod를 수용할 노드 그룹이 없음
- 스케줄 제약(affinity, topology spread)이 너무 빡빡함
옵션 C) 큰 Pod를 쪼개거나(수평 분할) 롤링 전략을 조정한다
간혹 단일 Pod가 너무 커서 어떤 노드에도 안 들어가는 경우가 있습니다. 예: requests.cpu가 4000m인데 노드가 2 vCPU짜리.
이때는 다음 중 하나가 현실적입니다.
- 워커를 여러 Pod로 분할(큐 컨슈머, 배치 워커 등)
- 인스턴스 타입을 키움
- 배포 전략에서 surge를 줄여 순간적으로 필요한 Pod 수를 제한
Deployment에서 롤링 업데이트 중 maxSurge 때문에 순간적으로 Pod 수가 증가하면서 Insufficient cpu가 터지기도 합니다.
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
옵션 D) 우선순위(priority)와 preemption을 활용한다
중요한 서비스가 덜 중요한 워크로드 때문에 Pending이라면 PriorityClass로 선점(preemption)을 고려할 수 있습니다.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical-service
value: 100000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Critical workload priority"
그리고 Pod에 적용:
spec:
priorityClassName: critical-service
이 방식은 운영 정책(무엇을 죽여도 되는가)이 명확할 때만 권장됩니다.
4) taint로 인한 Pending: 노드가 “거부”하는 상태
이벤트에서 had taint ... that the pod didn't tolerate가 보이면, 노드에 taint가 있고 Pod가 toleration을 갖고 있지 않다는 뜻입니다.
4-1) 노드 taint 확인
kubectl describe node <node-name> | grep -i taint -n
# 또는
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.taints}{"\n"}{end}'
자주 보는 taint 예시는 아래와 같습니다.
dedicated=system:NoSchedule(시스템 전용)dedicated=gpu:NoSchedule(GPU 전용)node.kubernetes.io/not-ready:NoSchedule(노드 상태 기반)
4-2) 해결 1: Pod에 toleration 추가
해당 노드에 “올라가도 된다”는 의사를 Pod에 명시합니다.
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
operator는 보통 Equal 또는 Exists를 씁니다.
Equal: 특정 key/value만 허용Exists: key만 맞으면 value 상관없이 허용(범위가 넓어 위험할 수 있음)
4-3) 해결 2: taint를 제거한다(정책적으로 허용될 때만)
노드의 taint 자체가 불필요하거나 잘못 설정된 경우 제거합니다.
kubectl taint nodes <node-name> dedicated=gpu:NoSchedule-
끝의 -가 “삭제”를 의미합니다.
운영에서는 “임시로 풀자”가 습관이 되면 격리가 무너집니다. taint 제거는 반드시 노드의 역할(전용 노드인지)을 합의한 뒤 진행하는 게 안전합니다.
4-4) 해결 3: nodeSelector/affinity와 충돌하는지 함께 점검
taint 문제처럼 보이지만 실제로는 nodeSelector나 affinity가 더 큰 제약이 되어 스케줄링이 막히는 경우도 많습니다.
spec:
nodeSelector:
workload: "batch"
이 라벨을 가진 노드가 없으면 Pending이 납니다. 노드 라벨은 아래로 확인합니다.
kubectl get nodes --show-labels
5) 실전 트러블슈팅 시나리오: 이벤트를 기준으로 분기
운영에서 빠르게 처리하려면 다음 순서가 효율적입니다.
kubectl describe pod로 이벤트 확인- 이벤트가
Insufficient cpu면- 노드 allocatable vs allocated 확인
- Pod
requests.cpu현실화 또는 노드 증설
- 이벤트가
taint면- 노드 taint 확인
- toleration 추가 또는 taint 제거(정책 확인)
- 그래도 안 되면
nodeSelector/affinity/topology spread 제약 확인- 롤링 업데이트 surge로 순간 용량이 튀는지 확인
EKS에서 다른 유형의 배포 장애(이미지 풀 실패 등)와 구분하는 것도 중요합니다. Pending은 스케줄링 문제일 가능성이 높지만, ContainerCreating이나 ImagePullBackOff로 넘어가면 원인이 달라집니다.
6) 자주 하는 실수와 예방 팁
6-1) requests를 “최대치”로 잡는 실수
스케줄러는 최대치가 아니라 requests를 기준으로 자리 배치를 합니다. 최대 사용량을 보장하고 싶다면 requests를 올리는 게 맞지만, 대부분 서비스는 평균이 낮고 피크만 높습니다. 이때는 requests는 낮게, limits는 높게(또는 CPU limit 미설정)로 두는 전략이 더 적합한 경우가 많습니다.
6-2) 전용 노드(taint) 운영에서 toleration을 무분별하게 추가
toleration을 아무 Pod에나 넣으면 전용 노드의 의미가 사라집니다. 전용 노드는 보통 다음 목적이 있습니다.
- 비용이 큰 인스턴스(GPU, 고메모리)를 특정 워크로드만 사용
- 시스템/인프라 컴포넌트 격리
- 규제/보안/성능 이유로 워크로드 분리
따라서 toleration은 “해당 워크로드가 그 전용 노드에 올라갈 자격이 있는가”를 기준으로 최소화합니다.
6-3) 오토스케일러가 있는데도 Pending이 오래 지속됨
오토스케일러가 있어도 다음 상황이면 노드가 안 늘 수 있습니다.
- Pod가 특정 라벨/taint 조합을 요구하는데, 그 조건을 만족하는 노드 그룹이 없음
- 최대 노드 수 제한
- 스팟 부족, 서브넷 IP 부족 등 인프라 제약
EKS에서 IAM 또는 STS 토큰 문제처럼 “권한/인증” 이슈도 종종 함께 터집니다. 특히 노드/파드가 AWS API를 호출하는 구성(IRSA)에서 장애가 겹치면 원인 파악이 어려워집니다.
7) 체크리스트: 현장에서 바로 쓰는 명령 모음
# 1) Pending Pod 이벤트 확인
kubectl describe pod <pod-name> -n <namespace>
# 2) 네임스페이스 이벤트 타임라인
kubectl get events -n <namespace> --sort-by=.lastTimestamp
# 3) 노드 리소스(allocatable/allocated) 확인
kubectl describe node <node-name>
# 4) 노드 taint 확인
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.taints}{"\n"}{end}'
# 5) 노드 라벨 확인
kubectl get nodes --show-labels
# 6) taint 제거(필요 시)
kubectl taint nodes <node-name> dedicated=gpu:NoSchedule-
마무리
EKS에서 Pod Pending은 대부분 “스케줄링 불가”의 다른 표현입니다. Insufficient cpu는 실제 사용량이 아니라 requests 기반이라는 점을 기억하면, 불필요한 증설 없이도 해결되는 경우가 많습니다. 반대로 taint는 의도된 격리 정책일 수 있으니, 무작정 taint를 풀기보다 toleration을 최소 범위로 적용하는 쪽이 운영 안정성에 유리합니다.
다음에 같은 문제가 재발하지 않게 하려면, 배포 파이프라인에서 requests/limits 표준을 정하고(서비스 유형별 가이드), 전용 노드(taint) 정책을 문서화해 팀 합의를 만들어 두는 것이 가장 확실한 예방책입니다.