18. Управление ресурсами с помощью cgroups

Важную роль в современных компьютерных системах играют механизмы управления использованием ресурсов: когда вы запускаете на одной системе несколько программ, возникает необходимость распределять между ними ресурсы системы, в соответствии с некоторыми правилами. В частности, это особенно актуально на маломощных встраиваемых и мобильных системах, обладающих очень скудными ресурсами. Но та же задача актуальна и для очень мощных вычислительных кластеров, которые располагают огромными ресурсами, но при этом несут и огромную вычислительную нагрузку. Исторически, в Linux поддерживался только одна схема управления ресурсами: все процессы получают примерно равные доли процессорного времени или потока вводавывода. При необходимости соотношение этих долей можно изменить при помощи значения nice, задаваемого для каждого процесса. Такой подход очень прост, и на протяжении долгих лет покрывал все нужды пользователей Linux. Но у него есть существенный недостаток: он оперирует лишь отдельными процессами, но не их группами. В результате, например, веб-сервер Apache с множеством CGI-процессов при прочих равных получает гораздо больше ресурсов, чем служба syslog, у которой не так много процессов.
В процессе проектирования архитектуры systemd, мы практически сразу поняли, что управление ресурсов должно быть одной из базовых функций, заложенных в основы его структуры. В современной системе — неважно, серверной или встраиваемой — контроль использования процессора, памяти и ввода-вывода для различных служб нельзя добавлять задним числом. Такая функциональность должна быть доступна изначально, через базовые настройки запуска служб. При этом, ресурсы должны распределяться на уровне служб, а не процессов, как это делалось при помощи значений nice или POSIX Resource Limits.
В этой статье я попробую рассказать о методах управления механизмами распределения ресурсов между службами systemd. Эта функциональность присутствует в systemd уже долгое время, и давно пора рассказать о ней пользователям и администраторам. В свое время я пояснял, что контрольные группы Linux (cgroups) могут работать и как механизм группировки и отслеживания процессов, и как инструмент управления использованием ресурсов.

Прим. перев.: В указанном документе автор рассказывает, что контрольные группы Linux состоят из двух сущностей: (A) механизма иерархической группировки и маркировки процессов, и (B) механизма, позволяющего распределять ресурсы между полученными группами. Для работы (B) необходимо (A), но не наоборот — (A) может прекрасно работать без (B). Для нормально функционирования systemd (A) необходим, а (B) опционален (он лишь обеспечивает работу некоторых настроек). Вы можете собрать ядро только с необходимой для (A) опцией CONFIG_CGROUPS=y, отключив все связанные с (B) опции (такие как CONFIG_CGROUP_FREEZER=y, CONFIG_CGROUP_DEVICE=y, CONFIG_CGROUP_CPUACCT=y, CONFIG_CGROUP_MEM_RES_CTLR=y, CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y, CONFIG_CGROUP_MEM_RES_CTLR_KMEM=y, CONFIG_CGROUP_PERF=y, CONFIG_CGROUP_SCHED=y, CONFIG_BLK_CGROUP=y, CONFIG_NET_CLS_CGROUP=y, CONFIG_NET_PRIO_CGROUP=y), и systemd будет нормально работать на такой системе (за исключением того, что связанные с этими контроллерами настройки не будут срабатывать). Однако, если собрать ядро без CONFIG_CGROUPS=y, функциональность systemd будет сильно ограничена. При этом, автор особо подчеркивает, что все негативные эффекты влияния контрольных групп на производительность обусловлены именно (B), в то время как (A) на производительность практически не влияет.

Для функционирования systemd необходим только первый из этих режимов, а второй опционален. И именно этот опциональный второй режим дает вам возможность распределять ресурсы между службами. (А сейчас очень рекомендую вам, прежде чем продолжать чтение этой статьи, ознакомиться с базовой информацией о cgroups. Хотя дальнейшие рассуждения и не будут затрагивать низкоуровневые аспекты, все же будет лучше, если у вас сформируется некоторое представление о них.)
Основными контроллерами cgroups, отвечающими за управление ресурсами, являются cpu, memory и blkio. Для их использования необходимо, чтобы они были включены на этапе сборки ядра; большинство дистрибутивов (в том числе и Fedora), включают их в штатных ядрах. systemd предоставляет ряд высокоуровневых настроек, позволяющих использовать эти контроллеры, не вникая в технические детали их работы.

