Зачем трогать CPU pinning и NUMA на VDS
CPU pinning (закрепление потоков за конкретными CPU) и NUMA-настройки обычно всплывают тогда, когда «в среднем всё быстро», но в проде случаются странные хвосты задержек: редкие пики latency, дрожание p99, плавающая скорость сборок, джиттер у real-time/стриминга или непредсказуемые задержки БД.
На VDS добавляется ещё один слой неопределённости: вы видите vCPU, а под ними скрыты физические ядра хоста, планировщик гипервизора, соседние виртуалки и политика распределения прерываний (IRQ). Поэтому цель pinning/NUMA здесь не «ускорить всё на 30%», а сделать производительность предсказуемой и убрать случайные деградации.
Важно: на типичном VDS вы не контролируете размещение vCPU на физике так, как на выделенном сервере. Но вы всё равно можете навести порядок внутри гостя: развести критичные и фоновые процессы по CPU, уменьшить миграции потоков и проследить, чтобы IRQ/softirq не забивали «дорогие» ядра.
- Ограничить «шумные» фоновые процессы и увести их с CPU, где крутится критичная нагрузка.
- Снизить миграции потоков между CPU (меньше cache-miss и меньше джиттера).
- Не дать IRQ/softirq съедать CPU, выделенные под p99.
- Если гость видит NUMA — уменьшить штраф за удалённую память (точечно и измеримо).
Базовые понятия: vCPU, steal time и «почему иногда тормозит без причины»
vCPU против pCPU: что вы реально закрепляете
Внутри гостя вы видите CPU 0..N и можете закреплять процессы за ними. Это закрепление внутри гостевой ОС. А вот соответствие «vCPU → физическое ядро» (pCPU) решает гипервизор. Поэтому pinning в госте чаще полезен для локальности кэшей и предсказуемости планировщика Linux, а не для «выбора лучшего физического ядра».
Steal time: главный маркер конкуренции на хосте
Steal time — время, когда ваш vCPU хотел выполняться, но гипервизор отдал физическое ядро другим задачам. Это один из самых честных индикаторов «соседи шумят» или «хост перегружен».
Как посмотреть быстро:
top
В top обращайте внимание на %st. Для более детального анализа по всем CPU:
mpstat -P ALL 1
Если %steal заметно растёт под нагрузкой — pinning внутри гостя может сгладить хвосты, но не отменит физическую конкуренцию. Тогда сильнее работают «приземлённые» меры: уменьшить размазывание нагрузки по всем vCPU, убрать фон, а при необходимости масштабироваться по ресурсам.
Ожидание от CPU pinning на VDS лучше формулировать так: «меньше вариативности и меньше микропровалов», а не «магический прирост на ровном месте».

Как понять, есть ли NUMA и имеет ли смысл оптимизация
NUMA (Non-Uniform Memory Access) на реальном железе означает, что память «ближе» к одним ядрам и «дальше» от других. В виртуализации гость может видеть NUMA-топологию, а может не видеть — зависит от режима виртуализации и настроек хоста.
Проверяем в госте:
lscpu
numactl --hardware
Если numactl показывает 1 node — для гостя NUMA фактически «плоская», и большинство NUMA-настроек смысла не имеют. Если nodes несколько — можно аккуратно тестировать привязку CPU и памяти, но помните: вы всё равно не управляете тем, как гипервизор разложил ваши vCPU по сокетам/нодам хоста.
Где NUMA обычно заметнее
- Базы данных и in-memory хранилища (много случайного доступа к памяти).
- JVM/Go-сервисы с большим heap и высоким RPS.
- Нагрузки, чувствительные к latency: очереди, телеметрия, финтех.
CPU pinning на практике: taskset и systemd
Быстрый pinning через taskset (для эксперимента)
taskset удобен для диагностики и коротких тестов:
taskset -c 2,3 your_command
Посмотреть текущую маску процесса:
taskset -p 1234
Минус подхода: после рестарта сервиса всё теряется, и легко забыть, что «временно закрепляли» процесс.
Закрепление CPU для systemd-сервиса (практичный вариант на проде)
Самый удобный путь в 2026 — задавать affinity на уровне юнита systemd. Создайте drop-in:
systemctl edit your.service
Добавьте:
[Service]
CPUAffinity=2 3
Примените изменения:
systemctl daemon-reload
systemctl restart your.service
Плюсы: декларативно, прозрачно, переживает рестарты. И удобно аудировать, когда вы разбираете «почему p99 поехал».
Если критичная нагрузка запускается воркерами/очередями, полезно также выстроить единый способ запуска и лимитов через systemd. По теме близко: очереди и воркеры: Supervisor vs systemd.
cpuset cgroup: более «правильный» pinning, чем taskset
cpuset cgroup позволяет управлять не только тем, на каких CPU могут работать процессы, но и тем, какие CPU доступны целой группе процессов (вместе с дочерними), а на NUMA-системах — ещё и политиками памяти.
Если у вас systemd, то cgroups у вас уже есть: юниты и слайсы. Практичная схема для небольших VDS выглядит так:
- оставить CPU 0 под «систему» (sshd, journald, агенты мониторинга, мелкие крон-задачи);
- выделить CPU 1..N под приложение/БД и закрепить их через
CPUAffinityили через слайс.
Идея простая: системный шум не должен конкурировать за те же CPU, на которых вы держите p99. Но не перегибайте: на инстансах 2–4 vCPU слишком жёсткое разделение иногда ухудшает ситуацию на всплесках.
isolcpus и изоляция ядер: когда стоит, а когда нет
isolcpus — параметр ядра, который исключает указанные CPU из обычного планирования, чтобы вы вручную размещали там критичные потоки. Исторически это применяли для low-latency задач.
Но в 2026 подход стал осторожнее: изоляция CPU — это не только потенциальный профит, но и риски. На практике легко получить одно из двух:
- «изолированные» CPU простаивают, а остальная система перегружена;
- IRQ/softirq внезапно остаются на «дорогих» ядрах, и весь эффект от pinning исчезает.
Если хочется пробовать, начинайте с менее инвазивных шагов (systemd affinity, разведение фона, анализ IRQ) и только потом рассматривайте изоляцию.
Проверить текущую командную строку ядра:
cat /proc/cmdline
Любые изменения параметров загрузки делайте только с планом отката и доступом к консоли (особенно на удалённых VDS).
IRQ и irqbalance: скрытый источник «плавающей» производительности
Даже если вы идеально закрепили приложение на CPU 2–3, никто не мешает ядру обрабатывать на этих CPU прерывания от сетевой карты, диска или виртуальных устройств. В итоге растёт softirq, падает throughput и увеличивается latency.
Как увидеть, что вас душат IRQ/softirq
Смотрите распределение прерываний:
cat /proc/interrupts
Если видите, что активные IRQ «прибиты» к тем же CPU, где живёт ваша нагрузка, — это кандидат на перераспределение.
Посмотрите softirq по CPU:
cat /proc/softirqs
Что делает irqbalance и почему он может мешать pinning
irqbalance старается равномерно распределять IRQ по CPU. На «обычных» серверах это часто полезно. Но при CPU pinning может быть наоборот: irqbalance начнёт разбрасывать IRQ по всем CPU, включая те, которые вы хотели оставить «чистыми» под приложение.
Практическое правило:
- Если вы не делаете pinning и не изолируете CPU —
irqbalanceобычно оставляют включённым. - Если вы выделяете CPU под приложение — либо настраивайте irqbalance (исключения), либо тестируйте вариант без него и сравнивайте метрики.
Проверка статуса сервиса:
systemctl status irqbalance
Нюанс VDS: часть IRQ в госте может быть виртуализована, и ручной контроль окажется ограниченным. Но даже тогда наблюдение за /proc/interrupts помогает понять, откуда берётся джиттер.

