Полное руководство по systemd cgroups для ограничения и изоляции ресурсов
Используйте systemd cgroups, срезы и свойства модулей для ограничения CPU, памяти и ввода-вывода без редактирования исходных файлов cgroups.
Полное руководство по systemd cgroups для ограничения и изоляции ресурсов
Systemd уже помещает службы в контрольные группы Linux. Вам не нужно вручную создавать каталоги cgroups, чтобы не допустить, чтобы пакетный обработчик съел всю машину. Во многих случаях вы можете добавить несколько свойств в службу или срез, перезагрузить systemd и получить контроль над CPU, памятью, задачами и вводом-выводом, который сохраняется после перезагрузки и отображается в обычных инструментах systemctl.
Хитрость заключается в выборе правильного типа ограничения. Жесткое ограничение памяти может защитить хост, но убить службу, если установить его слишком низко. Веса CPU мягкие, пока система не загружена. Квоты CPU строгие, но могут добавить задержку. Ограничения ввода-вывода зависят от стека хранения и версии cgroups. Контроль ресурсов — это не флажок; это эксплуатационный компромисс.
Понимание контрольных групп (cgroups)
Прежде чем углубляться в реализацию systemd, важно понять основные концепции cgroups. Cgroups — это иерархический механизм в ядре Linux, который позволяет группировать процессы, а затем назначать этим группам политики управления ресурсами. Эти политики могут включать:
- CPU: Ограничение времени CPU, приоритезация доступа к CPU.
- Память: Установка лимитов использования памяти, предотвращение условий нехватки памяти (OOM).
- Ввод-вывод: Ограничение операций чтения/записи на диск.
- Сеть: Управление сетью возможно через Linux traffic control и связанные инструменты, но встроенные свойства модулей systemd в основном сосредоточены на CPU, памяти, количестве процессов, доступе к устройствам и блочном вводе-выводе.
- Доступ к устройствам: Контроль доступа к конкретным устройствам.
Ядро предоставляет конфигурации cgroups через виртуальную файловую систему, обычно смонтированную в /sys/fs/cgroup. Каждый контроллер (например, cpu, memory) имеет свой собственный каталог, и внутри них иерархии каталогов представляют группы и связанные с ними ограничения ресурсов.
Архитектура управления cgroups в systemd
Systemd абстрагирует сложность прямого манипулирования cgroups, предоставляя структурированную систему управления модулями. Он организует процессы в иерархию модулей, которые затем отображаются в иерархии cgroups. Основные типы модулей, относящиеся к управлению ресурсами:
- Срезы (Slices): Это абстрактные контейнеры для модулей служб. Срезы образуют иерархию, позволяя делегировать ресурсы. Например, срез для пользовательских сессий может содержать срезы для отдельных приложений. Systemd автоматически создает срезы для системных служб, пользовательских сессий и виртуальных машин/контейнеров.
- Области (Scopes): Обычно используются для временных или динамически создаваемых групп процессов, часто связанных с пользовательскими сессиями или системными службами, которые не управляются как полноценные модули служб. Они временны и существуют, пока работают процессы внутри них.
- Службы (Services): Это основные модули для управления демонами и приложениями. Когда модуль службы запускается, systemd помещает его процессы в иерархию cgroups, обычно в пределах среза. Ограничения ресурсов могут применяться непосредственно к модулям служб.
Стандартная иерархия systemd часто выглядит так:
-.slice (Корневой срез)
|- system.slice
| |- <service_name>.service
| |- another-service.service
| ...
|- user.slice
| |- user-1000.slice
| | |- session-c1.scope
| | | |- <application>.service (если запущено пользователем)
| | | ...
| | ...
| ...
|- machine.slice (для ВМ/контейнеров)
...
Применение ограничений ресурсов с помощью файлов модулей systemd
Systemd позволяет указывать ограничения ресурсов cgroups непосредственно в файлах .service, .slice или .scope. Эти директивы размещаются в разделах [Service], [Slice] или [Scope] соответственно.
Ограничения CPU
Основные директивы для контроля ресурсов CPU:
CPUQuota=: Ограничивает общее время CPU, которое может использовать модуль. Указывается в процентах (например,50%для половины ядра CPU) или в виде доли ядра CPU (например,0.5). Также можно указать значение в микросекундах за период. Период по умолчанию — 100 мс.CPUWeight=: Устанавливает относительный вес для времени CPU в системах cgroup v2. Модуль с более высоким весом получает большую долю при конкуренции, но не резервирует CPU, когда машина простаивает.CPUShares=: Старый вес для cgroup v1. ПредпочитайтеCPUWeight=на современных дистрибутивах, если только вы не уверены, что вам нужна совместимость с v1.CPUQuotaPeriodSec=: Устанавливает период дляCPUQuota. По умолчанию —100ms.
Пример: Ограничение веб-сервера до 75% одного ядра CPU:
Создайте или отредактируйте файл службы, например, /etc/systemd/system/mywebapp.service:
[Unit]
Description=Мое веб-приложение
[Service]
ExecStart=/usr/bin/mywebapp
User=webappuser
Group=webappgroup
# Ограничение до 75% одного ядра CPU
CPUQuota=75%
[Install]
WantedBy=multi-user.target
После создания или изменения файла службы перезагрузите демон systemd и перезапустите службу:
sudo systemctl daemon-reload
sudo systemctl restart mywebapp.service
Ограничения памяти
Ограничения памяти контролируются такими директивами, как:
MemoryMax=: Устанавливает жесткий лимит на объем памяти, который могут потреблять процессы модуля. Может быть указан в байтах или с суффиксами, такими какK,M,G,T(например,512M).MemoryLimit=: Старое написание, сохраненное на некоторых системах для совместимости. ПредпочитайтеMemoryMax=в современных версиях systemd.MemoryHigh=: Устанавливает мягкий лимит. При приближении к этому лимиту более агрессивно запускается восстановление памяти (свопинг), но жесткий лимит еще не применяется.MemorySwapMax=: Ограничивает объем пространства подкачки, который может использовать модуль.
Пример: Ограничение базы данных до 2 ГБ ОЗУ:
Создайте или отредактируйте файл службы, например, /etc/systemd/system/mydb.service:
[Unit]
Description=Моя служба базы данных
[Service]
ExecStart=/usr/bin/mydb
User=dbuser
Group=dbgroup
# Ограничение памяти до 2 гигабайт
MemoryMax=2G
[Install]
WantedBy=multi-user.target
Перезагрузите и перезапустите:
sudo systemctl daemon-reload
sudo systemctl restart mydb.service
Ограничения ввода-вывода
Регулирование ввода-вывода можно контролировать с помощью таких директив, как:
IOWeight=: Устанавливает относительный вес для операций ввода-вывода. Более высокие значения дают более высокий приоритет ввода-вывода. Диапазон от 1 до 1000 (по умолчанию 500).IOReadBandwidthMax=: Ограничивает пропускную способность чтения. Указывается как[<устройство>] <байт_в_секунду>. Например,IOReadBandwidthMax=/dev/sda 100Mограничивает операции чтения на/dev/sdaдо 100 МБ/с.IOWriteBandwidthMax=: Ограничивает пропускную способность записи. Аналогичный форматIOReadBandwidthMax.
Пример: Ограничение службы фоновой обработки до 50 МБ/с на конкретном диске:
Создайте или отредактируйте файл службы, например, /etc/systemd/system/batchproc.service:
[Unit]
Description=Служба пакетной обработки
[Service]
ExecStart=/usr/bin/batchproc
User=batchuser
Group=batchgroup
# Ограничение операций записи до 50 МБ/с на /dev/sdb
IOWriteBandwidthMax=/dev/sdb 50M
# Установка умеренного приоритета чтения
IOWeight=200
[Install]
WantedBy=multi-user.target
Перезагрузите и перезапустите:
sudo systemctl daemon-reload
sudo systemctl restart batchproc.service
Управление и мониторинг cgroups
Systemd предоставляет инструменты для проверки и управления cgroups, связанными с вашими модулями.
Проверка статуса cgroups
Команда systemctl status предоставляет информацию о членстве модуля в cgroup и использовании ресурсов.
systemctl status mywebapp.service
Ищите строки, указывающие путь cgroup. Например:
● mywebapp.service - Мое веб-приложение
Loaded: загружен (/etc/systemd/system/mywebapp.service; включен; предустановка поставщика: включена)
Active: активен (работает) с Вт 2023-10-27 10:00:00 UTC; 1 день назад
Docs: man:mywebapp(8)
Main PID: 12345 (mywebapp)
Tasks: 5 (limit: 4915)
Memory: 15.5M
CPU: 2h 30m 15s
CGroup: /system.slice/mywebapp.service
└─12345 /usr/bin/mywebapp
Вы также можете напрямую проверить файловую систему cgroups:
systemd-cgls # Отображает иерархию cgroups, управляемую systemd
systemd-cgtop # Аналогично top, но для cgroups
Чтобы увидеть конкретные ограничения, примененные к cgroup службы:
# Для ограничений памяти на типичном хосте cgroup v2
cat /sys/fs/cgroup/system.slice/mywebapp.service/memory.max
# Для ограничений CPU
cat /sys/fs/cgroup/system.slice/mywebapp.service/cpu.max
Точные пути и имена файлов различаются в зависимости от версии cgroups и дистрибутива. В системах cgroup v1 могут по-прежнему существовать пути, специфичные для контроллера, такие как /sys/fs/cgroup/memory/.... В системах cgroup v2 единая иерархия в /sys/fs/cgroup/... является стандартным представлением.
Изменение ограничений cgroups на лету
Хотя рекомендуется устанавливать ограничения в файлах модулей, вы можете временно настроить их с помощью systemctl set-property:
sudo systemctl set-property mywebapp.service CPUQuota=50%
В зависимости от версии systemd и флагов, set-property может записать drop-in в /etc/systemd/system.control/ для постоянных свойств. Используйте systemctl cat mywebapp.service и systemctl show mywebapp.service -p CPUQuota -p MemoryMax, чтобы подтвердить, что произошло. Для инфраструктуры как кода и рецензирования явный drop-in модуля обычно понятнее.
Срезы для делегирования ресурсов
Срезы мощны для управления группами служб или приложений. Вы можете определить ограничения ресурсов для среза, и все службы или области в этом срезе будут наследовать или ограничиваться этими лимитами.
Пример: Создание выделенного среза для ресурсоемких пакетных заданий:
Создайте файл среза, например, /etc/systemd/system/batch.slice:
[Unit]
Description=Срез пакетной обработки
[Slice]
# Ограничение общего CPU для всех заданий в этом срезе до 1 ядра
CPUQuota=100%
# Ограничение общей памяти до 4 ГБ
MemoryMax=4G
Теперь вы можете настроить службы для работы в этом срезе, используя директиву Slice= в их файлах .service:
[Unit]
Description=Конкретное пакетное задание
[Service]
ExecStart=/usr/bin/mybatchjob
# Размещение этой службы в batch.slice
Slice=batch.slice
[Install]
WantedBy=multi-user.target
Перезагрузите systemd, включите/запустите срез, если необходимо (хотя он часто активируется неявно), и запустите службу.
sudo systemctl daemon-reload
sudo systemctl start mybatchjob.service
Этот подход позволяет группировать связанные процессы и управлять их совокупным потреблением ресурсов.
Лучшие практики и соображения
- Начинайте с постепенных ограничений: При установке лимитов начинайте с консервативных значений и постепенно увеличивайте их по мере необходимости. Агрессивные ограничения могут дестабилизировать приложения.
- Мониторинг: Регулярно отслеживайте использование ресурсов вашей системы и влияние ваших настроек cgroups. Инструменты, такие как
systemd-cgtop,htop,topиiotop, бесценны. - Понимание cgroup v1 vs. v2: Systemd поддерживает как cgroup v1, так и v2. Хотя многие директивы схожи, v2 предлагает единую иерархию и некоторые поведенческие различия. Убедитесь, что вы знаете, какую версию использует ваша система, если столкнетесь со сложными проблемами.
- Приоритезация против жестких ограничений: Используйте
CPUWeightдля приоритезации, когда ресурсы ограничены, иCPUQuotaдля строгих ограничений. Аналогично,MemoryHighпредназначен для давления перед жестким лимитом, аMemoryMax— это жесткий лимит. - Служба против среза: Используйте модули служб для отдельных приложений и срезы для управления группами связанных приложений или пулами ресурсов.
- Документация: Четко документируйте ограничения ресурсов, применяемые к критическим службам, особенно в производственных средах.
- OOM Killer: Имейте в виду, что если процесс превышает свой лимит
MemoryMax, ядро может завершить его с помощью механизма Out-Of-Memory (OOM), даже если он находится в cgroup. Systemd может управлять поведением OOM killer для конкретных cgroups с помощью директив, таких какOOMPolicy=.
Более безопасный способ внедрения ограничений
Начните с наблюдения. Прежде чем добавлять ограничения, посмотрите, как ведет себя служба при нормальной нагрузке и при наихудшей ожидаемой нагрузке:
systemctl status mywebapp.service
systemd-cgtop
systemctl show mywebapp.service -p MemoryCurrent -p CPUUsageNSec -p TasksCurrent
Для памяти хорошим первым шагом часто является MemoryHigh=, а не MemoryMax=:
[Service]
MemoryHigh=1G
MemoryMax=1536M
MemoryHigh= сообщает ядру о необходимости оказывать давление до того, как служба достигнет жесткого предела. MemoryMax= — это стена. Если процесс пересекает ее и память не может быть восстановлена, ядро может убить процесс в cgroup. Это может быть именно то, что вам нужно для вышедшего из-под контроля рабочего процесса, но это неприятный сюрприз для базы данных, если вы к этому не готовились.
Для CPU решите, хотите ли вы справедливость или жесткое ограничение:
[Service]
CPUWeight=50
Это снижает приоритет при конкуренции, но по-прежнему позволяет службе использовать простаивающий CPU. Для фоновых заданий это часто лучше, чем квота.
[Service]
CPUQuota=200%
Это ограничивает службу примерно двумя ядрами CPU по времени. Это полезно для шумного пакетного процессора, но может навредить чувствительным к задержкам приложениям, если рабочие потоки будут регулироваться во время пиков трафика.
Для предотвращения взрывов процессов добавьте лимит задач:
[Service]
TasksMax=200
Это защищает хост от случайных форк-штормов. Установите его достаточно высоким для нормального количества потоков. Нагрузки Java, баз данных и браузеров могут использовать больше задач, чем вы ожидаете.
Drop-In вместо редактирования модулей поставщика
Избегайте редактирования файлов модулей, поставляемых пакетами в /usr/lib/systemd/system/ или /lib/systemd/system/. Используйте drop-in:
sudo systemctl edit mywebapp.service
Затем добавьте:
[Service]
MemoryHigh=1G
MemoryMax=1536M
CPUWeight=80
После сохранения:
sudo systemctl daemon-reload
sudo systemctl restart mywebapp.service
systemctl cat mywebapp.service
systemctl cat показывает модуль поставщика и ваше переопределение вместе. Это значительно упрощает будущую отладку, поскольку активная конфигурация видна в одной команде.
Срезы для команд, арендаторов и классов рабочих нагрузок
Срезы становятся полезными, когда вы перестаете думать об одной службе за раз. Предположим, хост запускает API, генератор отчетов и несколько импортеров. Вам может быть все равно, какой импортер использует CPU, но вы заботитесь о том, чтобы вся работа по импорту вместе не могла заморить API голодом.
Создайте срез:
# /etc/systemd/system/import.slice
[Unit]
Description=Рабочие нагрузки импорта и обратного заполнения
[Slice]
CPUWeight=30
MemoryHigh=4G
MemoryMax=5G
Поместите службы импорта внутрь него:
[Service]
Slice=import.slice
ExecStart=/usr/local/bin/import-worker
Теперь группа имеет общее давление. Это чище, чем устанавливать отдельные жесткие ограничения на каждого рабочего и надеяться, что математика все еще сработает после того, как кто-то добавит нового.
Есть одна деталь именования, которая сбивает с толку: имена срезов кодируют иерархию. customer-a.slice — это срез верхнего уровня. customer-a-batch.slice не является дочерним элементом customer-a.slice; это просто еще одно имя верхнего уровня. Иерархические срезы используют дефисы в качестве разделителей определенным образом, поэтому прочитайте systemd.slice(5) перед проектированием большого дерева срезов.
Что ограничения ресурсов не могут исправить
Cgroups могут помешать одной рабочей нагрузке перегрузить хост, но они не могут сделать недоразмеренную машину быстрой. Если базе данных нужно больше памяти для рабочего набора, чем вы разрешаете, она может тратить больше времени на восстановление памяти или выйти из строя под нагрузкой. Если API требует короткого времени отклика, строгая квота CPU может создать задержки регулирования, которые выглядят как случайная задержка. Если устройство хранения уже насыщено, веса ввода-вывода могут улучшить справедливость, но не создадут пропускную способность.
Относитесь к ограничениям как к ограждениям. Сочетайте их с настройками на уровне приложения: размеры буферов базы данных, количество рабочих процессов, параллелизм очередей, ограничения кучи JVM, Go GOMEMLIMIT, флаги памяти Node или что-то еще, что предоставляет ваша среда выполнения. Лучшая настройка обычно включает и то, и другое: приложение знает свою собственную модель памяти и параллелизма, а systemd защищает остальную часть машины, если эта модель нарушается.
Ментальная модель, которую стоит запомнить
Используйте ограничения на уровне служб для одного демона. Используйте ограничения на уровне срезов для группы связанных рабочих нагрузок. Используйте веса, когда хотите приоритет при конкуренции. Используйте квоты и жесткие ограничения памяти, когда вам нужна твердая граница и вы готовы к последствиям. Проверьте эффективные свойства с помощью systemctl show, наблюдайте за поведением с помощью systemd-cgtop и храните конфигурацию в drop-in или файлах модулей, которые ваша команда может просмотреть.