Linux cgroup что это

Обновлено: 02.07.2024

Управлять рядом ограничений для выбранных множеств процессов в системах на основе ядра Linux можно используя средства Cgroups — групп управления (англ. control groups .) В данном разделе мы рассмотрим примеры команд, позволяющих осуществить такое управление.

Содержание

Проверить наличие активных иерархий можно следующей командой:

Создать такую иерархию можно командами mkdir и mount , подобно:

(При необходимости управлять или учитывать использование также и других ресурсов в рамках данной иерархии, после -o следует перечислить все соответствующие подсистемы, например: -o blkio,cpuacct,memory .)

Перед этим, однако, следует удостовериться в наличии директории /sys/fs/cgroup и, при необходимости, создать ее, подобно:

Отметим, что использование Cgroups для ограничения используемой памяти предполагает некоторые накладные расходы независимо от фактического использования данной функции. Чтобы их избежать, поддержка подсистемы memory по-умолчанию отключена; ее включение требует явного указания параметра cgroup_enable=memory в командной строке ядра. (Для изменения последней, в свою очередь, необходима перезагрузка системы.)

Информацию о поддерживаемых используемой сборкой ядра и доступных непосредственно в текущий момент параметрах Cgroups можно найти в файле /proc/cgroups . Для решаемой задачи, в поле enabled для подсистемы memory должно присутствовать ненулевое значение.

Напомним также, что копия действующей командной строки ядра отражается в файл /proc/cmdline (должен присутствовать параметр cgroup_enable=memory ); поддерживаемые типы ФС перечислены в файле /proc/filesystems (должен присутствовать тип cgroup .)

«Корень» созданной иерархии /sys/fs/cgroup/rg42 включает все процессы системы. Для того, чтобы установить ограничения только для конкретных процессов, следует создать для них отдельную группу в иерархии, подобно:

Перенести выбранные процессы в эту группу можно просто записав их идентификаторы (pid) в файл tasks созданной директории:

«Возвращение» процессов в «корневую» группу (и, тем самым, — снятие ограничений) выполняется совершенно аналогично:

Список групп, к которым отнесен данный процесс в настоящий момент (каждая иерархия позволяет отнести процесс к одной и только одной группе), можно найти в файле cgroup директории процесса:

Ограничения памяти для группы определяются содержимым файлов limit_in_bytes и soft_limit_in_bytes — для оперативной памяти, и memory.memsw.limit_in_bytes — для полного объема виртуальной памяти. Получить эти ограничения можно подобно:

Компьютеры – это «железо». И сегодня мы вернулись в исходную точку, в том смысле, что сейчас редко найдешь физический хост, на котором выполняется одна единственная задача. Даже если на сервере крутится только одно приложение, оно, скорее всего, состоит из нескольких процессов, контейнеров или даже виртуальных машин (ВМ), и все они работают на одном сервере. Red Hat Enterprise Linux 7 неплохо справляется с распределением системных ресурсов в таких ситуациях, но по умолчанию ведет себя как добрая бабушка, угощающая внуков домашним пирогом и приговаривающая: «Всем поровну, всем поровну».


В теории принцип «всем поровну», конечно, прекрасен, но на практике некоторые процессы, контейнеры или ВМ оказываются важнее других, и, следовательно, должны получать больше.

В Linux уже давно есть средства управления использованием ресурсов (nice, ulimit и прочее), однако с появлением Red Hat Enterprise Linux 7 и systemd у нас наконец-то появился мощный набор таких инструментов, встроенный в саму ОС. Дело в том, что ключевой компонент systemd – это уже готовый, настроенный набор cgroups, который в полной мере задействуется на уровне ОС.

Хорошо, а что это вообще за cgroups, и причем здесь управление ресурсами или производительностью?

Контроль на уровне ядра

