OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

systemd-run: ограничиваем CPU и RAM для одноразовых задач и интерактивных команд

Как быстро ограничить CPU и память для разовых команд без unit-файлов: используем systemd-run, transient units в режимах --service/--scope и oneshot, лимиты CPUQuota/CPUWeight и MemoryMax/High/SwapMax, OOM и cgroup v2.
systemd-run: ограничиваем CPU и RAM для одноразовых задач и интерактивных команд

Если вы регулярно запускаете «тяжелые» разовые команды — бэкапы, импорты, сборки фронтенда или видео-транскодирование — то рано или поздно сталкиваетесь с проблемой: процесс внезапно съедает все ядра и память, а сервисы на этом же сервере начинают «задыхаться». Классические утилиты nice/ionice помогают лишь частично: они не гарантируют жестких лимитов и не интегрируются с мониторингом systemd. В современном Linux на cgroup v2 элегантным решением становится systemd-run — создание transient units с мгновенными ограничениями CPUQuota= и MemoryMax= без написания файлов в /etc/systemd/system.

Что такое systemd-run и transient units

systemd-run динамически создает временные (transient) юниты в systemd. Это могут быть сервисы или «области» (scope), которые живут ровно столько, сколько идет команда, а затем исчезают. Плюс — мгновенно включается вся инфраструктура systemd: журналирование, учет ресурсов, границы по CPU/RAM/Tasks, а при желании — таймеры, срезы (slices) и политики OOM.

Transient units — это способ получить преимущества systemd (cgroups, логирование, изоляция) для разовых и интерактивных задач без постоянных unit-файлов. Отлично подходит для DevOps-практик и CI/CD.

Два ключевых режима:

  • --service (значение по умолчанию) — создается transient-сервис с ExecStart равным вашей команде. Умеет «one-shot» поведение через Type=oneshot, поддерживает зависимости и перезапуски.
  • --scope — не сервис, а «контейнер» для уже запущенного процесса с вашим TTY. Идеально для интерактивных команд: вы видите stdout/stderr сразу в консоли, а cgroup-лимиты работают как на полноценный сервис.

CPU: как работает CPUQuota и что с многопроцессорными системами

Свойство CPUQuota= задает потолок CPU в процентах от одного ядра. Примеры:

  • CPUQuota=50% — половина одного CPU в сумме по всем ядрам.
  • CPUQuota=100% — ровно одно полноценное ядро.
  • CPUQuota=250% — два с половиной ядра суммарно (на 4 vCPU это около 62.5% общего CPU).

Важно понимать: quota — это интегральный потолок «сколько CPU-времени суммарно» потребляет группа процессов в cgroup. Это не пиннинг на конкретные ядра и не приоритет. Если вам нужно распределить долю CPU относительно других сервисов, используйте CPUWeight= (от 1 до 10000). А если требуется закрепить выполнение на определенных ядрах — пригодятся CPUAffinity= или AllowedCPUs=.

Резюме по CPU-параметрам для transient units:

  • CPUQuota= — жесткий лимит «сколько CPU максимум».
  • CPUWeight= — относительная «важность» при конкуренции (работает при отсутствии жесткого потолка).
  • AllowedCPUs= — перечисление разрешенных CPU (например, 0-1,3).
  • CPUAffinity= — битовая маска/список для пиннинга процессов юнита.

Память: MemoryMax, MemoryHigh, MemorySwapMax и OOM

MemoryMax= — жесткий потолок RSS+cache для cgroup. Превышение ведет к OOMKill внутри cgroup (ядро завершит «виновных» процессов). Значение 0 означает «без лимита» (не рекомендуется на многоарендных серверах).

MemoryHigh= — мягкий потолок: ядро будет дросселировать задачи при превышении, но не обязательно завершать. Полезно, чтобы не допустить «выбросов», не рискуя мгновенным убийством процесса.

MemorySwapMax= — лимит на swap для cgroup. Значение 0 — запрет свопа для юнита; infinity — без лимита. На небольших инстансах это отличный способ предотвратить «болото» из свопинга.

Поведение при OOM можно уточнить через OOMPolicy= (например, continue, stop, kill) и параметры взаимодействия с systemd-oomd (если он активен в дистрибутиве). Помните, что systemd-oomd ориентируется на PSI и может завершить юнит еще до срабатывания «жесткого» OOM ядра. Для чувствительных задач стоит явно выставлять ManagedOOMPreference= и смежные опции по политике.

Схема: systemd-run создает transient service и scope с лимитами в cgroup v2

Режимы запуска: когда --scope, а когда --service

Используйте --scope, если:

  • вам нужна интерактивность (вы видите вывод в своей TTY, можно вводить пароли и т.п.);
  • вы запускаете разовую команду с лимитами и хотите вернуться к шеллу после завершения;
  • достаточно простых ограничений ресурсов и журналирования.

