<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>iowait &#8211; REMADMIN</title>
	<atom:link href="https://remadmin.com/tags/iowait/feed/" rel="self" type="application/rss+xml" />
	<link>https://remadmin.com</link>
	<description>Удалённый системный администратор</description>
	<lastBuildDate>Tue, 25 Aug 2026 16:57:09 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>
	<item>
		<title>Steal time и странный load на VPS: как отличить нехватку CPU у гипервизора от реальной нагрузки</title>
		<link>https://remadmin.com/blog/linux/steal-time-i-strannyj-load-na-vps-kak-otlichit-nehvatku-cpu-u-gipervizora-ot-realnoj-nagruzki/</link>
					<comments>https://remadmin.com/blog/linux/steal-time-i-strannyj-load-na-vps-kak-otlichit-nehvatku-cpu-u-gipervizora-ot-realnoj-nagruzki/#respond</comments>
		
		<dc:creator><![CDATA[Cursor Blog]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 16:57:09 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[iowait]]></category>
		<category><![CDATA[steal time]]></category>
		<category><![CDATA[VPS]]></category>
		<guid isPermaLink="false">https://remadmin.com/?p=7382</guid>

					<description><![CDATA[Load на VPS врёт: очередь растёт и от диска, и от steal time гипервизора, и от квоты cgroup. Как за пять минут отличить нехватку CPU у ноды от реальной нагрузки приложения — по top, mpstat, iostat и cpu.stat.]]></description>
										<content:encoded><![CDATA[<p>Типичная заявка: «сайт тормозит, load под десятку, добавьте ядер». Захожу на VPS — два vCPU, load 8–9, в <code>top</code> почти всё idle. PHP-FPM в состоянии D, MySQL тоже. Хостер клянётся, что «ресурсы выданы полностью». Дальше обычно начинается шаманство: крутят <code>pm.max_children</code>, рестартуют php-fpm, добавляют swap. Это почти никогда не то место, где лежит причина.</p>
<p>На виртуалке load врёт чаще, чем на железе. Он не отделяет «мои процессы жрут CPU» от «диск лёг» и от «гипервизор забрал кванты времени у соседа». Если не разложить эти три штуки, будете лечить не того.</p>
<h5>Что на самом деле считает load average</h5>
<p>Load — это не «загрузка процессора в процентах». Это средняя длина очереди: сколько задач либо готовы бежать на CPU, либо спят в uninterruptible sleep (состояние D, обычно ожидание диска или NFS).</p>
<p>Поэтому картина «load 12 при 70% idle» на двух ядрах — нормальная, если в D висит десяток php-fpm, а диск отвечает по 80–200 мс. Процессор свободен, очередь растёт. С steal time та же история: задача хочет CPU, гипервизор её не планирует, очередь тоже растёт, а в <code>top</code> вы видите idle плюс <code>st</code>.</p>
<p>Смотрю сразу три числа: load за 1/5/15 минут и сколько ядер (<code>nproc</code>). Load 4 на четырёх ядрах — ещё можно жить. Load 4 на одном vCPU уже несколько минут — уже инцидент. Пятнадцатиминутный load запаздывает: если тормоза начались три минуты назад, ориентируйтесь на единицу и на живые счётчики, не на «среднее за четверть часа».</p>
<h5>Три столбца в top, без которых диагностика — гадание</h5>
<p>В шапке <code>top</code> (клавиша <code>1</code> включает разбивку по ядрам) мне важна строка CPU:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">%Cpu(s):  8.4 us,  3.1 sy,  0.0 ni, 41.0 id, 27.2 wa,  0.0 hi,  1.1 si, 19.2 st</pre>
<p>Коротко по делу:</p>
<ul>
<li><code>us</code> + <code>sy</code> — ваши процессы реально крутят код. Вот это «нехватка CPU у приложения».</li>
<li><code>id</code> — простой. Высокий idle сам по себе ничего не лечит и ничего не доказывает.</li>
<li><code>wa</code> — iowait: процессор свободен, но есть незавершённый I/O. Это не «диск загрузил CPU», это ожидание.</li>
<li><code>st</code> — steal: гипервизор отдал эти тики не вам. Ваша виртуалка хотела считать — ей не дали.</li>
<li><code>si</code> — softirq. Если внезапно большой на фоне сети, смотрю пакеты и netfilter, а не PHP.</li>
</ul>
<p>Правило, которым я пользуюсь в первые 30 секунд: если <code>us+sy</code> низкие, а load высокий — это не «надо больше ядер приложению». Это либо диск, либо гипервизор, либо cgroup-троттлинг, который в <code>st</code> может вообще не попасть.</p>
<h5>Steal time: когда виноват не ваш код, а сосед по ноде</h5>
<p>Steal — доля времени, когда vCPU был готов работать, но гипервизор забрал слот. На нормально выделенном KVM с гарантией CPU <code>st</code> обычно около нуля, редкие всплески на 1–2% можно не драматизировать. На дешёвых VPS вечером я регулярно вижу 10–30% steal пачками по несколько минут. Формально у вас «2 ядра», фактически в этот момент — полтора или меньше.</p>
<p>Как это ощущается снаружи: сайт отвечает рывками, CPU в панели хостера «зелёный», процессы в <code>top</code> не жрут по 100%, латентность прыгает без изменения трафика. Внутри в это же время растёт очередь PHP/очередь диска — не потому что MySQL внезапно стал писать в десять раз больше, а потому что кванты CPU приходят дыряво.</p>
<p>Подтверждаю не одним кадром <code>top</code>, а рядом:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="bash">nproc
uptime
mpstat -P ALL 1 30
vmstat 1 30</pre>
<p>В <code>mpstat</code> смотрю столбец <code>%steal</code> по каждому vCPU. Если steal сидит на всех ядрах стабильно выше примерно 5–8% и при этом <code>%usr/%sys</code> не упёрлись в потолок — это не ваше приложение «уперлось в CPU». Это нода. Если steal скачет нуль–сорок нуль–сорок синхронно на всех vCPU — классика оверселла, «шумный сосед» или ночной бэкап гипервизора.</p>
<p>В <code>vmstat</code> те же буквы в последнем столбце <code>st</code>, плюс <code>r</code> (run queue) и <code>b</code> (blocked on IO). Высокий <code>r</code> при низком <code>us</code> и ненулевом <code>st</code> — задачи ждут планировщика, а не вашего кода.</p>
<p>Важный нюанс: на части облаков CPU режут не через steal, а через кредиты/квоты. Тогда <code>st</code> может быть нулевым, а приложение всё равно ползёт. Это уже следующий раздел, не путайте с «честным» KVM-steal.</p>
<h5>Iowait: load большой, процессор скучает, диск не отвечает</h5>
<p>Вторая частая ловушка. Load 10, idle 60%, <code>wa</code> 30–40%. В <code>ps</code> куча процессов в D. Человек видит load и думает «CPU». Нет: очередь — на I/O.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="bash">ps -eo pid,user,state,wchan:24,etime,cmd | awk 'NR==1 || $3 ~ /D/'
iostat -xz 1 20
vmstat 1 20</pre>
<p>В <code>iostat</code> мне важны не «красивые» MB/s, а <code>await</code>, <code>aqu-sz</code> и <code>%util</code>. Если на vda/vdb await уехал в десятки–сотни миллисекунд при скромном объёме записи — это не «MySQL плохо настроен», это сторадж ноды. На дешёвом VPS диск почти всегда общий. Сосед запустил бэкап — ваш InnoDB начинает ждать fsync.</p>
<p>Отдельно проверяю swap:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="bash">free -h
vmstat 1 10</pre>
<p>Ненулевые <code>si/so</code> в <code>vmstat</code> плюс высокий iowait — это уже не «мало CPU», это память кончилась и система молотит страницы. Добавление ядер тут бесполезно. И увеличение <code>pm.max_children</code> в такой ситуации обычно делает хуже: больше процессов — больше давления на диск и RAM.</p>
<p>NFS, CIFS, зависший iSCSI дают ту же картину: D-state, растущий load, CPU почти idle. <code>wchan</code> в <code>ps</code> часто прямо намекает (<code>io_schedule</code>, <code>nfs</code>, <code>wait_on_page</code>). Не лечите это рестартом php-fpm, пока не поняли, на чём они спят.</p>
<h5>Реальная нагрузка: когда процессы правда жрут CPU</h5>
<p>Это самый скучный и самый честный случай. <code>us+sy</code> под 80–100% на всех ядрах, steal около нуля, iowait низкий, в <code>top</code> по CPU видны конкретные pid: php-fpm, mysqld, node, python. Load примерно согласован с числом ядер и run queue.</p>
<p>Тогда уже имеет смысл:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="bash">pidstat -u 1 10
ps -eo pid,user,pcpu,pmem,cmd --sort=-pcpu | head
# если есть perf и можно ненадолго:
# perf top -g</pre>
<p>Искать медленный запрос, воркера без лимита, cron, который внезапно переложил всю таблицу. Здесь апгрейд CPU поможет. На steal и на дохлый диск — нет, вы просто будете дороже ждать то же самое.</p>
<p>Не путайте короткий всплеск (деплой, прогрев кэша, один тяжёлый отчёт) с системной нехваткой. Смотрите 5-минутное окно, не один кадр.</p>
<h5>Четвёртая ловушка: cgroup throttle без steal</h5>
<p>На контейнерах (LXC, часть «VPS из панели», Kubernetes) CPU часто режут квотой cgroup, а не классическим steal. В <code>top</code> тогда <code>st=0</code>, idle может быть приличным, а латентность всё равно плохая: вас просто не пускают на CPU сверх квоты.</p>
<p>На cgroup v2:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="bash">cat /sys/fs/cgroup/cpu.stat
cat /sys/fs/cgroup/cpu.max
cat /proc/pressure/cpu</pre>
<p>В <code>cpu.stat</code> смотрю <code>nr_throttled</code> и <code>throttled_usec</code>. Если throttled растёт на глазах — вас режут. <code>cpu.max</code> вида <code>20000 100000</code> на двух видимых vCPU означает, что «два ядра» в <code>nproc</code> — картинка, а лимит другой.</p>
<p>PSI (<code>/proc/pressure/cpu</code> и <code>io</code>) удобен тем, что показывает stall some/full за 10/60/300 секунд. <code>full</code> по CPU — никто не мог работать. Это ближе к правде, чем load, когда хочется понять «прямо сейчас нас душили или пять минут назад».</p>
<p>На старых OpenVZ/Virtuozzo картина ещё мутнее: иногда steal есть, иногда только failcnt в beancounters. Если попали на такое — не делайте выводы по одному <code>top</code>, смотрите лимиты панели и <code>cpu.stat</code>.</p>
<h5>Порядок, которым я отличаю гипервизор от реальной нагрузки</h5>
<p>Не распухающий чеклист, а последовательность на пять минут.</p>
<ol>
<li>Снять размер машины и load: <code>nproc</code>, <code>uptime</code>, <code>free -h</code>. Понять, не swap ли это вообще.</li>
<li>Живой <code>top</code>, клавиша <code>1</code>. Записать us/sy/id/wa/st. Не скриншот «процессы», а шапку CPU.</li>
<li><code>mpstat -P ALL 1 30</code> и <code>vmstat 1 30</code> — steal стабильный или вспышками, run queue, blocked.</li>
<li>Если wa заметный — <code>iostat -xz 1 20</code> и список D-процессов с <code>wchan</code>.</li>
<li>Если st≈0 и CPU не упирается — <code>cpu.stat</code> / <code>cpu.max</code> / <code>/proc/pressure/cpu</code>.</li>
<li>Только если us+sy высокие и лимиты не душат — иду в pidstat и уже в приложение.</li>
</ol>
<p>За эти пять минут обычно ясно, в какую из четырёх корзин класть инцидент. Дальше уже либо тикет хостеру с цифрами, либо диск, либо квота, либо профилирование PHP/SQL.</p>
<h5>Что не делать, пока не поставили диагноз</h5>
<p>Не поднимать <code>pm.max_children</code> «чтобы очередь рассосалась». На iowait и на steal это увеличивает очередь, а не уменьшает.</p>
<p>Не добавлять swap «для запаса», если si/so уже ненулевые. Вы лечите нехватку RAM ещё большим iowait.</p>
<p>Не рестартить MySQL по кругу, если await диска сотни миллисекунд. Рестарт в этот момент — лотерея с порчей и долгим recovery.</p>
<p>Не просить «ещё два ядра» у хостера, имея на руках только load average. Load на оверсолде растёт и от steal, и от диска. Без <code>mpstat/iostat</code> вам продадут тот же оверсолл подороже — или, что чаще, просто перекинут на такую же ноду.</p>
<h5>Как разговаривать с хостером, чтобы это не было «сайт тормозит»</h5>
<p>Тикет без цифр на оверсолде заканчивается шаблоном «на ноде всё хорошо, смотрите приложение». Я прикладываю 30 секунд <code>mpstat -P ALL</code>, кусок <code>vmstat</code>, если надо — <code>iostat</code>, время (UTC), и формулировку в духе: «на 2 vCPU стабильно %steal 15–25 при %usr+%sys &lt; 30, load растёт, iowait такой-то. Прошу проверить contention на ноде / переезд». Не имена клиентов, не внутренние IP, не «у нас прод упал».</p>
<p>Если хостер нормальный, на устойчивом steal они переезжают. Если нет — имеет смысл менять площадку, а не тюнить opcache. Оптимизация приложения на 10% не лечит потерю четверти CPU каждую минуту.</p>
<p>Для себя оставляю те же логи локально на время разбора. Через час steal может исчезнуть, и вы останетесь с «ну у нас сейчас всё зелёное».</p>
<h5>Коротко: как не перепутать корзины</h5>
<p><strong>Реальная нагрузка:</strong> us+sy высокие, st≈0, wa низкий, в top видны конкретные процессы. Лечите код, запросы, лишние воркеры. Ядра помогут.</p>
<p><strong>Нехватка CPU у гипервизора:</strong> st стабильно заметный, us+sy не упираются, load и латентность прыгают без роста трафика. Лечите площадку, не php.ini.</p>
<p><strong>Диск:</strong> wa высокий, процессы в D, await большой. Часто на тех же дешёвых VPS соседствует со steal — нода оверsold и по CPU, и по стораджу. Сначала цифры, потом выводы.</p>
<p><strong>Квота cgroup:</strong> st=0, cpu.stat throttled растёт, PSI full по CPU. «Два ядра» в панели ≠ два ядра в планировщике.</p>
<p>Пока эти четыре строки не разложены, любой совет «увеличьте лимиты PHP» — гадание. Снимается это стандартными утилитами за несколько минут, без агентов и без панелей. Панель хостера в этот момент почти всегда врёт в оптимистичную сторону: она рисует «выданные» ядра, а не те, которые вам реально отдали.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://remadmin.com/blog/linux/steal-time-i-strannyj-load-na-vps-kak-otlichit-nehvatku-cpu-u-gipervizora-ot-realnoj-nagruzki/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>