Начиная с вышедшей в январе 2008 года версии 2.6.24, в ядре Linux появилось то, что изначально было придумано и создано в Google под именем «process containers», а в Linux стало называться «control groups», сокращенно cgroups. Вкратце, cgroups – это механизм ядра, позволяющий ограничивать использование, вести учет и изолировать потребление системных ресурсов (ЦП, память, дисковый ввод/вывод, сеть и т. п.) на уровне коллекций процессов. Cgroups также могут замораживать процессы для проверки и перезапуска. Контроллеры cgroups впервые появились в 6-й версии Red Hat Enterprise Linux, но там их надо было настраивать вручную. А вот с приходом Red Hat Enterprise Linux 7 и systemd преднастроенный набор cgroups идет уже в комплекте с ОС.

Все это работает на уровне ядра ОС и поэтому гарантирует строгий контроль над каждым процессом. Так что теперь какому-нибудь зловреду крайне сложно нагрузить систему так, чтобы она перестала реагировать и зависла. Хотя, конечно, багованный код с прямым доступом к «железу» (например, драйверы), все еще на такое способен. При этом, Red Hat Enterprise Linux 7 предоставляет интерфейс для взаимодействия с cgroups, и вся работа с ними в основном ведется через команду systemd.

Свой кусок пирога

На диаграмме ниже, напоминающей нарезанный пирог, представлены три cgroups, которые по умолчанию есть на сервере Red Hat Enterprise Linux 7 – System, User и Machine. Каждая из этих групп называется «слайс» (slice – сектор). Как видно на рисунке, каждый слайс может иметь дочерние секторы-слайсы. И, как и в случае с тортом, в сумме все слайсы дают 100% соответствующего ресурса.


Теперь рассмотрим несколько концепций cgroups на примере процессорных ресурсов.

На рисунке выше видно, что процессорное время поровну делится между тремя слайсами верхнего уровня (System, User и Machine). Но так происходит только под нагрузкой. Если же какой-то процесс из слайса User попросит 100% процессорных ресурсов, и никому больше эти ресурсы в данный момент не нужны, то он получит все 100% процессорного времени.

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

  • System – демоны и сервисы.
  • User – пользовательские сеансы. Каждый пользователь получает свой дочерний слайс, причем все сеансы с одинаковым UID «живут» в одном и том же слайсе, чтобы особо ушлые умники не могли получить ресурсов больше положенного.
  • Machine – виртуальные машины, типа KVM-гостей.
  • Слайс System получает 33,333% процессорного времени и поровну делит его между четырьмя демонами, что дает каждому из них по 8,25% ресурсов ЦП.
  • Слайс User получает 33,333% процессорного времени и делит его между двумя пользователями, каждый из которых имеет по 16,5% ресурсов ЦП. Если пользователь mrichter выйдет из системы или остановит все свои запущенные процессы, то пользователю dorf станет доступно 33% ресурсов ЦП.
  • Слайс Machine получает 33,333% процессорного времени. Если выключить ВМ или перевести ее в холостой режим, то слайсы System и User будут получать примерно по 50 % ресурсов ЦП, которые затем поделятся между их дочерними слайсами.

Выкручиваем на полную!

Как можно поменять настройки слайсов?

Для этого у каждого слайса есть настраиваемые свойства. И поскольку это Linux, мы можем вручную прописывать настройки в файлах конфигураций или же задавать из командной строки.

Во втором случае используется команда systemctl set-property. Вот что будет на экране, если набрать эту команду, добавить в конце имя слайса (в нашем случае User) и затем нажать клавишу Tab для отображения опций:


Не все свойства на этом скриншоте являются настройками cgroup. Нас в основном интересуют те, что начинаются на Block, CPU и Memory.

Если вы предпочитаете не командную строку, а config-файлы (например, для автоматизированного развертывания на нескольких хостах), то тогда придется заняться файлами в папке /etc/systemd/system. Эти файлы автоматически создаются при установке свойств с помощью команды systemctl, но их также можно создавать в текстовом редакторе, штамповать через Puppet или даже генерировать скриптами на лету.

Итак, с базовыми понятиями cgroups все должно быть ясно. В следующий раз пройдем по некоторым сценариям и посмотрим, как изменения тех или иных свойств влияют на производительность.

