Published on

EKS Pod Pending(Insufficient cpu) 원인과 해결

Authors

서버리스처럼 보이지만, Kubernetes 스케줄링은 매우 ‘물리적’입니다. EKS에서 Pod가 Pending으로 오래 머물고 0/.. nodes are available: Insufficient cpu 이벤트가 반복되면, 대부분은 단순히 “CPU가 부족하다”가 아니라 **요청(request)·제한(limit)·노드 용량·스케줄링 제약(affinity/taint/PDB/priority)**이 맞물린 결과입니다. 이 글에서는 Insufficient cpu를 스케줄러 관점에서 해부하고, 운영에서 바로 쓰는 해결 순서를 제시합니다.

> 같은 EKS 트러블슈팅 맥락에서 네트워크/런타임 이슈도 함께 점검하면 좋습니다: EKS CrashLoopBackOff인데 로그가 0줄? 원인 8가지, EKS에서 NLB 타겟 Unhealthy - 헬스체크·Pod·SG

증상 확인: 정말 CPU가 ‘부족’한가?

먼저 “노드의 실제 사용률”이 아니라 Pod의 request 합 기준으로 스케줄링이 막힌다는 점을 확인해야 합니다. 노드에 CPU가 남아 보여도(top) request가 과도하면 스케줄러는 배치하지 않습니다.

1) Pending Pod 이벤트 확인

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

Events에 보통 아래 같은 문구가 보입니다.

  • 0/6 nodes are available: 6 Insufficient cpu.
  • 또는 Insufficient cpu, Insufficient memory 혼합
  • 또는 node(s) had taint {..} 같이 다른 제약과 동시 발생

2) 클러스터에서 어떤 리소스가 병목인지 빠르게 보기

kubectl get nodes
kubectl describe node <node>

AllocatableAllocated resources(requests/limits 합계)를 비교하세요.

추가로 Metrics Server가 있다면:

kubectl top nodes
kubectl top pods -A --sort-by=cpu

단, top은 “실사용”이고 스케줄링은 “request” 기반이므로 둘이 다를 수 있습니다.

원인 1: Pod CPU request가 과도하거나 잘못 설정됨

가장 흔한 케이스입니다. 특히 HPA/VPA를 도입했거나, 템플릿에서 과거 값이 그대로 남아 request가 과하게 잡혀 있는 경우가 많습니다.

체크 포인트

  • resources.requests.cpu가 실제 필요량 대비 과도
  • limit만 있고 request가 없는 경우(기본적으로 request=limit로 잡히는 정책이 있거나, LimitRange가 강제하는 경우)
  • 사이드카(Envoy, Fluent Bit 등) request 합산으로 커짐

예시: request/limit 합리화

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: api
        image: example.com/api:1.0.0
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "1000m"
            memory: "512Mi"

운영 팁:

  • 초기에는 request를 보수적으로 낮게, limit은 피크 보호용으로 설정
  • CPU는 throttling이 있어도 죽지 않는 경우가 많아(워크로드 특성에 따라) request를 낮추는 효과가 큼
  • 반대로 latency 민감 서비스라면 limit을 너무 낮게 잡으면 throttling으로 지연이 커질 수 있음

원인 2: 노드의 Allocatable CPU가 생각보다 작음(오버헤드)

EC2 인스턴스 vCPU가 4라고 해서 쿠버네티스가 Pod에 4 vCPU를 다 주지 않습니다.

  • kubelet/system-reserved, kube-reserved
  • DaemonSet(aws-node, kube-proxy, coredns, 로깅/모니터링 에이전트)
  • ENI/IP 제약과 함께 노드가 사실상 ‘작게’ 느껴짐

확인 방법

kubectl describe node <node> | sed -n '/Allocatable/,+20p'

여기서 cpu allocatable이 기대보다 작다면, 노드 타입 자체를 키우거나(스케일 업), 노드 수를 늘리거나(스케일 아웃), DaemonSet 리소스를 최적화해야 합니다.

