← 아티클 목록

쿠버네티스 CPU 스로틀링 진단 — limits와 CFS quota의 함정

2027-11-15#kubernetes#성능#트러블슈팅

CPU 사용률은 40%인데 응답이 느리고 p99 지연이 튄다 — 모니터링만 보면 여유로워 보이는데 실제로는 느린 이 현상의 흔한 원인이 CPU 스로틀링입니다. resources.limits.cpu를 걸어 두면 쿠버네티스는 리눅스 cgroup의 CFS quota로 컨테이너를 강제로 멈췄다 풀었다 합니다. 이 "멈춤"이 지연으로 나타납니다.

CFS quota가 컨테이너를 조이는 원리

CPU limit은 코어 개수가 아니라 시간 할당량으로 동작합니다. 리눅스 CFS 스케줄러는 100ms(cfs_period_us)마다 quota를 새로 줍니다. limits.cpu: 500m이면 한 주기당 50ms만 쓸 수 있고, 50ms를 다 쓰면 멀티스레드 앱이 일을 더 하려 해도 다음 주기까지 강제로 잠깐 멈춥니다(throttle).

문제는 평균 사용률입니다. 100ms 중 앞 50ms에 폭발적으로 일하고 나머지 50ms는 throttled로 놀면, 평균 CPU는 50%로 보이지만 실제로는 절반의 시간 동안 멈춰 있던 겁니다. 그래서 사용률은 낮은데 느린 모순이 생깁니다.

1단계 — throttling이 실제로 일어나는지 확인

추측 말고 컨테이너의 cgroup 통계를 직접 봅니다.

Kubernetes
# Pod 안에서 cgroup v2 CPU 통계 확인
kubectl exec <pod> -- cat /sys/fs/cgroup/cpu.stat
OUTPUT
nr_periods 23051
nr_throttled 18420
throttled_usec 92140000

nr_throttled / nr_periods가 throttling 비율입니다. 위 예시는 80%로, 거의 매 주기마다 멈춘 상태입니다. 10%만 넘어도 의심해야 합니다.

2단계 — 원인별 판단

증상원인대응
nr_throttled 비율이 높고 limit이 1코어 미만quota가 너무 빡빡함limits.cpu 상향 또는 limit 제거 검토
멀티스레드인데 limits.cpu가 낮음스레드들이 quota를 순식간에 소진limit을 코어 수에 맞게
requests만 있고 limits 없음인데 느림CPU 스로틀링 아님노드 경합·requests 확인
GC·JIT 구간에서만 튐기동 시 순간 폭발cpu.cfs_period보다 워밍업 고려

3단계 — 해결 방향

CPU는 압축 가능한 자원입니다. 메모리와 달리 limit이 없어도 노드가 죽지 않고 그냥 느려질 뿐이라, CPU limit을 아예 빼고 requests로만 스케줄링하는 운영 방식도 널리 쓰입니다. limit을 유지해야 한다면 실제 피크에 맞춰 넉넉히 잡습니다. requests는 보장량, limits는 상한이라는 점을 구분하세요.

YAML
resources:
  requests:
    cpu: "500m"      # 스케줄링 보장량
  # limits.cpu 생략 — throttling 회피
  # 또는 피크에 맞춰 넉넉히
  limits:
    cpu: "2"

체크리스트

Kubernetes
kubectl exec <pod> -- cat /sys/fs/cgroup/cpu.stat   # nr_throttled 비율
kubectl describe pod <pod> | grep -A4 Limits          # 걸린 limit 확인
kubectl top pod <pod>                                 # 실제 사용량 대조

nr_throttled 비율이 사용률보다 신뢰할 수 있는 신호입니다.


CPU와 메모리 limit이 cgroup으로 어떻게 강제되는지 직접 Pod를 조여 보며 확인하는 실습은 쿠버네티스 트랙에서 해볼 수 있습니다 — 회원가입 없이 무료로.