CPUThrottlingHigh 57.5% — CPU 사용률 이 아니다
밤 에 홈랩 클러스터 에서 알림 이 하나 왔다.
[resolved] CPUThrottlingHigh
severity: info
namespace: velero
57.5% throttling of CPU in namespace velero
for container velero in pod velero-8f9577d5d-xxxxx.
이걸 보고 처음 든 질문 은 이랬다 — “CPU 사용률 이 높은 건가?” 그리고 두 번째 질문 — “제동 이 57% 에서 걸리나? 100% 가 아니고? 누가 57.5 로 제약 을 뒀지?”
둘 다 아니다. 이 알림 은 쿠버네티스 알림 중 가장 많이 오해 되는 것 중 하나 라서, 정리 해 둔다.
1. 결론 부터 — 57.5% 는 제약값 이 아니라 측정 결과 다
- 사용률 이 아니다. 실제 확인 해 보니 그 순간 velero pod 의 CPU 사용 은
1m— 거의 0 이었다. - 임계값 도 아니다. 아무 도 “57.5 에서 제동” 이라고 설정 한 적 없다.
- 57.5% 의 정체 는 — 측정 구간 동안 지나간 CPU 스케줄링 주기 들 중, 제동(throttle) 이 걸린 주기 의 비율 이다.
그러니까 이 알림 은 “CPU 를 많이 쓰고 있다” 가 아니라 “쓰려고 할 때마다 limit 벽 에 부딪히고 있다” 는 뜻 이다. 사용률 과 throttling 은 아예 다른 축 이다.
2. 동작 원리 — CFS quota 는 100ms 단위 로 집행 된다
쿠버네티스 의 limits.cpu 는 리눅스 CFS(Completely Fair Scheduler) quota 로 구현 된다. 핵심 은 시간 을 잘게 쪼개서 집행 한다는 것:
- 시간 이 100ms 주기(period) 로 나뉜다.
- 컨테이너 는 주기 마다 quota 를 받는다.
limits.cpu: 2면 매 100ms 마다 최대 200ms 어치 CPU 시간 (코어 2개 몫). - 어떤 주기 에서 quota 를 다 써버리면 — 그 주기 의 남은 시간 동안 프로세스 가 강제 로 얼어붙는다(frozen). 다음 주기 가 시작 되면 다시 실행.
이 “얼어붙은 주기” 가 throttled period 다. 커널 은 nr_periods(지나간 주기 수) 와 nr_throttled(제동 걸린 주기 수) 를 센다. 그리고:
throttling 비율 = nr_throttled / nr_periods
5분 간 주기 3,000개 중 1,725개 에서 quota 소진 → 1725 / 3000 = 57.5%. 이게 알림 의 57.5% 다.
즉 제동 은 항상 limit 의 100% 를 친 순간 에 걸린다. 57.5% 는 그 제동 이 얼마나 자주 걸렸는가 라는 빈도 통계 다.
3. 그럼 사람 이 정한 숫자 는 뭔가
이 알림 뒤 에 사람 이 설정 한 숫자 는 딱 두 개 다:
| 숫자 | 누가 정했나 | 의미 |
|---|---|---|
limits.cpu: 2 |
내 가 (helm values) | 제동 이 걸리는 벽 |
25% |
kube-prometheus-stack 기본 룰 | 알림 이 뜨는 임계값 |
알림 룰 (요지) 은 이렇게 생겼다:
sum(increase(container_cpu_cfs_throttled_periods_total[5m]))
/
sum(increase(container_cpu_cfs_periods_total[5m]))
> 0.25
“최근 5분 간 주기 의 25% 이상 에서 제동 이 걸리면 info 알림.” 우리 케이스 는 57.5% > 25% 라서 발화 한 것 뿐 이다.
4. 왜 velero 였나 — 버스트형 워크로드 의 숙명
velero 는 전형적 인 버스트형 워크로드 다. 평소 엔 거의 놀다가(1m), 백업 스케줄 이 돌 때 몇 분 간 CPU 를 확 끌어쓴다.
실제 로 타임라인 을 맞춰 보니 — 알림 시각 은 4시간 마다 도는 hourly-critical 백업 job 직후 였다. 백업 하는 몇 분 동안 velero 가 2 core 벽 을 계속 치받았고 → throttled 주기 비율 이 치솟았고 → 백업 이 끝나자 알림 도 [resolved] 로 저절로 꺼졌다.
이 패턴 (평소 0 → 순간 burst → limit 충돌) 은 백업 · 배치 · 컴파일 · GC 같은 워크로드 에서 아주 흔하다. 그래서 CPUThrottlingHigh 는 대표적 인 노이즈성 알림 으로 불린다. 오죽하면 kube-prometheus-stack 에서 severity 가 info 다.
5. 그래서 언제 조치 해야 하나
판단 기준 은 하나 다 — throttling 이 지연(latency) 으로 이어지는가.
조치 불필요 (우리 케이스):
- 배치성 작업 이고, 몇 분 늦게 끝나도 무방
- 알림 이 주기 작업 시각 과 겹치고 스스로 resolved
- 서비스 응답 시간 에 영향 없음
조치 필요:
- 사용자 요청 을 받는 서비스 (API, 웹) 에서 지속 발생 — throttling 은 곧 tail latency (p99 폭발) 로 나타난다
- 백업/배치 가 throttling 때문 에 시간 창(window) 을 넘김
조치 방법 (택 1):
limits.cpu상향 — 벽 을 뒤로 민다 (2 → 3~4)- limit 제거 — CPU 는 압축 가능한(compressible) 자원 이라, requests 만 두고 limit 을 없애는 운영 철학 도 널리 쓰인다 (노드 여유 가 있을 때 마음껏 쓰게)
- 알림 튜닝 — 특정 네임스페이스 제외 or 임계값 상향
나 는 그냥 둔다 — 백업 은 정상 완료 됐고, info 알림 이 4시간 에 한 번 스쳐 가는 건 비용 이 아니다. 모든 알림 에 반응 하는 것 보다, 알림 의 의미 를 정확히 아는 게 먼저 다.
6. 요약
- CPUThrottlingHigh ≠ CPU 사용률 높음. 사용률 1m 짜리 pod 에서도 뜬다.
- 제동 은 항상 limit 100% 에서 걸린다. 57.5% 는 제동 이 걸린 주기 의 비율 이라는 빈도 통계 다.
- 사람 이 정한 건
limits.cpu(벽) 와 알림 임계값 25%(기본값) 둘 뿐. - 버스트형 워크로드 (백업·배치) 에선 정상 현상 에 가깝다. latency 에 영향 줄 때만 조치.
같은 원리 로 — 그라파나 에서 CPU 그래프 가 낮은데 서비스 가 느리다면,
container_cpu_cfs_throttled_periods_total을 꼭 봐라. 사용률 은 낮은데 목 이 졸리고 있는 경우 가 진짜 있다.