원인 3: Cluster Autoscaler/Karpenter가 스케일 아웃을 못함

Pending인데 노드가 늘지 않는다면, 오토스케일러가 작동하지 않거나 “늘려도 배치할 수 없는 Pod”로 판단했을 수 있습니다.

Cluster Autoscaler 점검

kubectl -n kube-system get deploy | grep -i autoscaler
kubectl -n kube-system logs deploy/cluster-autoscaler --tail=200

자주 보는 원인:

  • 노드그룹 maxSize가 이미 꽉 참
  • ASG/노드그룹이 특정 AZ에만 묶여 있고 해당 AZ 용량 부족
  • Pod에 nodeSelector/affinity가 걸려 있는데 해당 라벨을 가진 노드그룹이 없음
  • 스케일 아웃 권한(IAM) 문제

Karpenter 점검(사용 시)

kubectl -n karpenter logs deploy/karpenter --tail=200
kubectl get nodepool,ec2nodeclass -A

특정 인스턴스 패밀리만 허용해 두고 해당 타입이 스팟/온디맨드에서 품절이면, 결과적으로 Insufficient cpu가 지속됩니다.

원인 4: 스케줄링 제약이 CPU 부족처럼 보이게 만듦

이벤트 메시지에 Insufficient cpu만 찍히는 경우도 있지만, 실제로는 아래 제약이 더 큰 문제인 경우가 있습니다.

  • nodeSelector/nodeAffinity로 특정 노드에만 배치 가능
  • taints/tolerations 불일치
  • topologySpreadConstraints가 너무 빡빡함
  • podAntiAffinity로 인해 같은 노드에 못 올라감

한 번에 보는 방법

kubectl describe pod <pod> -n <ns>
# Node-Selectors, Tolerations, Affinity, Topology Spread 확인

그리고 스케줄러의 실제 판단을 보려면:

kubectl -n kube-system get events --sort-by=.lastTimestamp | tail -n 50

해결 전략: “지금 당장” vs “재발 방지”

아래는 운영에서 효과가 큰 순서입니다.

1) 즉시 조치(가장 빠름): request 낮추거나 replicas 줄이기

긴급 장애 상황에서는 스케줄링이 먼저입니다.

  • CPU request를 낮춰 Pending을 해소
  • 일시적으로 replicas를 줄여 핵심 트래픽만 처리
kubectl -n <ns> scale deploy/<name> --replicas=1

또는 values(Helm)로 request를 조정 후 재배포합니다.

2) 노드 스케일 아웃: 노드그룹 maxSize/서브넷/AZ 확인

Cluster Autoscaler를 쓰는 경우:

  • Managed Node Group의 maxSize 상향
  • 서브넷 IP 여유(특히 VPC CNI 사용 시) 확인
  • AZ 분산이 한쪽으로 쏠리지 않게 구성

노드가 늘어나도 여전히 Pending이면, “늘어난 노드가 Pod 요구조건을 만족하는지(라벨/taint/아키텍처)”를 확인해야 합니다.

3) 노드 스케일 업: 인스턴스 타입 재설계

작은 노드를 많이 쓰는 전략이 항상 이득은 아닙니다.

  • DaemonSet 오버헤드가 큰 클러스터는 노드가 작을수록 손해
  • CPU request가 큰 워크로드(예: 2 vCPU 이상)는 작은 노드에 조각나서 못 들어갈 수 있음(단편화)

예:

  • t3.medium(2 vCPU) 다수 → m6i.large(2 vCPU)로 바꿔도 큰 차이는 없을 수 있음
  • 오버헤드 고려 시 m6i.xlarge(4 vCPU) 이상으로 올리면 효율이 좋아지는 경우가 많음

4) Pod 우선순위/선점(Preemption) 활용

