Что означает steal time %st: читаем метрику виртуальной машины
Что означает steal time %st, понять можно только вместе с очередью CPU и метриками приложения. Если вы хотите разобраться, как читать CPU steal time, не нужно считать единичный максимум доказательством перегрузки хоста. Усреднение по длинному окну способно скрыть короткие паузы, из-за которых сервис и вышел за SLO.
Что означает %st в top и vmstat?
%st показывает долю интервала, когда готовый к работе vCPU не получал физический CPU, потому что хост в это время занимался другой виртуальной машиной, собственными задачами или упирался в лимит. В top и vmstat процент рассчитывается по накопительным счётчикам ядра. Нулевое значение говорит лишь о том, что за этот интервал ядро не насчитало steal. Другие паузы хоста при этом возможны, а часть сред вообще не передаёт счётчик гостю.
Разобрать, какое CPU-время гипервизор забрал у гостя
Планировщик гостевого ядра делит время vCPU между процессами гостя. Планировщик хоста решает, когда vCPU получит физический CPU. Если готовый к запуску vCPU не получает его, гостевое ядро считает интервал как steal. Поэтому %st относится не к конкретному процессу, а к vCPU целиком.
Если вы хотите разобраться, как читать CPU steal time, разделите два уровня планирования. Высокие %usr и %sys относятся к работе гостя. %idle означает простой без ожидающего I/O, а %iowait не измеряет ожидание физического CPU. Троттлинг из-за cgroup quota тоже возникает внутри VM, его проверяют по квоте в cpu.max и счётчикам cpu.stat, а не по %st.
Для такого учёта гипервизор должен передавать данные гостю. KVM поддерживает steal time на x86 и arm64, но наличие показателя зависит от гостевого ядра и настроек платформы. Нулевой %st означает отсутствие учтённого steal за интервал и не исключает других задержек хоста.
Измерять steal по интервалам, а не по строке с момента загрузки
В procps-ng vmstat столбец st, а в mpstat поле %steal показывают долю выбранного интервала. Первая строка vmstat без -y содержит средние значения с момента загрузки и сглаживает недавний эпизод. Минутный срез лучше начать без этой строки и снять 60 замеров с шагом в секунду:
TZ=UTC LC_ALL=C vmstat -y -t 1 60 | tee vmstat.txt
TZ=UTC LC_ALL=C mpstat -P ALL 1 60 | tee mpstat.txt
Такой срез показывает %st в виртуальной машине вместе с r, %usr, %sys, %iowait и %idle. Строка all в mpstat усредняет vCPU, поэтому всплеск на одном из них может потеряться в общем значении. Поле r в vmstat – это очередь готовых задач (run queue): число процессов, которые уже исполняются или ждут CPU. Это не точная очередь на каждом ядре.
К сырым логам добавьте UTC-время, число vCPU, версию ядра и тип виртуализации. Без этих данных одно и то же значение может относиться к разным конфигурациям и периодам:
date -u --iso-8601=seconds
uname -r
nproc
systemd-detect-virt
LC_ALL=C lscpu | sed -n '/Hypervisor vendor/p;/Virtualization type/p'
Найти обычный уровень для этой VM и этого часа
Baseline формируют на той же VM до инцидента. Нужны интервалы в часы низкой и обычной нагрузки, одинаковые дни недели и сопоставимый профиль запросов. После изменения числа vCPU, версии ядра, тарифа или лимитов прежний baseline перестаёт быть точкой отсчёта, и замеры нужно снять заново. Другое число vCPU меняет распределение потоков по ядрам и усреднение в строке all, а смена тарифа или лимитов меняет условия, в которых гость получает физический CPU.
Для отчёта полезнее медиана и верхние перцентили %st по минутным окнам, чем максимум за месяц. Секундный пик может не повлиять на сервис, а продолжительный умеренный steal способен сдвинуть хвост задержки. Поэтому высокий steal time на VPS определяют как устойчивое отклонение от обычного уровня конкретной VM с измеримым эффектом.
При каком steal time пора искать проблему?
Универсального порога для steal time нет. Проверку стоит начинать, когда %st устойчиво выше обычного уровня этой VM и совпадает с ростом очереди CPU, прикладной задержки или снижением throughput. Короткий всплеск без пользовательского эффекта сначала нужно подтвердить повторным интервальным замером и, если это возможно, данными гипервизора.
Порог оповещения зависит от SLO. Фоновая обработка допускает заметно более долгое ожидание CPU, чем синхронный API-запрос, где задержка сразу попадает в ответ пользователю. И в том, и в другом случае несколько подряд идущих окон с превышением значат больше, чем одно значение выше baseline.
Отделить steal от собственной загрузки CPU
Разбор начинается с сопоставления steal в vmstat с остальными полями того же интервала. Большие %usr или %sys, низкий %idle и r выше числа vCPU означают, что готовым задачам не хватает CPU-времени. Эти признаки ещё не отделяют собственное насыщение от steal, так как оба источника могут действовать одновременно.
Среднее по VM может скрыть занятый vCPU: один CPU-bound поток загрузит его на 100 %, пока остальные свободны. mpstat -P ALL обнаружит неравномерность, а pidstat покажет CPU-время потоков процесса:
TZ=UTC LC_ALL=C mpstat -P ALL 1 60
TZ=UTC LC_ALL=C pidstat -u -t -p PID 1 60
Ровная загрузка vCPU и отсутствие тяжёлых потоков ещё не закрывают вопрос: процессу может не хватать CPU из-за лимита cgroup, а не из-за хоста. Проверьте, упирается ли нагрузка в квоту. Если CPU-контроллер cgroup v2 активен, файл cpu.stat содержит nr_throttled и throttled_usec, и рост этих счётчиков означает throttling выбранной cgroup. Саму квоту показывает cpu.max. Такой троттлинг может увеличить latency и при нулевом %st, поскольку его создаёт гостевая ОС.
При I/O и блокировках задержка растёт даже с небольшой очередью готовых задач. Эти случаи проверьте по метрикам диска, приложения и внешних зависимостей.
Связать недоступный CPU с latency и throughput
Все ряды нужно привести к одной временной шкале. Сопоставьте в UTC значения %st, r, request rate, throughput, error rate и p50/p95/p99 за одинаковые окна. Секундный замер %st нельзя напрямую сравнивать с пятиминутным p99, поскольку в длинном окне короткий эпизод растворится.
Совпадение высокого %st с жалобами на сервис обычно объясняют соседом по хосту, но шумный сосед и CPU steal здесь остаются гипотезой, пока её не подтвердят данные хоста. Эта версия становится правдоподобнее, если деградация повторяется при сопоставимом потоке запросов и не объясняется релизом, сборкой мусора, I/O или cgroup quota. Если данные хоста показывают тот же эпизод, оснований становится больше. Без них корректный вывод ограничивается наблюдением: VM ждала CPU, и в тот же период сервис вышел за SLO.
Для перепроверки минимальный CSV объединяет ряды по временной метке. Рядом сохраните vmstat.txt, mpstat.txt и экспорт приложения:
timestamp_utc,st,r,usr,sys,p95_ms,p99_ms,rps,error_rate
Отчёт строится на сырых рядах, потому что по изображению графика перцентили за другое окно уже не посчитать. Укажите вместе с рядами границы эпизода, часовой пояс и шаг агрегации. Без них принимающая сторона получит на тех же событиях другие числа.
Проверить гипотезу контролируемой CPU-нагрузкой
Контрольный тест добавляет к текущей картине ровно одну известную переменную – предсказуемую CPU-нагрузку на одном vCPU. На тестовой VM закрепите нагрузку на vCPU 0 на две минуты, а версию stress-ng и выбранный CPU-метод сохраните вместе с результатом:
stress-ng --version | tee stress-ng-version.txt
taskset -c 0 stress-ng --cpu 1 --cpu-method matrixprod \
--timeout 120s --metrics-brief 2>&1 | tee stress-ng.txt
Вторая команда создаёт CPU-bound нагрузку на одном vCPU, поэтому на рабочей VM для неё нужно согласованное окно. Одновременно снимите vmstat и mpstat. Bogo-ops сравнивайте только между прогонами с одной версией stress-ng, одинаковом образе и прежних параметрах. Другая VM подходит для контроля лишь при том же классе CPU и лимитах.
На хосте платформенная команда проверяет oversubscription, время ожидания потоков vCPU и их привязку к физическим ядрам. Эти счётчики показывают hypervisor scheduling напрямую, поэтому сопоставьте их с гостевым %st за тот же UTC-интервал. Конкретный набор зависит от гипервизора, и часть значений доступна только через интерфейсы платформы.
Как отличить steal time от нагрузки самого приложения?
Сопоставьте %st с %usr, %sys, %idle и r: эти поля дают разные признаки дефицита CPU.
Проверьте загрузку отдельных vCPU и cpu.stat нужной cgroup: неравномерность или throttling укажут на источник внутри VM.
Повторите одинаковую нагрузку и синхронизируйте прикладные метрики. Совпадение всплесков %st с провалами throughput укажет на хост, но окончательный ответ дадут только его счётчики.
Подготовить доказательства для провайдера или платформенной команды
Начните заявку с того, что вы наблюдали, без версий о причине. Формулировка может выглядеть так: «с 10:14 до 10:22 UTC средний %st вырос относительно обычного уровня, одновременно увеличились run queue и p99». Утверждать, что виноват сосед по хосту, вы не можете – гостевые счётчики этого не показывают.
К описанию приложите данные, по которым отклонение можно проверить: идентификатор VM, регион, число vCPU, версию ядра, UTC-интервал и baseline. Дальше идут сырые логи – по ним принимающая сторона пересчитает агрегаты своим шагом и проверит границы эпизода. Собранный архив можно назвать по инциденту и сложить в него metadata.txt, vmstat.txt, mpstat.txt, прикладной CSV и README с порядком воспроизведения. Пароли, токены и пользовательские данные обязательно удалите перед отправкой.
Отдельно попросите проверить на узле ожидание потоков vCPU, oversubscription по vCPU и события платформы за тот же интервал.
После миграции или смены тарифа повторите тест с прежними длительностью, числом процессов и входными данными. Здесь одного прогона будет мало. Если %st снижается, а throughput возвращается к прежнему уровню сразу в нескольких повторах, влияние платформы подтверждается.
Настроить сигнал по длительности и влиянию, а не по всплеску
Для постоянного наблюдения нужен накопительный счётчик по каждому CPU. В node_exporter это node_cpu_seconds_total с режимом steal. Долю за пять минут рассчитывают так:
100 * avg by (instance) (rate(node_cpu_seconds_total{mode="steal"}[5m]))












