Говоря о контрольных группах, пользователи Red Hat часто задают один и тот же вопрос: «У меня есть приложение, которое очень чувствительно к задержкам.
Можно ли с помощью cgroups изолировать это приложение от остальных, привязав его к конкретным ядрам процессораЭ»
Конечно вы можете.
В противном случае мы бы не выбрали этот вопрос темой сегодняшней статьи.
В детстве нам часто говорили, что делиться — это хорошо и правильно.
По большому счету это правда.
Но есть исключения.
Как мы писали в первый пост в этой серии По умолчанию Red Hat Enterprise Linux 7 ведет себя как сферическая добрая бабушка.
В том смысле, что пытается справедливо распределить системные ресурсы между всеми, кто их попросит. Однако в реальной жизни у бабушек есть любимчики, которые получают больше.
В переводе на язык системного администратора это означает, что бывают ситуации, когда одни приложения или службы важнее других, поэтому им нужно уделять все возможное внимание, чтобы они реагировали как можно лучше.
В Red Hat Enterprise Linux 7 это делается в два этапа:
- Мы изолируем некоторые ядра процессора, чтобы передать их в эксклюзивное пользование такому приложению.
- Мы создаем cgroups и юнит-файлы, которые привязывают это приложение к изолированным ядрам.
Небольшое отступление относительно примеров из этих постов
В Hat Enterprise Linux 7.4 изменился механизм работы с кратковременными слайсами, такими как пользовательские сессии.В результате они больше не могут изменять настройки cgroup на лету, вносить постоянные изменения в конфигурацию или создавать встраиваемые файлы с помощью команды systemctl set-property. Да, обидно, но так решило сообщество разработчиков Linux. Хорошей новостью является то, что эти изменения не коснулись услуг.
То есть если приложения запускаются и останавливаются через юнит-файлы (работающие как демоны), то все наши примеры работают. Также можно создавать свои собственные контрольные группы, используя древние инструменты, такие как cgcreate и cgset, а затем помещать в эти группы пользовательские сеансы и процессы, чтобы использовать общие ресурсы ЦП и другие регуляторы.
В жизни все меняется, поэтому нам остается только адаптироваться и изобретать новые техники.
Теперь перейдем к сегодняшней теме.
Организуем сепаратизм с помощью isolcpus
Одним из наиболее важных компонентов ядра Linux является планировщик процессов (диспетчер).Если глубже, процесс — это исполняемый код, который является частью приложения или службы.
По сути, процесс состоит из ряда инструкций, которые выполняет компьютер для выполнения той или иной работы, будь то наблюдение за кошками или что-то более серьезное.
Выполнение этих инструкций осуществляется центральным процессором, также известным как ЦП.
На современных компьютерах ЦП обычно состоит из нескольких процессоров, называемых ядрами.
По умолчанию планировщик рассматривает каждое ядро процессора как один из своих исполнительных блоков, которому он назначает новые процессы по мере их появления.
При этом планировщик старается более-менее равномерно распределить возникающие процессы между ядрами с учетом нагрузки.
К сожалению, планировщик не может сказать, что этот конкретный процесс в конечном итоге породит целую группу процессов, и эту группу нужно будет выполнять изолированно от других процессов, в том смысле, что у них не должно быть общих процессорных ядер.
Поэтому нам нужно как-то сказать планировщику, чтобы он не трогал некоторые ядра процессора, то есть не давал им никаких случайных процессов.
А потом мы сами (или с помощью какого-то другого процесса) принудительно посадим на эти изолированные от планировщика ядра те процессы, которые считаем нужными.
Это можно сделать с помощью опции isolcpus в строке загрузки ядра в файле конфигурации grub. В примере ниже у нас есть машина с четырьмя ядрами, на которой есть два файла grub: один находится в /etc/default и называется grub.noiso (это резервная копия конфигурации по умолчанию), а второй — там же и называется просто grub, так что подхватил grub2-mkconfig. Этот второй файл отредактирован, чтобы изолировать ядра 1–3 от планировщика процессов.
ПРИМЕЧАНИЕ.
В Red Hat Enterprise Linux 7 никогда не следует вручную изменять файл grub.conf в папке /boot. Вместо этого внесите необходимые изменения в /etc/default/grub, а затем пересоберите файл grub.conf с помощью соответствующей утилиты, например:
При использовании параметра isolcpus необходимо перечислить освобождаемые ядра процессора через запятую, нумерацию начиная с 0. После перезагрузки системы планировщик процессов не будет использовать эти ядра ни для чего, за исключением определенных системных процессы, которые ДОЛЖНЫ БЫТЬ на каждом ядре.
Чтобы проверить, сработал ли наш метод, запустим несколько процессов загрузки, а затем посмотрим загрузку каждого ядра с помощью команды top.
Как видите, все процессы загрузки находились на CPU 0, а не были равномерно распределены по всем четырем ядрам.
Это означает, что мы правильно указали параметр загрузки.
Связывание процессов с ядрами с помощью cpuset
Теперь давайте перейдем к делу чего лучше не делать, если вы не понимаете, зачем вы это делаете, а какие лучше внедрять в производство только после тщательного тестирования .Почему эти предупреждения? Вплоть до того, что мы будем делать, в общем-то, простые вещи, используя инструментарий libcgroup, о котором мы писали в прошлом посте.
Если вы помните, это всего лишь набор команд для создания, изменения и уничтожения cgroups. На самом деле они являются частью Red Hat Enterprise Linux 6, но их также можно установить и в Red Hat Enterprise Linux 7, хотя не исключено, что в будущем эта опция исчезнет. Кратко напомним основные рекомендации по использованию libcgroup:
- Используйте systemd для управления теми контроллерами cgroup, которые контролируются самим systemd (это процессор, память и блочный ввод-вывод).
- Используйте инструменты libcgroup для управления всеми остальными контроллерами cgroup.
- Будьте очень осторожны с непредвиденными последствиями своих действий.
Это наиболее распространенные ядра процессора; они могут либо находиться под управлением планировщика процессов (так настроена система по умолчанию), либо, наоборот, изолированы от планировщика (как мы это сделали в примере выше).
Давайте проверим каталог файловой системы /sys/fs/cgroup в нашей примерной системе.
Как видите, каталог cpuset уже существует, поскольку этот контроллер является частью ядра (хотя он и не находится под контролем systemd).
Однако групп в нем пока нет, поэтому мы видим в этом каталоге только настройки по умолчанию.
Давайте проверим, что на нашей машине установлен инструментарий libcgroup:
Если он не установлен, то это легко исправить командой yum install libcgroup; вам даже не нужна перезагрузка.
Теперь давайте создадим процессорный набор.
Для этого мы воспользуемся следующими командами, чтобы создать новую контрольную группу для процессорного набора и установить ее свойства:
Команда Cgcreate создает контрольную группу под названием testset и помещает ее в контроллер процессорного набора.
Затем мы назначаем этому новому процессорному набору третье ядро нашей виртуальной машины и выделяем ему зону NUMA 0. Даже если ваша система не использует NUMA (а наша нет), зону все равно нужно прописать, иначе нельзя будет назначать задачи в cgroup. Теперь проверим, что в файловой системе создан каталог testset, и посмотрим, что в нем находится.
Как видите, наши изменения вступили в силу, но на этом процессорном наборе пока не запущен ни один процесс.
Как сюда поместить процесс? Это можно сделать несколькими способами:
- В задачи можно ввести PID существующего процесса.
Это работает, но это не очень красиво.
- Вы можете использовать cgexec и указать группу при запуске процесса.
Это работает, если приложение не является демоном; к тому же все это можно красиво прописать в скрипте запуска приложения.
- Для приложения, которое работает как демон под systemd, вы можете создать служебный файл.
Мы запустили foo.exe, который, в свою очередь, запустил дочерний процесс, который ничего не делает, а только активно загружает процессор.
Опция --sticky в команде cgexec говорит, что «любой дочерний процесс должен оставаться в той же контрольной группе, что и родительский процесс».
Так что это важный вариант, о котором следует помнить.
Теперь мы видим, что в нашей контрольной группе запущены два процесса, и мы знаем их PID. Давайте посмотрим сверху:
Как видите, CPU 3 сейчас загружен под завязку, а остальные остывают.
А вот как выглядит юнит-файл для запуска того же приложения, что и служба systemd:
Файл модуля содержит три команды ExecStartPre, которые выполняют настройки, которые мы уже сделали вручную.
Далее идет команда ExecStart, которая запускает приложение.
А когда приложение останавливается, команда ExecStopPost очищает за собой, удаляя cgroup.
Как видите, в последнем примере мы создали новую контрольную группу под названием set1. Мы сделали это, чтобы показать, что можно иметь несколько активных контрольных групп, использующих один и тот же процессор.
Некоторые могут найти это полезным, но другие могут сбить с толку.
Ну что, все работает? Похоже, да!
Теперь давайте закроем наш сервис и проверим, что контрольная группа уничтожена:
ВНИМАНИЕ: группы, созданные с помощью cgcreate, не сохраняются после перезагрузки.
Поэтому создание таких групп необходимо указать в сценариях запуска и файлах модулей.
Итак, теперь в вашем арсенале есть еще пара инструментов для работы с контрольными группами.
Надеемся, они будут полезны! Другие статьи о группах из нашей серии «Борьба за ресурсы» доступны по следующим ссылкам:
- Часть 1
- Часть 2
- Часть 3
- Часть 4
- Часть 5
-
Вьет-Муонгские Языки
19 Oct, 24 -
Интернет Может Рухнуть. Нам Нужен «План Б»
19 Oct, 24