А буквально завтра приглашаем всех на Red Hat Forum Russia 2018 – будет возможность задать вопросы напрямую инженерам Red Hat.

Другие посты по cgroups из нашей серии «Борьба за ресурсы» доступны по ссылкам:

механизмы контейнеризации

Продолжаем цикл статей о механизмах контейнеризации. В прошлый раз мы говорили об изоляции процессов с помощью механизма «пространств имён» (namespaces). Но для контейнеризации одной лишь изоляции ресурсов недостаточно. Если мы запускаем какое-либо приложение в изолированном окружении, мы должны быть уверены в том, что этому приложению выделено достаточно ресурсов и что оно не будет потреблять лишние ресурсы, нарушая тем самым работу остальной системы. Для решения этой задачи в ядре Linux имеется специальный механизм — cgroups (сокращение от control groups, контрольные группы). О нём мы расскажем в сегодняшней статье.

Тема cgroups сегодня особенно актуальна: в ядро версии 4.5, вышедшей в свет в январе текущего года, была официально добавлена новая версия этого механизма — group v2.
В ходе работы над ней cgroups был по сути переписан заново.

Почему потребовались столь радикальные изменения? Чтобы ответить на этот вопрос, рассмотрим в деталях, как была реализована первая версия cgroups.

Cgroups: краткая история

Разработка cgroups была начата в 2006 году сотрудниками Google Полом Менеджем и Рохитом Сетом. Термин «контрольная группа» тогда ещё не использовался, а вместо него употреблялся термин «контейнеры процессов» (process containers). Собственно, сначала они и не ставили перед собой цели создать cgroups в современном понимании. Изначальный замысел был гораздо скромнее: усовершенствовать механизм cpuset, предназначенный для распределения процессорного времени и памяти между задачами. Но со временем всё переросло в более масштабный проект.

В конце 2007 года название process containers было заменено на control groups. Это было сделано, чтобы избежать разночтений в толковании термина «контейнер» (в то время уже активно развивался проект OpenVZ, и слово «контейнер» стало употребляться в новом, современном значении).

В 2008 году механизм cgroups был официально добавлен в ядро Linux (версия 2.6.24). Что нового появилось в этой версии ядра по сравнению с предыдущими?

Ни одного системного вызова, предназначенного специально для работы с cgroups, добавлено не было. В числе главных изменений следует назвать файловую систему cgroups, известную также под названием cgroupfs.

В init/main.c были были добавлены отсылки к функциям для активации cgoups во время загрузки: cgroup_init и cgroup_init_early. Были незначительно изменены функции, используемые для порождения и завершения процесса — fork() и exit().

В виртуальной файловой системе /proc появились новые директории: /proc//сgroup (для каждого процесса) и /proc/cgroups (для системы в целом).

Архитектура

Механизм cgroups состоит из двух составных частей: ядра (cgroup core) и так называемых подсистем. В ядре версии 4.4.0.21 таких подсистем 12:

    — устанавливает лимиты на чтение и запись с блочных устройств; — генерирует отчёты об использовании ресурсов процессора; — обеспечивает доступ процессов в рамках контрольной группы к CPU; — распределяет задачи в рамках контрольной группы между процессорными ядрами; — разрешает или блокирует доступ к устройствам; — приостанавливает и возобновляет выполнение задач в рамках контрольной группы — активирует поддержку больших страниц памяти для контрольных групп; — управляет выделением памяти для групп процессов; — помечает сетевые пакеты специальным тэгом, что позволяет идентифицировать пакеты, порождаемые определённой задачей в рамках контрольной группы; — используется для динамической установки приоритетов по трафику; — используется для ограничения количества процессов в рамках контрольной группы.

Вывести список подсистем на консоль можно с помощью команды:

  • cgroup.clone_children — позволяет передавать дочерним контрольным группам свойства родительских;
  • tasks — содержит список PID всех процессов, включённых в контрольные группы;
    cgroup.procs — содержит список TGID групп процессов, включённых в контрольные группы;
  • cgroup.event_control — позволяет отправлять уведомления в случае изменения статуса контрольной группы;
  • release_agent — содержится команда, которая будет выполнена, если включена опция notify_on_release. Может использоваться, например, для автоматического удаления пустых контрольных групп;
  • notify_on_release — содержит булеву переменную (0 или 1), включающую (или наоборот отключающую), выполнение команду, указанной в release_agent.

