CPU steal time: как измерить, как доказать и когда он ни при чём. В логах пусто, загрузка процессора умеренная, а приложение отвечает вдвое медленнее обычного. Один из кандидатов на объяснение — steal time: время, когда виртуальная машина была готова считать, но не получила физическое ядро.
Метрика простая на вид и очень легко используется неправильно. Ниже — как она устроена, как снять её так, чтобы результат что-то значил, и почему высокий steal сам по себе ещё не диагноз.
Как steal вообще появляется Гипервизор раздаёт физические ядра между виртуальными машинами. Когда планировщик гостевого ядра ставит задачу на vCPU, а гипервизор в этот момент отдал физическое ядро другой машине, гостевое ядро видит, что время прошло, а работа не выполнялась. Эта разница и учитывается как steal.
Отсюда важное следствие: steal измеряется изнутри гостя и всегда является косвенной оценкой. Гость не знает, почему ему не дали ядро. Причин минимум четыре:
конкуренция с соседними VM на ноде;
ограничение по CPU на уровне тарифа (квота), которое гипервизор применяет к вам;
накладные расходы самого планировщика гипервизора;
кратковременные всплески — миграция VM, резервное копирование ноды, обслуживание.
Где steal не виден вообще? Если у вас контейнерная виртуализация (lxc, openvz), steal time не появится никогда — механизма для него нет, ядро общее с хостом. Проверить:
systemd-detect-virt
Аналог steal для контейнеров — троттлинг по cgroup:
grep -E 'nr_throttled|throttled_usec' /sys/fs/cgroup/cpu.stat
cat /sys/fs/cgroup/cpu.max
Растущий nr_throttled означает, что вы выбираете свою квоту. Это не соседи и не оверселлинг — это ваш лимит, и решается он либо оптимизацией, либо тарифом.
Какие значения считать нормой. Универсальной нормы нет — она зависит от платформы виртуализации, политики провайдера и тарифа. Практические ориентиры, которые у меня обычно работают:
0–1% — фон, встречается почти везде, игнорируем.
3–5% — повод посмотреть динамику и сопоставить с задержками приложения. Само по себе не проблема.
выше 10% устойчиво — заметно влияет на латентно-чувствительные сервисы: API, realtime, базы под нагрузкой. Пакетную обработку может почти не задевать.
Ключевое слово — устойчиво. Смотрите значение за 10–15 минут, а не пиковый выброс. Разовый скачок до 30% на две секунды не значит ничего.
Как отличить соседей от собственной квоты. Единственный надёжный способ — сопоставить steal с вашей нагрузкой.
Постройте два ряда за сутки: steal и ваш собственный CPU usage (us + sy). Дальше:
Steal растёт вместе с вашей нагрузкой и падает вместе с ней — почти наверняка вы упираетесь в квоту тарифа. Провайдер тут ни при чём.
Steal приходит независимо от вашей активности, в том числе ночью при простое, — это внешняя конкуренция.
Steal ровным фоном 2–3% круглосуточно — накладные расходы платформы, обычно нормально.
Без исторических данных этот анализ невозможен, поэтому sysstat стоит поставить заранее, а не в момент инцидента.
Что передавать в поддержку? Обращение вида «у меня высокий steal» почти всегда возвращается с просьбой уточнить. Работает такой набор:
вывод sar -u 1 600 или график за несколько часов;
mpstat -P ALL 1 за минуту — с разбивкой по ядрам;
ваша собственная загрузка CPU за тот же период, чтобы показать отсутствие корреляции;
конкретные временные метки, когда приложение деградировало;
systemd-detect-virt и параметры тарифа.
Что не поможет
Оптимизация кода. Если ядро вам не выдают, эффективность вашего кода на steal не влияет.
Добавление vCPU. Иногда даже ухудшает: больше vCPU — больше конкуренции за планирование, особенно на переподписанной ноде.
Перезагрузка. Помогает только если приводит к переезду на другую ноду, и это лотерея.
Чек-лист
systemd-detect-virt # есть ли steal в принципе
vmstat 1 # характер: плато или выбросы
mpstat -P ALL 1 # распределение по ядрам
sar -u 1 600 # устойчивость за 10 минут
grep throttled /sys/fs/cgroup/cpu.stat # для контейн