중요한 Pod가 Pending이면, 덜 중요한 Pod를 밀어내는 전략이 필요할 수 있습니다.

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: critical
value: 100000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Critical workloads"

그리고 Deployment에:

spec:
  template:
    spec:
      priorityClassName: critical

주의: 선점은 클러스터 전체 안정성에 영향을 주므로 “무조건” 답은 아닙니다.

5) HPA/VPA/LimitRange 정책 재점검(재발 방지 핵심)

HPA가 replicas를 늘리는데 노드가 못 따라가는 경우

  • HPA target CPU utilization이 너무 낮아 과도하게 스케일 아웃
  • Cluster Autoscaler 반응 속도(노드 프로비저닝 수 분)와 HPA 반응 속도(수십 초)가 불일치

해결:

  • HPA 정책 완화(최대 replicas 제한)
  • scaleUp/scaleDown behavior 튜닝

VPA가 request를 과하게 올리는 경우

VPA를 쓰면 request가 커져 Pending을 유발할 수 있습니다. 특히 Auto 모드에서 급격한 상향이 발생하면, 기존 노드로는 스케줄 불가가 됩니다.

LimitRange가 request를 강제하는 경우

네임스페이스에 LimitRange가 있으면 “명시하지 않은 request”가 자동으로 커질 수 있습니다.

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

6) 리소스 단편화(Fragmentation) 줄이기

클러스터에 여유 CPU 총량이 있어도, 각 노드에 남은 CPU 조각이 request보다 작으면 Pending이 납니다.

대응:

  • 노드 타입을 더 큰 것으로 섞기(혼합 인스턴스)
  • Pod request를 조금 낮춰 “들어갈 틈” 만들기
  • Karpenter 사용 시 다양한 인스턴스 패밀리/사이즈 허용

실전 디버깅 체크리스트(명령어 중심)

1) 어떤 조건 때문에 못 올라가는지

kubectl describe pod <pod> -n <ns>

2) 노드 allocatable과 requests 합

kubectl describe node <node>

3) 네임스페이스 정책(LimitRange/ResourceQuota)

kubectl get resourcequota,limitrange -n <ns>
kubectl describe resourcequota -n <ns>

4) 오토스케일러 로그

kubectl -n kube-system logs deploy/cluster-autoscaler --tail=300
# 또는
kubectl -n karpenter logs deploy/karpenter --tail=300

자주 하는 실수 5가지

  1. 노드 CPU 사용률이 낮으니 여유가 있다고 착각: 스케줄링은 request 기준입니다.
  2. 사이드카 request를 빼먹음: Envoy/로그 에이전트가 합산되어 큰 Pod가 됩니다.
  3. maxSize를 안 올림: Autoscaler는 있어도 늘릴 수 없으면 Pending이 지속됩니다.
  4. nodeSelector/taint로 스스로 가둠: 특정 노드군에만 배치되도록 해두고 CPU 부족을 겪습니다.
  5. 너무 작은 노드로만 구성: 오버헤드와 단편화로 실제 가용 CPU가 급감합니다.

마무리: Insufficient cpu는 “request 설계” 문제로 귀결된다

EKS에서 Pod Pending: Insufficient cpu는 단순 증상이고, 근본 원인은 보통 아래 중 하나로 정리됩니다.

  • request가 현실 대비 과도하거나 정책(LimitRange/VPA)로 부풀려짐
  • 노드 allocatable이 오버헤드로 작아짐(특히 DaemonSet)
  • 오토스케일러가 확장하지 못함(maxSize/AZ/제약/권한)
  • 스케줄링 제약으로 특정 노드에만 몰림

운영 관점에서 가장 효과적인 접근은 (1) Pending Pod 이벤트로 제약을 정확히 읽고 → (2) request/replicas를 즉시 조정해 서비스 복구 → (3) 노드그룹/오토스케일러/정책을 재설계해 재발을 막는 것입니다.