Используйте --service, если:

  • нужен «one-shot» сервис с Type=oneshot, контролем зависимостей, политиками перезапуска;
  • вы хотите именованный юнит для мониторинга/логов (--unit=), и чтобы он оставался в истории (--collect или без него);
  • планируете подвязать таймеры (systemd-run --on-active=, --on-unit-active=, и т.д.).
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Быстрый старт: примеры команд

Интерактивный запуск с лимитами через scope

systemd-run --scope -p CPUQuota=100% -p MemoryMax=1G bash -lc 'tar -cf /backups/site.tar /var/www/site'

Здесь мы ограничили задачу одним CPU и 1 Гбайтом памяти. После завершения вернемся в shell, а лог попадет в journald под именем transient-unit.

One-shot transient service с именем и ожиданием завершения

systemd-run --unit=backup-db --wait --quiet -p Type=oneshot -p CPUQuota=80% -p MemoryMax=2G bash -lc 'pg_dump -F c appdb > /srv/backup/appdb.dump'

--wait вернет коду выхода процесса в оболочку (удобно для CI). --quiet приглушает вспомогательный вывод. Имя юнита backup-db.service упростит поиск логов в journalctl -u backup-db.

Пиннинг к ядрам и «легкая» конкуренция

systemd-run --scope -p AllowedCPUs=2-3 -p CPUWeight=200 bash -lc 'make -j4 build'

Сборка будет использовать только CPU 2 и 3, а CPUWeight подскажет планировщику относительную «важность» этой группы по сравнению с другими cgroups.

Куда складываются логи и как следить за ресурсами

Все, что запущено через systemd-run, автоматически уходит в journald. Для именованных юнитов проверяйте:

journalctl -u backup-db -e

Для «безымянных» scope можно смотреть последние записи по PID, UID или глобально по времени. Нагрузка по cgroups видна в systemd-cgtop. Это удобно, когда нужно проверить, сработали ли CPUQuota и MemoryMax в условиях конкуренции.

Разница между MemoryMax= и «оно умерло от OOM»

Если процесс завершился с OOM, важно понять «чей OOM»: ядра внутри cgroup (жесткий лимит MemoryMax) или системный/oomd из-за глобального давления. Признаки:

  • При срабатывании MemoryMax журнал содержит записи о «killed process in cgroup»; exit code обычно 9 или 137 (SIGKILL).
  • Если сработал systemd-oomd, в журнале появятся строчки от него с PSI-контекстом.

Для критичных задач добавляйте мягкий MemoryHigh рядом с MemoryMax — это позволит ядру заранее дросселировать и снизить риск мгновенного убийства.

Срезы (slices), группировка и бюджет ресурсов

Если у вас несколько периодических задач, имеет смысл складывать их в общий slice и задать лимиты на уровне среза, а отдельным запускам — дополнительные, более строгие:

systemd-run --scope -p Slice=batch.slice -p CPUQuota=200% -p MemoryMax=3G bash -lc 'rclone sync /data s3:bucket/data'

Срезы удобны тем, что их лимиты наследуются всеми дочерними юнитами. Например, можно ограничить весь batch.slice двумя CPU и 4 Гбайтами памяти, а внутри запускать разные systemd-run с частными квотами. Подробнее про срезы в проде — в материале Как организовать slices и cgroups для PHP-FPM.

Мониторинг ресурсов transient unit через journalctl и systemd-cgtop

Пользовательские юниты: --user контур

systemd-run --user запускает transient units в пользовательском менеджере systemd. Это полезно на CI-агентах или разработческих машинах, где нет root-доступа. Ограничения CPU/RAM будут действовать в рамках пользовательского cgroup-дерева. Пример:

systemd-run --user --scope -p CPUQuota=150% -p MemoryMax=2G bash -lc 'npm ci && npm run build'

В user-контуре часть свойств может быть недоступна, если админ ограничил возможности через policy; проверяйте сообщения об ошибках от systemd-run.

Таймеры и отложенный старт без unit-файла

systemd-run умеет transient timers. Это удобно для одноразового отложенного запуска «с лимитами», без редактирования файлов:

systemd-run --unit=cleanup-once --on-active=10min -p Type=oneshot -p CPUQuota=50% -p MemoryMax=512M bash -lc 'find /tmp -type f -mtime +1 -delete'

Через 10 минут запустится разовая уборка с ограничениями по CPU и памяти. Логи — в journald, имя юнита известно, отследить просто. Если вы привыкли к cron, посмотрите сравнение подходов в статье Cron против systemd-timers: плюсы и минусы.

Чек-лист перед запуском «тяжелой» команды

  • Определите потолок CPU: CPUQuota для жесткой шапки или CPUWeight для «справедливой» конкуренции.
  • Задайте память: MemoryMax и, при необходимости, MemoryHigh; подумайте о MemorySwapMax.
  • Решите режим: --scope для интерактива, --service для one-shot с мониторингом состояния.
  • Дайте понятное имя юниту через --unit= — проще будет смотреть логи и метрики.
  • При необходимости ограничьте CPU-набор: AllowedCPUs или CPUAffinity.
  • Сгруппируйте схожие задачи в batch.slice и задайте срезу общий бюджет.
  • Для CI используйте --wait и проверяйте код выхода.