У каждой подсистемы имеются также собственные управляющие файлы. О некоторых из них мы расскажем ниже.

Чтобы создать контрольную группу, достаточно создать вложенную директорию в любой из подсистем. В эту вложенную директорию будут автоматически добавлены управляющие файлы (ниже мы расскажем об этом более подробно). Добавить процессы в группу очень просто: нужно просто записать их PID в управляющий файл tasks.

Совокупность контрольных групп, встроенных в подсистему, называется иерархией.Попробуем разобрать принципы функционирования cgroups на простых практических примерах.

Иерархия cgroups: практическое знакомство

Пример 1: управление процессорными ресурсами

С помощью этой команды мы создали контрольную группу, в которой содержатся следующие управляющие файлы:

Пока что в нашей группе никаких процессов нет. Чтобы добавить процесс, нужно записать его PID в файл tasks, например:

Cимволами $$ обозначается PID процесса, выполняемого текущей командной оболочкой.

Этот процесс не закреплён ни за одним ядром CPU, что подтверждает следующая команда:

Вывод этой команды показывает, что для интересующего нас процесса доступны 2 ядра CPU с номерами 0 и 1.

Попробуем «привязать» этот процесс к ядру с номером 0:

Проверим, что получилось:

Пример 2: управление памятью

Встроим созданную в предыдущем примере группу ещё в одну подсистему:

Попробуем ограничить для контрольной группы group0 потребление памяти. Для этого нам понадобится прописать соответствующий лимит в файле memory.limit_in_bytes:

Механизм cgroups предоставляет очень обширные возможности управления памятью. Например, с его помощью мы можем оградить критически важные процессы от попадания под горячую руку OOM-killer’a:

Если мы поместим в отдельную контрольную группу, например, ssh-демон и отключим для этой группы OOM-killer, то мы можем быть уверены в том, что он не будет «убит» при преувеличении потребления памяти.

Пример 3: управление устройствами

Добавим нашу контрольную группу ещё в одну иерархию:

По умолчанию у группы нет никаких ограничений доступа к устройствам:

Попробуем выставить ограничения:

Эта команда включит устройство /dev/null в список запрещённых для нашей контрольной группы. Мы записали в управляющий файл строку вида ‘c 1:3 rmw’. Сначала мы указываем тип устройства — в нашем случае это символьное устройство, обозначаемое буквой с (сокращение от character device). Два других типа устройств — это блочные (b) и все возможные устройства (а). Далее следуют мажорный и минорный номера устройства. Узнать номера можно с помощью команды вида:

Вместо /dev/null, естественно, можно указать любой другой путь. Вывод этой команды выглядит так:

Первая цифра в выводе — это мажорный, а вторая — минорный номер.

Три последние буквы означают права доступа: r — разрешение читать файлы с указанного устройства, w — разрешение записывать на указанное устройство, m — разрешение создавать новые файлы устройств.

С устройством /dev/null мы никак взаимодействовать не можем, потому что доступ закрыт.

В результате выполнения этой команды в файл /sys/fs/cgroup/devices/group0/devices.allow будет добавлена запись a *:* rwm, и все ограничения будут сняты.

Cgroups и контейнеры

Из приведённых примеров понятно, в чём заключается принцип работы cgroups: мы помещаем определённые процессы в группу, которую затем «встраиваем» в подсистемы. Разберём теперь более сложные примеры и рассмотрим, как cgroups используются в современных инструментах контейнеризации на примере LXC.

Установим LXC и создадим контейнер:

Посмотрим, что изменилось в директории cgroups после создания и запуска контейнера:

Как видим, в каждой иерархии появилась директория lxc, которая в свою очередь содержит поддиректорию Ubuntu. Для каждого нового контейнера в директории lxc будет создаваться отдельная поддиректория. PID всех запускаемых в этом контейнере процессов будут записываться в файл /sys/fs/cgroup/cpu/lxc/[имя контейнера]/tasks

Выделять ресурсы для контейнеров можно как с помощью управляющих файлов cgroups, так и с помощью специальных команд lxc, например:

Аналогичным образом дело обстоит с контейнерами Docker, systemd-nspawn и другими.

Недостатки cgroups

Если мы, например, создаём вложенную контрольную группу, то в некоторых подсистемах настройки родительской группы наследуются, а в некоторых — нет.

Об устранении этих и других недостатков cgroups разговоры в сообществе разработчиков ядра шли очень давно: один из первых текстов на эту тему датируется началом 2012 года.

Автор этого текста, инженер Facebook Течжен Хе, прямо указал, что главная проблема cgroups заключается в неправильной организации, при которой подсистемы подключаются к многочисленным иерархиям контрольных групп. Он предложил использовать одну и только одну иерархию, а подсистемы добавлять для каждой группы отдельно. Такой подход повлёк за собой серьёзные изменения вплоть до смены названия: механизм изоляции ресурсов теперь называется cgroup (в единственном числе), а не cgroups.

Разберёмся более подробно в сути реализованных нововведений.

Cgroup v2: что нового

Как уже было отмечено выше, сgroup v2 был включён в ядро Linux начиная с версии ядра 4.5. При этом старая версия поддерживается тоже. Для версии 4.6 уже существует патч, с помощью которого можно отключить поддержку первой версии при загрузке ядра.

На текущий момент в cgroup v2 можно работать только с тремя подсистемами: blkio, memory и PID. Уже появились (пока что в тестовом варианте) патчи, позволяющие управлять ресурсами CPU.

Cgroup v2 монтируется при помощи следующей команды:

Предположим, мы смонтировали cgroup 2 в директорию /cgroup2. В этой директории будут автоматически созданы следующие управляющие файлы:

  • cgroup.controllers — содержит список поддерживаемых подсистем;
  • cgroup.procs — по завершении монтирования содержит список всех выполняемых процессов в системе, включая процессы-зомби. Если мы создадим группу, то для неё тоже будет создан такой файл; он будет пустым, пока в группу не добавлены процессы;
  • cgroup.subtree_control — содержит список подсистем, активированных для данной контрольной группы; по умолчанию пуст.

Эти же самые файлы создаются в каждой новой контрольной группе. Также в группу добавляется файл cgroup.events, который в корневой директории отсутствует.

Новая группа создаётся так:

Чтобы добавить для группы подсистему, нужно записать имя этой подсистемы в файл cgroup.subtree_control:

Для удаления подсистемы используется аналогичная команда, только на место плюса ставится минус:

Когда для группы активируется подсистема, в ней создаются дополнительные управляющие файлы. Например, после активации подсистемы PID в директории появятся файлы pids.max и pids.current. Первый из этих файлов используется для ограничения числа процессов в группе, а второй — содержит информацию о числе процессов, включённых в группу на текущий момент.

Внутри уже имеющихся групп можно создавать подгруппы:

Все подгруппы наследуют характеристики родительской группы. В только что приведённом примере подсистема PID будет активирована как для группы group1, так и для обеих вложенных в неё подгрупп; в них также будут добавлены файлы pids.max и pids.current. Сказанное можно проиллюстрировать с помощью схемы:


Чтобы избежать недоразумений с вложенными группами (см. выше), в cgroup v2 действует следующее правило: нельзя добавить процесс во вложенную группу, если в ней уже активирована какая-либо подсистема:


В первой версии cgroups процесс мог входить в несколько подгрупп одновременно, если эти подгруппы входили в разные иерархии, встроенные в разные подсистемы. Во второй версии один процесс может принадлежать только к одной подгруппе, что позволяет избежать путаницы.

Заключение

