Написание `apply`-скрипта ========================= В парадигме, используемой фреймворком ``ram`` для написания конфигурационных юнитов действия сохранения настроек (`store`) и применения настроек (`apply`) разделены. Смысл действия ``apply`` -- примененить сохраненную конфигурацию к запущенной системе без перезагрузки. Чаще всего это означает перезапуск соответствующих сервисов. Для удобства действия ``apply`` могут быть реализованы с помощью языка сценариев shell. 1) Перезапуск сервисов: ~~~~~~~~~~~~~~~~~~~~~~~ В качестве первоначальной реализации сервиса ``apply`` можно использовать скрипт безусловно перезапускающий нужный сервис. Например, в системе CentOS-6 сервисы ``sshd`` и ``network`` управляются скриптами sysv с помощью утилиты ``service``. Соответсвенно, скрипты ``apply`` для этих сервисов могут быть реализованы следующим образом: .. sourcecode:: sh #!/bin/sh service sshd restart .. sourcecode:: sh #!/bin/sh service network restart В системе CentOS-7 большинство сервисов управляется с помощью systemd. Тем не менее, systemd поддерживает совместимость и предоставляет команду ``service``. 2) Перезапуск в зависимости от состояния: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Иногда возникают ситуации, когда конфигурация сервиса была изменена, однако на момент изменения конфигурации сервис был выключен. Это могло произойти в результате ручной остановки сервиса для отладки или на постоянной основе для сокращения потребления ресурсов. В зависимости от реализации sysv-скрипта для такого сервиса выполнение команды ``restart`` может привести либо к ошибке, либо к невостребованному запуску сервиса. Для исключения таких ситуаций рекомендуется использовать команду ``condrestart`` соответствующего sysv-скрипта: .. sourcecode:: sh #!/bin/sh service sshd condrestart В случае, если в sysv-скрипте нужного сервиса поддержка команды ``condrestart`` не реализована, можно воспользоваться командой ``status``: .. sourcecode:: sh #!/bin/sh if service sshd status; then service sshd restart fi В случае же, если в sysv-скрипте нужного сервиса не реализована поддержка команды ``condrestart`` и команды ``status`` -- следует проанализировать реализацию сервиса, чтобы определить признаки работы сервиса. Например, в системе CentOS-6 сервис ``network``, хоть и имеет поддержку команды ``status``, последняя не позволяет понять был ли сервис включен или выключен. При этом реализация sysv-скрипта создает файл ``/var/lock/subsys/network`` при запуске сервиса и удаляет его при останове. Т.о. для проверки состояния сервиса ``network`` можно воспользоваться проверкой наличия этого файла: .. sourcecode:: sh #!/bin/sh if [ -f /var/lock/subsys/network ]; then service network restart fi В системе CentOS-7 сервис ``network`` по прежнему реализован в качестве sysv-скрипта и этот код остается функциональным. Однако большинство демонов теперь управляются через systemd юниты. Создание файлов в /var/lock/subsys/ для таких демонов не практикуется. В качестве более переносимого механизма можно ориентироваться на pid-файлы нужных демонов. Для некоторых systemd юнитов потребуются правки для включения pid-файлов. 3) Перезапуск в зависимости от измениний: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Другим аспектом, который следует принять во внимание при написании скриптов ``apply`` -- избыточность перезапуска сервисов в том случае, если конфигурация фактически не была изменена. Особенно важно учитывать этот аспект для сервисов, работа которых не должна прерываться без необходимости. Если нужный сервис поддерживает действие ``reload`` (переконфигурация запущенного демона без перезапуска сервиса), следует воспользоваться этой функциональностью: .. sourcecode:: sh #!/bin/sh if service sshd status; then service sshd reload fi Однако следует убедиться, что переконфигурация запущенного демона корректно работает во всех предусмотренных сценариях. Если же нужный сервис не поддерживает действия ``reload`` или реализация этого действия не удовлетворяет требованиям, можно воспользоваться перезапуском сервиса предварительно проверив работает ли сервис с актуальной конфигурацией. Для примера можно рассмотреть сервис ``network`` и его конфигурационный файл -- ``/etc/sysconfig/network``. С помощью операций ``-ot`` (older than)/``-nt`` (newer than) команды ``test`` можно сравнить даты модификации двух файлов. В случае сервиса ``network`` дата модификации конфигурационного файла сравнивается с датой модификации файла ``/var/lock/subsys/network``, создаваемого при запуске сервиса: .. sourcecode:: sh #!/bin/sh if [ -f /var/lock/subsys/network \ -a /var/lock/subsys/network -ot /etc/sysconfig/network ]; then service network restart fi Следует принять во внимание, что если один из файлов в сравнении не существует -- его время модификации принимается равным 0, если же оба файла учавствующих в сравнении не существуют -- результат сравнения будет не определен (как ``-ot``, так и ``-nt`` вернут ошибку). Важно: проверка необходимости перезапуска сервиса на основе сравнения времени модификации конфигурационных файлов накладывает дополнительные требования к реализации скриптов ``store``. Следует реализовывать эти скрипты таким образом, чтобы при отсутствии логических измений не происходило обновления конфигурационных файлов. Стандартные классы для работы с файлами ``ini`` и ``env`` из библиотеки ``ram.formats`` соответствуют этим требованиям. На самом деле для конфигурации сервиса ``network`` задействовано несколько конфигурационных файлов: .. code:: /etc/sysconfig/network /etc/sysconfig/network-scripts/ifcfg-* /etc/sysconfig/network-scripts/route-* Т.о. для корректной проверки необходимости перезапуска сервиса нужно проверить их все: Помимо файлов необходимо проверить также директорию ``/etc/sysconfig/network-scripts/``, т.к. если какие-то из конфигурационных файлов были удалены -- это отразится на времени изменения директории. Следующий скрипт поочередно проверяет все указанные файлы, до тех пор пока не будет найден хотя бы один удовлетворящий условию -- дата модификации конфигурационного файла больше чем дата модификации файла ``/var/lock/subsys/network``. Если такой файл будет найден, сервис ``network`` будет перезапущен: .. sourcecode:: sh #!/bin/sh if [ ! -f "/var/lock/subsys/network" ]; then exit 0 fi for file in /etc/sysconfig/network \ /etc/sysconfig/network-scripts/ \ $(find /etc/sysconfig/network-scripts/ \ -name 'ifcfg-*' -o -name 'route-*'); do if [ "/var/lock/subsys/network" -ot "${file}" ]; then service network restart break fi done 4) Послесловие ~~~~~~~~~~~~~~ В этом документе описаны методики написания скриптов ``apply`` при использовании скриптов управления сервисами на основе sysv. Тем не менее описанные методики будут актуальны и для других систем управления сервисами (upstart, systemd, monit, ...). В общем случае следует стремиться к тому, чтобы перезапуск сервисов учитывал текущее состояние сервиса и фактическое изменений конфигурационных файлов. Помимо этого, рекомендуется не плодить скрипты ``apply`` управляющие одним и тем же сервисом в разных юнитах, вместо этого рекомендуется написать скрипт обрабатывающий все аспекты управления нужным сервисом и задейстовать его в конкретных юнитов с помощью символических ссылок или вложенного вызова ``apply``.