Практические паттерны для DevOps

Бэкап БД ночью с мягким дросселированием

systemd-run --unit=nightly-pg-backup --wait -p Type=oneshot -p CPUQuota=60% -p MemoryHigh=1.5G -p MemoryMax=2G bash -lc 'pg_dump -F c app > /srv/backup/app-$(date +%F).dump'

MemoryHigh позволит ядру притормаживать pg_dump при всплесках page cache, а MemoryMax спасет остальную систему от безлимитного роста потребления памяти.

Ограничение build-пайплайна фронтенда

systemd-run --scope -p CPUQuota=150% -p MemoryMax=2G bash -lc 'corepack pnpm i && corepack pnpm build'

Сборка не «съест» весь CPU сервера и не вытеснит PHP-FPM или БД.

Немедленная перезапаковка медиа с пиннингом

systemd-run --scope -p AllowedCPUs=1-2 -p MemoryMax=1G bash -lc 'ffmpeg -i in.mp4 -c:v libx264 -preset veryfast -c:a aac out.mp4'

Пиннинг на 1–2 CPU сохраняет отзывчивость «системных» ядер для веба и БД.

Типичные ошибки и способы избежать

  • Неправильное понимание процентов в CPUQuota. 100% — это одно полноценное ядро. Если у вас 8 vCPU и вы хотите «не более половины всей мощности», ставьте 400%, а не 50%.
  • Слишком жесткий MemoryMax без MemoryHigh. Резкие всплески page cache могут привести к OOMKill. Добавьте «мягкий потолок».
  • Запуск без имени юнита. Потом сложно искать логи. Привычка к --unit= окупается.
  • Забыли про своп. Если своп медленный, ограничьте MemorySwapMax или запретите своп для этой задачи.
  • Путают --scope и --service. Для интерактива — scope; для one-shot с таймерами и зависимостями — service.

Наблюдение и отладка

  • systemd-cgtop — покажет потребление CPU и памяти по cgroups; удобно для проверки эффектов CPUQuota/MemoryMax.
  • journalctl -u <unit> — логи конкретного transient unit.
  • Проверяйте результат через код выхода: systemd-run --wait вернет код процесса в оболочку.
  • Для долгих задач полезно добавить RuntimeMaxSec=, чтобы не забыть «висящий» процесс.

Совместимость и нюансы платформ

Современные дистрибутивы используют cgroup v2, где политика CPU/Memory работает предсказуемо и единообразно. На старых системах с cgroup v1 некоторые свойства могут вести себя иначе или требовать дополнительных настроек. В виртуализированных окружениях убедитесь, что гипервизор корректно прокинул лимиты CPU/Memory и видимость CPU для гостя; это влияет на восприятие квот и веса.

В контейнерах возможны ограничения от runtime (namespace/cgroup) поверх systemd. Если systemd внутри контейнера не является PID 1 или запускается в ограниченном режиме, часть свойств может быть недоступна. В таких случаях либо переносите лимиты на уровень оркестратора, либо запускайте задачи снаружи контейнера — на хосте, где systemd управляет cgroups. На классическом shared-хостинге systemd обычно недоступен; если нужен полный контроль ресурсов и фоновые задания, используйте VDS.

Итог

systemd-run — быстрый и аккуратный способ навесить лимиты на CPU и память для разовых команд и «one-shot» задач. Он соединяет удобство shell-команды с управляемостью systemd: ограничение ресурсов, понятные логи, группировка по slices и интеграция с таймерами. Для DevOps это инструмент «под рукой», который помогает держать сервер отзывчивым даже в пиковые моменты — без риска «уронить» прод действием одной-единственной команды.

Поделиться статьей

Вам будет интересно

OpenSearch на VDS: практический гид по памяти JVM heap, ISM-политикам и снапшотам OpenAI Статья написана AI (GPT 5)

OpenSearch на VDS: практический гид по памяти JVM heap, ISM-политикам и снапшотам

Поднимем OpenSearch на VDS: настроим JVM heap без сюрпризов с GC, спроектируем ISM с rollover и удалением, организуем регулярные s ...
ACME DNS‑01 через RFC2136: свой DNS‑API без облаков OpenAI Статья написана AI (GPT 5)

ACME DNS‑01 через RFC2136: свой DNS‑API без облаков

DNS‑01 решает выпуск wildcard и закрытых сервисов, но нужен API к авторитетному DNS. Покажу, как поднять свой «API» на RFC2136: BI ...
Gitea на VDS: установка, systemd, SSL и Nginx reverse proxy OpenAI Статья написана AI (GPT 5)

Gitea на VDS: установка, systemd, SSL и Nginx reverse proxy

Самостоятельный Git без лишней тяжеловесности: развернём Gitea на VDS с обратным прокси Nginx и SSL. Оформим как systemd‑сервис, п ...