Типичная проблема с которой встречаются администраторы веб-серверов:
На сайт приходит шквал запросов, Apache занимает всю оперативную память и как только залазит в своп - до сайта не достучаться и даже на ssh зайти проблематично. Рано или поздно и swap закончится и... как думаете, что будет делать в ситуации когда программа просит памяти, а памяти нет?
А вот и нет :P, Linux начнет килять все процессы вподряд освобождая память (8.
Есть стандартное средство - ограничение числа запущенных процессов Apache. Только надо заранее прикинуть сколько один процесс ОЗУ сожрет, и оставить 1-2 ГБ запаса. Проблема в том, что сколько апач сожрет памяти заранее может быть неизвестно и это число может плавать. Если ошибся - сервер завис. Если перестраховался - неоптимально расходуешь ресурсы.
Есть уже несколько лет другой способ ограничения ресурсов: cgroups или контрольные группы. Позволяет в весовом эквиваленте ограничивать утилизацию дисков (блочных устройств), памяти, сети, процессора и даже более тонкие штуки, вплоть до контроля в кеше какого ядра процессора держать данные. И привязывается это все на пользователя или на процессы.
Еще один плюс: ограничения работают на все что процесс породит. Т.е. мы можем зарезать Apache по памяти и нам уже не нужно считать сколько потомков он породит и сколько памяти они вместе сожрут.
В RHEL cgroups появились еще в 6 версии, а в 7 уже вовсю используются в системе инициализации systemd. Также на основе cgroups реализуется управление ресурсами в системах виртуаллизации, таких как docker и KVM.
Более подробную информацию можно посмотреть с курсов Яндекса и в офф. руководстве RHEL6.
Ограничение памяти для процесса[править]
Я буду работать, впрочем, с CentOS7 и опущу установку и сразу перейду к настройке. Дело в том, что в RHEL7 предлагается применять cgroups не так как написано в большинстве руководств по cgroups, включая руководство RHEL6. В RHEL7 для запуска используется systemd 205 и управление cgroups рекомендуется осуществлять через systemd, а не всюду описанным cgconfig. При этом именно в systemd 205 была отключена возможность тонкой настройки cgroups через ControlGroupAttribute и т.п. команды :).
Итак, хотим ограничивать процесс. Но в RHEL7 cgroups работают через systemd, а тот работает со службами, а не исполняемыми файлами. Поэтому натравливать cgroups будем на службы systemd:
Итак, набираем
systemctl set-propery <name.service> <name>=<value>
Для ограничения занимаемого объема ОЗУ надо поставить, например, MemoryLimit=4096M
Если применяется php-fpm, то
systemctl set-propery php-fpm.service MemoryLimit=8G
Затем надо выполнить
systemctl default-reload
И перезапустить сервис (т.к. cgroups нельзя применить к уже запущенным процессам)
systemctl stop php-fpm
systemctl start php-fpm
Снижение приоритета процесса[править]
В cgroups есть работоспособный аналог команды nice. Надо установить:
CPUShares=<number>
По умолчанию CPUShares=1024 и приоритет можно понизить установив меньшее значение типа:
systemctl set-propery php-fpm.service MemoryLimit=8G CPUShares=300 systemctl default-reload; systemctl stop php-fpm.service; systemctl start php-fpm.service
Конечно, вам уже хочется узнать какие еще команды есть? Смотрите в руководстве RHEL.
Motivation[править]
Лично я взялся за cgroups не из-за Apache или php-fpm. Начал тестировать кластерную файловую систему GlusterFS и, когда php-fpm выжирает всю память, файловая система начинает падать. Причем довольно хитро, в статусе показывая "все OK", всех пиров в кластере типа вижу, а по факту надо все перезагружать. Дополнительно на этой упавшей файловой системе как раз файлы лежат, необходимые для php-fpm, от чего php-fpm тоже подвисает не освобождая память и ситуация усугубляется.