В этой статье мы рассказали, как устроен механизм cgroups и какие изменения были внесены в его новую версию. Если у вас есть вопросы и дополнения — добро пожаловать в комментарии.

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

    — документация первой версии cgroups; — документация cgroup v2; — лекция Течжена Хе о нововведениях cgroup v2; — презентация доклада о cgroup v2 с подробными разъяснениями всех нововведений и изменений.

Если вы по тем или иным причинам не можете оставлять комментарии здесь, добро пожаловать в наш корпоративный блог.

cgroups (англ. control group) — механизм ядра Linux, который ограничивает и изолирует вычислительные ресурсы (процессорные, сетевые, ресурсы памяти, ресурсы ввода-вывода) для групп процессов. Механизм позволяет образовывать иерархические группы процессов с заданными ресурсными свойствами и обеспечивает программное управление ими.

Содержание

История

Разработка была начата инженерами Google Полом Менэджем (Paul Menage) и Рохитом Сетом (Rohit Seth) в 2006 году и первоначально называлась «контейнеры процессов» (англ. process containers). В 2007 году проект был переименован в сgroups (от англ. control groups) по причине неоднозначности значения термина «контейнер» в ядре Linux.

Начиная с версии 2.6.24 ядра Linux технология включена в официальные версии ядра. С этого момента разработка значительно активизировалась, в механизм добавлено много дополнительных возможностей, механизм существенным образом используется в технологии инициализации systemd, а также является ключевым элементом в реализации системы виртуализации на уровне операционной системы LXC.

Возможности

Механизм предоставляет следующие возможности:

  • ограничение ресурсов (англ. resource limiting): использование памяти, в том числе виртуальной;
  • приоритезацию: разным группам можно выделить разное количество процессорного ресурса и пропускной способности подсистемы ввода-вывода;
  • учёт: подсчёт затрат тех либо иных ресурсов группой;
  • изоляцию: разделение пространств имён для групп таким образом, что одной группе недоступны процессы, сетевые соединения и файлы другой;
  • управление: приостановку (freezing) групп, создание контрольных точек (checkpointing) и их перезагрузку.

Подключение

Для управления cgroups используется «виртуальная» файловая система (ФС) с идентификатором типа cgroup , подключаемая, как правило, к директориям иерархии /sys/fs/cgroup . Каждое такое подключение соответствует отдельной и независимой иерархии групп управления.

Проверить наличие активных иерархий можно следующей командой:

В данном примере, в системе активна единственная иерархия, управляемая через файлы директории /sys/fs/cgroup/rg42 , и позволяющая ограничивать использование памяти процессами, — на что указывает параметр memory .

Создать такую иерархию можно командами mkdir и mount , подобно:

(При необходимости управлять или учитывать использование также и других ресурсов в рамках данной иерархии, после -o следует перечислить все соответствующие подсистемы, например: -o blkio,cpuacct,memory .)

Перед этим, однако, следует удостовериться в наличии директории /sys/fs/cgroup и, при необходимости, создать ее, подобно:

Отметим, что использование Cgroups для ограничения используемой памяти предполагает некоторые накладные расходы независимо от фактического использования данной функции. Чтобы их избежать, поддержка подсистемы memory по-умолчанию отключена; ее включение требует явного указания параметра cgroup_enable=memory в командной строке ядра. (Для изменения последней, в свою очередь, необходима перезагрузка системы.)

Информацию о поддерживаемых используемой сборкой ядра и доступных непосредственно в текущий момент параметрах Cgroups можно найти в файле /proc/cgroups . Для решаемой задачи, в поле enabled для подсистемы memory должно присутствовать ненулевое значение.

Напомним также, что копия действующей командной строки ядра отражается в файл /proc/cmdline (должен присутствовать параметр cgroup_enable=memory ); поддерживаемые типы ФС перечислены в файле /proc/filesystems (должен присутствовать тип cgroup .)

Примеры

Заморозка процессов

В cgroups существует возможность создания "специального изолятора" для процессов выполнение которых требуется немедленно приостановить. Подсистема, отвечающая за это, называется freezer . Пользоваться ей очень просто:

Запускаем процесс съедающий все ядра процессора в группе perl подсистемы freezer:

Читайте также: