Ограничение ресурсов процессов через systemd на практике
systemd умеет ограничивать CPU, память, число процессов и дисковый ввод-вывод без отдельного контейнерного рантайма. Показываем, как настроить лимиты для сервиса, проверить их действие и не превратить защиту от перегрузки в новый отказ.

Процесс, который бесконтрольно съедает память или запускает сотни дочерних задач, способен положить соседние сервисы на том же сервере. В systemd 252 и новее такие риски можно ограничить настройками юнита: задать потолок CPU и памяти, ограничить число задач, снизить приоритет дискового ввода-вывода и проверить результат через cgroups. Ниже — рабочая схема для Linux с systemd и cgroup v2; примеры рассчитаны на сервис myapp.service, который уже установлен в системе.
Что именно ограничивает systemd
systemd помещает процесс сервиса и его потомков в control group, или cgroup1. Ядро применяет к этой группе общие лимиты, поэтому дочерний процесс не может обойти ограничение простым запуском из основного процесса.
Для практики полезно разделять жёсткие пределы и мягкое давление:
CPUQuota=ограничивает суммарное процессорное время группы;MemoryHigh=создаёт давление на группу, аMemoryMax=задаёт жёсткий потолок памяти;TasksMax=ограничивает число процессов и потоков;IOWeight=меняет относительный приоритет дискового ввода-вывода;LimitNOFILE=ограничивает число открытых файловых дескрипторов на процесс.
Это не полноценная песочница. Ограничения ресурсов не заменяют настройку прав доступа, сетевой фильтрации, контейнерной изоляции и резервного копирования.
Базовый drop-in вместо правки unit-файла
Не редактируйте файл из /usr/lib/systemd/system или /lib/systemd/system: обновление пакета может затереть изменения. Для локальной настройки используйте drop-in — отдельный файл, который systemd добавляет к исходному юниту.
- Проверьте имя сервиса и его состояние. Выполните
systemctl status myapp.service. В выводе должно быть точное имя юнита и понятный текущий статус. - Откройте локальный фрагмент. Команда
sudo systemctl edit myapp.serviceсоздаст каталог/etc/systemd/system/myapp.service.d/. Если редактор открыл пустой файл, это нормально. - Добавьте ограничения. Начните с консервативных значений и не копируйте их вслепую в продакшен:
[Service]
CPUQuota=150%
MemoryHigh=768M
MemoryMax=1G
TasksMax=256
LimitNOFILE=65536
CPUQuota=150% означает до полутора логических процессоров суммарно, а не 150% каждого потока. При достижении MemoryMax ядро может завершить процессы группы из-за нехватки памяти. После сохранения проверьте, что файл содержит именно эти директивы, без случайных кавычек или лишних секций.
- Перечитайте конфигурацию и перезапустите сервис. Выполните
sudo systemctl daemon-reload, затемsudo systemctl restart myapp.service. Командаsystemctl is-active myapp.serviceдолжна вернутьactive; если сервис не стартовал, сразу смотрите журнал. - Проверьте применённые значения. Выполните:
systemctl show myapp.service \
-p CPUQuotaPerSecUSec \
-p MemoryHigh \
-p MemoryMax \
-p TasksMax \
-p LimitNOFILE
В выводе должны быть заданные значения или их эквивалент в микросекундах и байтах. Это важнее, чем проверка только содержимого drop-in: итоговая конфигурация может зависеть от других фрагментов и настроек менеджера.
Как выбрать лимиты без угадывания
Лимит должен следовать из наблюдений, а не из числа ядер в паспорте сервера. Сначала измерьте обычную нагрузку и пик: потребление памяти, число задач, открытые дескрипторы и задержки. Затем оставьте запас на всплеск, но не настолько большой, чтобы сервис мог вытеснить критичные процессы.
| Директива | Что происходит | Цена ошибки | Когда применять |
|---|---|---|---|
CPUQuota= | Суммарное CPU-время принудительно ограничивается | Задачи начинают ждать CPU, растёт latency | Для фоновых воркеров и сервисов с известным бюджетом CPU |
MemoryHigh= | Ядро усиливает давление на память группы | Процесс работает медленнее, но не обязательно завершается | Как первый предохранитель перед жёстким пределом |
MemoryMax= | Группа не должна превысить лимит | Возможен OOM-килл внутри cgroup | Для изоляции ненадёжных или фоновых процессов |
TasksMax= | Ограничивается число процессов и потоков | Новые worker-процессы и потоки не создаются | Против fork-бурстов и утечек пулов |
IOWeight= | Меняется относительный приоритет IO | Соседний сервис может получить меньше пропускной способности | Когда сервисы конкурируют за один диск |
Для веб-сервиса обычно опасно сразу ставить маленький TasksMax: сервер может считать потоки, процессы воркеров и служебные задачи вместе. Для базы данных отдельно учитывайте кеш, фоновые процессы и требования документации самой СУБД.
Жёсткий лимит памяти — это предохранитель от каскадного отказа, а не способ сделать приложение экономнее.
CPU и память: где настройка ломается
CPUQuota может не обеспечить низкую задержку
При заданной квоте сервис получает ограничение на суммарное процессорное время, но это не означает, что запрос будет обрабатываться без пауз. Когда группа выбирает квоту, cgroup throttling останавливает её до следующего периода. Для интерактивного API слишком низкая квота проявится ростом времени ответа, даже если средняя загрузка машины кажется небольшой.
Проверяйте не только top, но и задержку приложения, очередь запросов и состояние cgroup. Если лимит нужен только для фоновой задачи, часто безопаснее ограничить именно фоновый юнит, а не весь сервис, который обслуживает пользовательские запросы.
MemoryHigh и MemoryMax решают разные задачи
MemoryHigh не является гарантированной стеной: при превышении система начинает сильнее давить на группу. MemoryMax — граница, после которой дальнейшее потребление памяти невозможно в обычном режиме. Если приложение регулярно упирается в неё, простое увеличение лимита лишь отложит проблему: ищите утечку, слишком большой кеш или неверный размер пула.
После срабатывания проверяйте журнал сервиса и состояние памяти. Сообщения об убийстве процесса могут появиться не в приложении, а в журнале ядра. В systemd также полезно посмотреть сводку: systemctl status myapp.service и systemctl show myapp.service -p MemoryCurrent -p MemoryPeak.
Число задач, дескрипторы и дисковый ввод-вывод
TasksMax считает не только процессы, но и потоки. Если приложение использует пул из 32 воркеров, каждый из которых создаёт несколько потоков, лимит в 64 может сломать запуск под нагрузкой. Начните с фактического значения в спокойном режиме, добавьте запас и протестируйте сценарий максимальной параллельности.
LimitNOFILE задаёт лимит открытых файловых дескрипторов для процессов сервиса. Поднимать его стоит вместе с настройкой самого приложения: системный предел не заставит сервер автоматически увеличить собственный пул соединений. Если в журнале появляется Too many open files, сначала определите, что расходует дескрипторы, а не просто выставляйте миллион.
Для ограничения конкуренции за диск можно добавить:
[Service]
IOWeight=200
Это относительный вес, а не фиксированные мегабайты в секунду. Он имеет смысл только там, где несколько cgroup действительно конкурируют за один и тот же блочный ресурс. Для точного ограничения скорости нужен другой механизм, например отдельная настройка IO-контроллера; не подменяйте её IOWeight.
Как понять, что лимит сработал
- Показывайте итоговые параметры через
systemctl show, а не только исходный файл. - Сопоставляйте пик нагрузки с метриками приложения: latency, ошибки, длина очереди и число активных соединений.
- Проверяйте журнал:
journalctl -u myapp.service -b. Ошибка запуска после изменения часто означает синтаксическую или ресурсную проблему. - Для теста CPU создайте нагрузку в отдельном стенде и убедитесь, что ограничивается именно нужный сервис, а не вся система.
- После теста уберите временные генераторы нагрузки и убедитесь, что сервис возвращается в штатное состояние.
Если параметр не виден или игнорируется, проверьте версию systemd, режим cgroup и права менеджера. Команда systemd-detect-virt поможет заметить контейнерное окружение, но не докажет, что контейнеру разрешено управлять всеми контроллерами.
Что делать дальше
Сохраните настройки в drop-in, добавьте их в конфигурацию сервера и зафиксируйте исходные метрики до изменения. Затем проведите нагрузочный тест с реалистичным числом запросов или задач. В продакшене начните с наблюдения за MemoryCurrent, пиками памяти, throttling CPU, числом задач и временем ответа; только после этого ужесточайте пределы.
Если сервис регулярно достигает MemoryMax или CPUQuota, следующий шаг — не новый лимит, а разбор причины: утечка памяти, слишком большой пул, блокирующий ввод-вывод или неверная модель фоновых задач.
1 cgroup — механизм ядра Linux, который объединяет процессы и применяет к группе общие ограничения и учёт ресурсов.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: systemd.resource-control — официальная документация, systemd.service — официальная документация
Частые вопросы
Можно ли применить лимиты без перезагрузки сервера?
Что произойдёт, если сервис превысит MemoryMax?
Почему CPUQuota=100% — это не загрузка всех ядер на 100%?
Может ли TasksMax сломать приложение, которое создаёт много потоков?
Почему systemctl show показывает не то значение, которое указано в drop-in?
Работает ли эта схема на любой версии systemd?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.