18.1 Процессор
Если в ядре включен контроллер cpu, systemd по умолчанию создает контрольную группу по этому ресурсу для каждой службы. Даже без каких-либо дополнительных настроек это дает положительных эффект: на системе под управлением systemd все службы получают равные доли процессорного времени, независимо от количества процессов, запущенных в рамках службы. Например, на вашем веб-сервере MySQL с несколькими рабочими процессами получит такую же долю процессорного времени, как и Apache, даже если тот запустил 1000 CGI-процессов. Разумеется, такое поведение при необходимости можно легко отключить — см. опцию DefaultControllers= в файле /etc/systemd/system.conf.
Если равномерное распределение процессорного времени между службами вас не устраивает, и вы хотите выделить определенным службам больше или меньше времени — используйте опцию CPUShares= в конфигурационном файле службы. По умолчанию это значение равно 1024. Увеличивая это число, вы даете службе больше процессорного времени, уменьшая — соответственно, меньше. Рассмотрим небольшой практический пример. Допустим, вам нужно увеличить для службы Apache относительную долю потребления процессора до 1500. Для этого создаем файл «ручных» настроек /etc/systemd/system/httpd.service, который включает в себя все те же опции, что и файл настроек по умолчанию /usr/lib/systemd/system/httpd.service, отличаясь от него только значением CPUShares=:


.include /usr/lib/systemd/system /httpd.service

[Service]
CPUShares=1500


Первая строка обеспечивает включение в нашу конфигурацию файла с настройками по умолчанию, сделанными разработчиками Apache или его сопровождающими в вашем дистрибутиве (если это включение не указать явно, данный файл будет проигнорирован). Далее, мы указываем тот параметр, который хотим изменить. Сохраняем файл, приказываем systemd перечитать конфигурацию, и перезапускаем Apache, чтобы настройки вступили в силу (прим. перев.: к сожалению, в настоящее время systemd не поддерживает изменение параметров контрольных групп без перезапуска службы. Но вы можете узнать контрольную группу службы командой наподобие systemctl show -p ControlGroup avahi-daemon.service, и выполнить настройки любым удобным для вас способом, например, через запись значений в псевдофайлы cgroupfs. Разумеется, при следующем запуске службы к ней будут применены параметры, указанные в конфигурационном файле):


systemctl daemon-reload
systemctl restart httpd.service


Готово!
Обратите внимание, что явное указание значения CPUShares= в конфигурации службы заставит systemd создать для нее контрольную группу в иерархии контроллера cpu, даже если этот контроллер не указан в DefaultControllers= (см. выше).

18.3 Память
Используя опции MemoryLimit= и MemorySoftLimit=, вы можете ограничивать суммарное потребление оперативной памяти всеми процессами службы. В них указывается предел потребления памяти в байтах (прим. перев.: разница между MemorySoftLimit= и MemoryLimit= состоит в том, что первый предел можно превышать, если в системе еще есть достаточное количество свободной памяти. Второй из этих пределов превышать нельзя, независимо от наличия свободной памяти). При этом поддерживаются суффиксы K, M, G и T, обозначающие соответственно, килобайт, мегабайт, гигабайт и терабайт (по основанию 1024).


.include /usr/lib/systemd/system/httpd.service

[Service]
MemoryLimit=1G


По аналогии с CPUShares=, явное указание этих опций заставит systemd создать для службы контрольную группу в иерархии контроллера memory, даже если он не был указан в DefaultControllers=.

18.4 Ввод-вывод
Для контроля пропускной полосы ввода-вывода с блочных устройств, доступно несколько настроек. Первая из них — BlockIOWeight=, задающая долю полосы ввода-вывода для указанной службы. Принцип похож на CPUShares= (см. выше), однако здесь величина относительной доли ограничена значениями от 10 до 1000. По умолчанию, она равна 1000. Уменьшить долю для службы Apache можно так:


.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=500


При необходимости, вы можете задать такое значение отдельно для каждого устройства:


.include /usr/lib/systemd/system/httpd.service

[Service]
BlockIOWeight=/dev/disk/by-id/ata-SAMSUNG_MMCRE28G8MXP-0VBL1_DC06K01009SE009B5252 750


При этом, точное название устройства знать не обязательно — достаточно указать интересующий вас каталог:


.include /usr/lib/systemd/system/httpd.service

[Service]
BlockIOWeight=/home/lennart 750


Если заданный вами путь не указывает на файл устройства, systemd автоматически определит, на каком устройстве расположен указанный файл/каталог, и выставит для этого устройства соответствующую настройку. Вы можете добавить несколько таких строк, задавая долю пропускной полосы отдельно для различных устройств, и при этом также допускается указать «общее» значение (как в первом примере), которое будет использовано для всех остальных устройств.
В качестве альтернативы относительной доле пропускной полосы, вы также можете ограничивать абсолютную долю, используя настройки BlockIOReadBandwidth= и BlockIOWriteBandwidth=. В них нужно указать устройство или любой находящийся на нем файл/каталог, а также предельную скорость чтения/записи в байтах в секунду:


.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOReadBandwith=/var/log 5M


В результате, для данной службы скорость чтения с устройства, содержащего каталог /var/log, будет ограничена величиной 5 мегабайт в секунду.
По аналогии с вышеописанными CPUShares= и MemoryLimit=, явное указание любой из приведенных настроек пропускной полосы заставит systemd создать для службы контрольную группу в иерархии контроллера blkio.

18.5 Прочие параметры
Вышеописанные опции покрывают лишь малую толику настроек, поддерживаемых различными контроллерами Linux cgroups. Мы добавили высокоуровневый интерфейс только к тем настройкам, которые кажутся нам наиболее важным для большинства пользователей. Из соображений удобства мы добавили механизмы, обеспечивающие поддержку крупных единиц измерения (килобайты, мегабайты и т.д.) и автоматическое определение блочных устройств по указанному файлу/каталогу.

В некоторых случаях описанных высокоуровневых настроек может оказаться недостаточно — допустим, вам нужно задать низкоуровневую настройку cgroups, для которой мы (пока) не добавили высокоуровневого аналога. На этот случай мы предусмотрели универсальнй механизм задания таких опций в конфигурационных файлах юнитов. Рассмотрим, например, задание для службы параметра swappiness (относительная интенсивность использования подкачки для процессов службы). В systemd нет высокоуровневой настройки для этого значения. Однако вы можете задать его, используя низкоуровневую настройку ControlGroupAttribute=:


.include /usr/lib/systemd/system/httpd.service

[Service]
ControlGroupAttribute=memory.swappiness 70


Как обычно, явное указание настройки, относящейся к какому-либо контроллеру (в нашем случае memory) приведет к автоматическому созданию группы в иерархии данного контроллера.
В дальнейшем, возможно, мы расширим возможности высокоуровневой настройки различных параметров контрольных групп. Если вы часто пользуетесь какими-то из них и полагаете, что для них можно добавить соответствующие опции — не стесняйтесь обращаться к нам. А лучше всего — присылайте сразу патч!
Предупреждение: Обратите внимание, что использование некоторых контроллеров может сильно сказаться на производительности системы. Это та цена, которую приходится платить за контроль над ресурсами. Использование таких контроллеров может ощутимо замедлить некоторые операции. В частности, весьма нелестная в этом плане репутация закрепилась за контроллером memory (хотя, не исключено, что эта проблема уже исправлена в свежих выпусках ядра).
Для углубленного изучения темы, затронутой в этой статье, вы можете обратиться к документации по поддерживаемым настройкам юнитов, а также по контроллерам cpu, memory и blkio.
Стоит подчеркнуть, что мы сейчас обсуждали распределение ресурсов между службами. В дополнение к этим современным механизмам, systemd также поддерживает и традиционные настройки, касающиеся распределения ресурсов между отдельными процессами. Хотя такие настройки обычно наследуются порожденными процессами, они, тем не менее, все равно ограничивают ресурсы на уровне отдельных процессов. В частности, к ним относятся IOSchedulingClass=, IOSchedulingPriority=, CPUSchedulingPolicy=, CPUSchedulingPriority=, CPUAffinity=, LimitCPU= и т.п. Для их работы не требуют контроллеры cgroups, и они не так сильно ухудшают производительность. Возможно, мы рассмотрим их в последующих статьях.

Содержание
Назад - Работа с Journal


Перейти в тематический каталог