Борьба за ресурсы, часть 3: Памяти мало не бывает

    Продолжаем изучать Control Groups (Cgroups) в Red Hat Enterprise Linux 7. Займемся памятью. Вы помните, что для распределения процессорного времени есть две регулировки: CPUShares для настройки относительных долей и CPUQuota для того, чтобы ограничивать пользователя, службу или виртуальную машину (ВМ) в абсолютных величинах (процентах) процессорного времени. Причем, обе эти регулировки можно использовать одновременно. Например, если для пользователя задана CPU-квота в 50 %, то его CPU-шара тоже будет приниматься во внимание до тех пор, пока он полностью не выберет свою квоту в 50 % процессорного времени.



    Что касается оперативной памяти, то systemd предлагает только один способ регулировки, а именно…

    Объем памяти, который может быть выделен пользователю или службе. Допустим, мы хотим ограничить пользователя mrichter 200 МБ ОЗУ. Если помните, его UID равен 1000, поэтому мы вводим следующую команду:

    systemctl set-property user-1000.slice MemoryLimit=200M
    

    Теперь mrichter хочет проверить свои границы и запускает утилиту нагрузочного тестирования stress, которая начинает усиленно потреблять память. И stress очень быстро выдает ошибку:


    По системному журналу видно, что stress был попросту прерван OOM (Out Of Memory) Killer.


    Здесь важно обратить внимание вот на что: по умолчанию ограничение на ОЗУ распространяется только на резидентную память. То есть, если процесс может уходить в файл подкачки («своп»), то он обойдет установленное ограничение. В нашем примере stress вылетел потому, что превысил ограничение на резидентную память.

    А если мы не хотим, чтобы программа сливалась в своп?

    Это, в общем-то, легко запретить. Ну или относительно легко… В общем, придется кое-куда залезть.

    Есть такие настройки cgroup, до которых не добраться ни через команду systemctl, ни через юнит-файлы. Однако эти настройки можно менять на лету через файлы в папке /sys/fs/cgroup/. Вот как, к примеру, выглядит cgroup пользователя mrichter в части памяти:


    Файл, отвечающий за то, сколько памяти может уходить в своп, вполне очевидно называется memory.swappiness. Посмотрим, что у него внутри:


    Если вам случалось играться с настройками ядра и подсистемой свопинга, то вы сразу увидите здесь стандартное значение параметра swappiness по умолчанию. Если поменять его на ноль, то ОЗУ-регулятор для пользователя mrichter вообще запретит ему использовать своп.


    Кстати, здесь же можно глянуть статистику памяти для пользователя mrichter:


    Значение параметра hierarchical_memory_limit – это тот самый MemoryLimit, который мы задали командой systemctl. Параметр hierarchical_memsw_limit представляет собой суммарный лимит (резидентная память и память в файле подачки). Мы запретили пользователю mrichter использовать файл подкачки, поэтому значение этого параметра такое странное.

    Теперь о проблемах только что описанного подхода:

    • Вносить изменения в эти файлы можно только тогда, когда пользователь mrichter залогинился в систему. Пока он не войдет, его cgroup будет неактивна.
    • Эти настройки не сохраняются после перезагрузки. Более того, они потеряются, если mrichter перелогинится.

    Справиться с этими проблемами поможет сценарий pam_exec (подробнее см. access.redhat.com/solutions/46199).

    Вот какой сценарий мы создадим в папке /usr/local/bin:


    А затем добавим его вызов в последнюю строку /etc/pam.d/sshd. В результате, этот сценарий будет запускаться при каждом входе пользователя через ssh. Именно поэтому мы и проверяем в сценарии, что это пользователь mrichter, прежде чем менять настройки.


    Итак, мы отрезали пользователя mrichter от файла подкачки.


    Можно конечно пойти еще дальше и менять конфигурационные файлы активной cgroup на лету, но мы пока отложим это рисковое дело. Тем не менее, общий метод, как менять настройки пользователя, вы уловили.

    А со службами все еще проще. В юнит-файле службы можно использовать директиву ExecStartPost=, чтобы запускать сценарий, меняющий настройки. Например, вот как надо изменить юнит-файл службы foo, чтобы выключить свопинг:


    Запускаем foo – и никакого свопинга:


    Ладно, на сегодня, пожалуй, хватит с нас этого шаманства.

    Но прежде чем закончить, давайте остановимся на документации по cgroup, в которой можно найти информацию обо всех этих скрытых настройках регуляторов. Вы можете установить пакет kernel-doc на свой компьютер, как это сделал я, загрузив его из репозитория «rhel-7-server-rpms».


    После установки откройте папку /usr/share/docs, соответствующую вашему ядру, и перейдите в папку cgroups, где и содержится последняя информация по всем регуляторам.


    В следующем раз мы поговорим о вводе-выводе. И, кстати, мы уже почти подошли к тому, чтобы узнать, как cgroups привели к появлению контейнеров (на самом деле cgroups – это ключевой компонент контейнеров в Red Hat Enterprise Linux и Red Hat OpenShift Container Platform).

    Red Hat
    55.98
    Программные решения с открытым исходным кодом
    Share post

    Comments 10

      0

      Будет ли в ограничение по памяти входить шареная память?

        0
        Если я вас правильно понял, то мы же сейчас теплое с мягким сравниваем. В статье речь идет исключительно про ограничения потребления памяти процессом, а не ограничения по shared memory. Алсо, контейнеры могут в shared memory, но должна быть веская причина и производственная необходимость это делать, по крайней мере с позиции обеспечения безопасности. С другой стороны, делить память между контейнерами всяко безопасней, чем делить память на хосте.
          0

          Ага по вашему ответу я и сам понял, что эту тему не до конца понимаю. Как-то мне казалась, что шареная память может быть и за пределами контейнера даже без моего участия: взаимодествие с ядром, библиотеки (или они не попадают в SHARED?) из общего для нескольких контейнеров слоя и т.п.

        0
        Спасибо. Очень интересно и актуально. Мне это нужно для распиливания одной большой ноды, на которой крутится множество сервисов. Хочется увидеть более серьезные примеры: например, есть узел, на котором крутится условная mongo, условный clickhouse и docker c кучей контейнером. БД нужно отдать ХХ и YY ГиБ ОЗУ, а все остальное — докер-контейнерам. Явно, что это сложнее и интереснее, чем устанавливать cgroups ограничения для конкретного пользователя.
          0

          Раз вы упоминаете systemctl, зачем в rhel выпилили возможности создавать, управлять службами пользователя (systemctl --user)?
          Я про те, что размещаются в домашнем каталоге пользователя (~/.config/systemd/user/)

            0
            gecube, именно эти и другие задачи решает Red Hat OpenShift Container Platform, который не просто оркестратор, а полноценная платформа для разработки, развертывания и эксплуатации приложений в контейнерах в физических, виртуальных и общедоступных облачных средах со встроенными средствами кластеризации, планирования и оркестрации для балансировки нагрузки и автомасштабирования. Квотирование и распределение ресурсов, организация совместной работы над проектами тоже включены.

            Из полезных ресурсов:
            1) Интерактивный учебный портал learn.openshift.com
            2) Очень полезная доступная для скачивания книга www.openshift.com/deploying-to-openshift («Learn how to run, access, and manage containers in OpenShift, including how to orchestrate them at scale»)
              0
              redhatrussia мы уже пользуемся OKD. Действительно классная вещь, но порой ее ставить — как из пушки по воробьям. Речь шла про вполне конкретный сценарий «поделить» один сервер cgroups'ами.
            0
            Если я правильно вас понял, логика победы в вашем сценариибудет абсолютно такая же как и в статье. Например…

            Еще можно вынести критические сервисы в виртуалки, тем самым решить вопрос ограничения ресурсов и попутно хоть как-то попробовать закрыть тему HA и DR.
              0
              gecube

              Если я правильно вас понял, логика победы в вашем сценариибудет абсолютно такая же как и в статье. Например…

              Еще можно вынести критические сервисы в виртуалки, тем самым решить вопрос ограничения ресурсов и попутно хоть как-то попробовать закрыть тему HA и DR.
                0
                heysharpy

                Там на виртуалках производительность умрет. И это потребует полной переделки архитектуры. Понятно, что прям продакшн будет грамотно распилен на нужное количество инстансов правильного размера. Но это чутка позже.

                И ещё — спасибо, я гуглить умею. Ещё не говорю о том, что не все приложение корректно работают при ограничении их cgroups

                Only users with full accounts can post comments. Log in, please.