Короткий ответ
CPU throttling подтверждается ростом метрики container_cpu_cfs_throttled_periods_total (или доли throttled-времени) относительно container_cpu_cfs_periods_total в cAdvisor/Prometheus, при одновременном наблюдении, что приложение упирается в limits.cpu, хотя по логике не перегружено; решение — поднять limits.cpu, либо убрать CPU limit вовсе (оставив только requests) с учётом компромиссов для шумных соседей.
Как это работает подробнее
Linux CFS (Completely Fair Scheduler) квотирует CPU контейнера долями времени за период (обычно 100 мс); если за период контейнер исчерпал квоту, соответствующую limits.cpu, ядро принудительно "замораживает" процесс до следующего периода — это и есть throttling, который проявляется как задержки и рост латентности без видимой перегрузки по usage.
Пример кода
rate(container_cpu_cfs_throttled_periods_total{pod="checkout-7d9f"}[5m])
/
rate(container_cpu_cfs_periods_total{pod="checkout-7d9f"}[5m])Что использовать на практике
- Практика. throttling особенно коварен при многопоточных приложениях (Java, Go с большим GOMAXPROCS), где кратковременные всплески параллельной работы быстро исчерпывают квоту CPU даже при невысоком среднем потреблении. Решение зависит от контекста: можно увеличить
limits.cpu, настроитьGOMAXPROCS/размер пула потоков в соответствии с лимитом, либо для некритичных сервисов вообще не ставитьlimits.cpu, полагаясь только наrequestsи QoS.
Подводные камни
- Риски. смотреть только на среднее использование CPU (
usage) без метрики throttling и не видеть проблему; устанавливатьlimits.cpuравнымrequests.cpu"для простоты", что гарантированно создаёт throttling при любом всплеске; игнорировать, что отсутствие CPU limit повышает риск "шумного соседа", отбирающего ресурсы у других подов на узле — здесь нет универсально правильного выбора без учёта архитектуры узла.