Короткий ответ
Лимит --cpus транслируется в cgroup-параметры CFS-квоты (cpu.cfs_quota_us/cpu.cfs_period_us в cgroup v1 или cpu.max в cgroup v2); превышение квоты за период не убивает процесс, а принудительно приостанавливает его (throttling) до следующего периода, что видно по росту счётчика nr_throttled в cpu.stat.
Как это работает подробнее
CFS (Completely Fair Scheduler) делит время на периоды (обычно 100ms) и выделяет контейнеру квоту CPU-времени внутри каждого периода пропорционально лимиту (например, --cpus=0.5 даёт квоту в половину периода). Если контейнер исчерпал квоту раньше конца периода, ядро приостанавливает (throttle) его потоки до начала следующего периода — приложение не падает, но ощутимо замедляется, даже если на хосте формально есть свободные ядра.
Пример кода
Пример конфигурации
$ cat /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.stat
usage_usec 123456789
nr_periods 45000
nr_throttled 1800
throttled_usec 950000Что использовать на практике
- Практика. throttling диагностируют через
cpu.statсоответствующей cgroup контейнера: растущийnr_throttledотносительноnr_periodsи накопленныйthrottled_timeуказывают на то, что лимит CPU слишком жёсткий для фактической нагрузки приложения, что может объяснять скачки латентности, не видимые в среднем значенииdocker stats.
Подводные камни
- Риск. смотрят только на среднее значение CPU% в
docker statsи не замечают кратковременный throttling, который проявляется именно в хвостовых задержках (p99 latency), а не в средней загрузке; ставят лимит CPU "как получится" без профилирования реального пикового потребления приложения.