Практический план работ: внедряем без боли
Шаг 1. Фиксируем базовую линию
До любых изменений зафиксируйте:
- p50/p95/p99 latency приложения (или запросов к БД);
%st(steal time) по CPU;- косвенные признаки «давки»: рост system CPU, рост run queue, лишние миграции;
- распределение IRQ/softirq по CPU.
Минимальный набор команд под нагрузкой:
uptime
mpstat -P ALL 1
pidstat -u -p ALL 1
cat /proc/interrupts
Шаг 2. Мягко выделяем CPU под критичный сервис
Начните с CPUAffinity в systemd для одного сервиса. Выберите, например, 2 CPU под приложение, оставив CPU 0 под систему. После этого перепроверьте tail latency и внимательно посмотрите, не просела ли сеть/диск.
Шаг 3. Убеждаемся, что IRQ не попали на «дорогие» CPU
Если после pinning растёт system CPU или softirq на тех же CPU — это сигнал заняться политикой IRQ и поведением irqbalance.
Шаг 4. Если NUMA видна — тестируем точечно
Если NUMA доступна, тестируйте numactl для конкретного процесса в рамках эксперимента (например, бенчмарк или отдельный воркер). На VDS эффект может быть меньше, чем на bare metal, поэтому критично сравнивать метрики «до/после», а не «включать всё подряд».
Типичные ошибки и анти-паттерны
Ошибка 1: закрепили приложение, но забыли про фон
Если на тех же CPU крутятся агент мониторинга, лог-шейпер, cron-джобы, CI-раннеры — вы сами создаёте конкуренцию. Смысл pinning как раз в том, чтобы развести критичное и шумное.
Частый источник «шума» — задания по расписанию, которые стартуют внезапно и размазываются по CPU. Если вы постепенно переносите запуск задач в systemd, пригодится: cron vs systemd timers.
Ошибка 2: «изолировали CPU» и потеряли эластичность
Изоляция через isolcpus иногда даёт красивые графики на синтетике, а в реальности ухудшает ситуацию при всплесках. Особенно на маленьких инстансах (2–4 vCPU), где каждый CPU на счету.
Ошибка 3: игнорировать steal time
Если %steal высокий, можно долго полировать affinity и IRQ, но физическая конкуренция всё равно будет съедать производительность. Тогда правильнее думать о смене тарифа/класса CPU, масштабировании или выносе самых чувствительных компонентов.
Чек-лист: цель — предсказуемая производительность без сюрпризов
Соберите метрики p95/p99 и параллельно посмотрите
%st(steal time).Сделайте мягкий CPU pinning через systemd
CPUAffinityдля одного критичного сервиса.Проверьте
/proc/interruptsи/proc/softirqs, убедитесь, что IRQ не «бьют» в те же CPU.Если pinning есть — убедитесь, что политика
irqbalanceне мешает вашей схеме (или сравните с вариантом без него).Если NUMA доступна — тестируйте точечно и измеряйте эффект, а не «включайте всё подряд».
Вывод
CPU pinning в 2026 — это не экзотика, а нормальный инструмент, когда вам нужна предсказуемость на VDS: меньше миграций, меньше джиттера, более стабильные хвосты latency. В связке с пониманием NUMA и аккуратным контролем IRQ можно убрать «случайные просадки», которые сложно объяснить только уровнем приложения.
Рабочая стратегия такая: сначала измеряем (включая steal time), затем делаем минимальные изменения (systemd affinity), и только потом усложняем схему (cpuset, IRQ-политики, изоляция ядер).


