Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

systemd: преобразование сценариев sysvinit для работы с systemd

Оригинал: systemd: Converting sysvinit scripts
Автор: Jon Stanley
Дата публикации: 4 ноября 2015 г.
Перевод: А.Панин
Дата перевода: 13 ноября 2015 г.

systemd: преобразование сценариев sysvinit для работы с systemd

systemd: преобразование сценариев sysvinit для работы с systemd

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

Сценарии инициализации SysVinit

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

Сценарий, используемый для управления состоянием службы в SysVinit, называется сценарием SysVinit (SysVinit script). При наличии некоторых дополнений он также мог называться сценарием инициализации LSB или сценарием инициализации Linux Standard Base (LSB init script). Не следует беспокоиться относительно объема работы, ведь слой совместимости systemd реализует обратную совместимость с описанными сценариями на 99.9%. Это означает, что вам не придется работать в поте лица, преобразовывая ваши сценарии инициализации для работы в таких основанных на systemd операционных системах, как Fedora. Однако, подобный подход также имеет ряд преимуществ.

Сценарии инициализации по своей сути являются императивными сценариями командной оболочки, исполняемыми интерпретатором. В данном случае слово "императивные" означает то, что они содержат команды, которые должны выполняться системой для управления состоянием службы. Однако, файлы юнитов systemd содержат лишь декларативные объявления. Они сообщают systemd о том, как следует управлять состоянием службы, а systemd осуществляет само управление. В результате файлы юнитов обычно гораздо короче сценариев инициализации SysV, которые они заменяют. Это объясняется тем, что в этих файлах не реализуются какие-либо механизмы для управления состоянием служб, а описывается лишь назначение этих служб.

Первый шаг процесса адаптации сценария инициализации SysV для работы с systemd заключается в исследовании сценария, который необходимо преобразовать. Давайте используем сценарий инициализации sshd из состава Fedora 16 в качестве примера. Вы можете изучить сценарий в том виде, в котором он был до преобразования в файл юнита systemd, перейдя по ссылке выше. Забавный факт: все функции, выполняемые данным 184-строчным сценарием, в данное время выполняются с помощью 27 строк директив, распределенных между двумя файлами юнитов.

Уровни исполнения против целей

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

# chkconfig: 2345 55 25

Первый набор значений, 2345, указывает уровни исполнения (runlevels), на которых данный сценарий должен исполняться в мире SysV. Уровень исполнения SysV является объявлением системного состояния, при достижении которого должны запускаться определенные процессы и службы. Существует четко определенная номенклатура уровней исполнения:

  • 0: отключение системы
  • 1: однопользовательский режим
  • 2: многопользовательский режим
  • 3: многопользовательский режим с поддержкой сети
  • 4: не определено (определяется пользователем)
  • 5: многопользовательский режим с дисплейным менеджером (вход в систему в графическом режиме)
  • 6: перезагрузка

В systemd не существует концепции уровней исполнения. Уровни исполнения заменены на цели (targets). Systemd позволяет создать неограниченное число целей, каждая из которых будет представлена файлом юнита с суффиксом .target. Аналогом уровня исполнения 3 SysV является цель systemd под названием multi-user.target. Аналогично, юнит, соответствующий уровню исполнения 3, носит имя graphical.target.

Цели могут зависеть друг от друга. Поэтому systemd инициирует запуск служб, связанных с целью graphical.target, только после достижения цели multi-user.target. Также существуют явные зависимости от цели basic.target, которая, в соответствии с именем, предназначена для активации системных служб и функций. Некоторые из этих служб и функций активируются с помощью своих собственных целей, таких, как network.target.

Порядок запуска и остановки служб

Следующие два числа в сценарии инициализации SysV, 55 и 25, описывают порядок запуска и остановки службы. В системе инициализации SysVinit сценарии исполняются в строго установленном порядке. Порядок их исполнения обуславливается именами символьных ссылок в специальных директориях. Некорректный порядок запуска или остановки нового сценария может привести к невозможности запуска службы, эффекту, аналогичному зависанию системы, а также к появлению других ошибок.

С другой стороны, такие директивы, как Requires= и After= позволяют гарантированно запускать юниты в корректной последовательности. Они не требуют заблаговременного получения информации о других юнитах или службах, используемых в рамках системы. Например, в том случае, если ваш юнит предназначен для запуска службы, для работы которой необходима сеть, вы можете добавить в него директивы Requires=network.target и After=network.target. В результате служба будет запускаться после активации сети.

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

Слишком много кода

После директив заголовка сценария инициализации, таких, как директивы для описания жестких и мягких зависимостей (Required-Start, Required-Stop, Should-Start и Should-Stop), следует описание службы. В данном случае используется описание службы "Start up the OpenSSH server daemon". Далее размещены реализации различных функций, позволяющие сценарию выполнять предусмотренные стандартом действия. В стандартах описываются определенные действия, которые должны быть реализованы в рамках всех сценариев инициализации, такие, как запуск службы (start), остановка службы (stop) и перезапуск службы (restart).

Как говорилось ранее, systemd использует декларативные описания. Вместо объявления и реализации набора функций вам придется указать лишь параметры процесса, который должен исполняться. При этом нет необходимости, к примеру, в разработке большого объема кода для выполнения таких стандартных операций, как получение идентификатора процесса (PID) запущенной службы.

Давайте рассмотрим реализацию функции start() данного сценария инициализации для того, чтобы иметь представление о выполняемых в рамках нее операциях, исследуя каждую из ее строк. Это сценарий командной оболочки, т.е., последовательность инструкций, которые должны исполняться с помощью командной оболочки, запущенной системой инициализации. Не беспокойтесь, если вы не знаете языка сценариев командной оболочки; назначение каждой из инструкций описано ниже.

start()
{
    [ -x $SSHD ] || exit 5
    [ -f /etc/ssh/sshd_config ] || exit 6
    # Create keys if necessary
    /usr/sbin/sshd-keygen

    echo -n $"Starting $prog: "
    $SSHD $OPTIONS && success || failure
    RETVAL=$?
    [ $RETVAL -eq 0 ] && touch $lockfile
    [ $RETVAL -eq 0 ] && cp -f $XPID_FILE $PID_FILE
    echo
    return $RETVAL
}

А это описание операций, выполняемых системой инициализации:

  • Проверяется наличие бинарного файла sshd, а также возможность его исполнения; при отрицательном результате проверки осуществляется завершение работы сценария инициализации с кодом 5.
  • Проверяется наличие файла конфигурации /etc/ssh/sshd_config; при отрицательном результате проверки осуществляется завершение работы сценария инициализации с кодом 6.
  • Исполняется бинарный файл sshd-keygen с целью генерации ключей, если их еще не существует.
  • На экран/в системный журнал отправляется сообщение о начале процесса запуска службы.
  • Осуществляется запуск службы, заключающийся в исполнении бинарного файла sshd и выводе сообщения об удачном или неудачном запуске.
  • Сохраняется код завершения работы процесса sshd; код завершения 0 говорит о том, что процесс sshd успешно перешел в режим демона.
  • В зависимости от сохраненного кода завершения работы процесса, связанного со службой, обрабатывается несколько файлов-маркеров, указывающих на то, что служба исполняется и предназначенных для корректной обработки тех случаев, когда системный администратор пытается повторно запустить службу.
  • На экран/в системный журнал отправляется символ перехода на следующую строку.
  • Работа сценария завершается возвратом кода завершения работы процесса sshd для тех случаев, когда он может понадобиться где-либо.

Учтите, что это всего лишь одна функция сценария инициализации SysV для службы sshd. Кроме нее необходимо реализовать множество других функций, таких, как функции stop, restart и другие. В итоге файл сценария инициализации будет содержать более ста дополнительных строк кода! Но как в сравнении с данным сценарием выглядит юнит-файл systemd?

systemd: сокращение объема кода

А это файл юнита sshd.service из состава дистрибутива Fedora 23. Обратите внимание на то, насколько сократился объем и улучшилась читаемость содержимого этого файла:

[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

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

  • Директива After предназначена для описания порядка запуска юнита, причем ее значение указывает на то, что перед запуском данного юнита должна быть достигнута цель network.target и запущен юнит sshd-keygen.service.
  • Значение директивы Wants указывает на то, что служба sshd-keygen должна быть запущена для корректного запуска рассматриваемой службы. Обратите внимание на незначительное отличие данной директивы от других директив, регламентирующих порядок запуска юитов. systemd рассматривает порядок запуска юнитов и их зависимости как ортогональные параметры юнита и это означает, что размещение указания на юнит sshd-keygen в начале списка не делает его зависимостью юнита sshd - хотя в данном случае так и есть. Директива Wants сообщает systemd о том, что требуется запустить службу sshd-keygen.service, но даже в том случае, если запуск этой службы не завершится удачно (например, в том случае, если ключи сервера SSH уже существуют), служба sshd будет запущена. Если в вашем случае необходимо осуществить гарантированный успешный запуск службы sshd-keygen.service, вам следует использовать директиву Requires вместо данной директивы.
  • Директива EnviromentFile, по аналогии с системой инициализации SysVinit, предназначена для указания пути к файлу с параметрами для бинарного файла sshd. Он должен содержать пары ключ=значение, которые будут переданы sshd.
  • Директива ExecStart предназначена для указания команды, которая должна быть выполнена для запуска службы sshd. Данная директива заменяет собой всю функцию start из старого сценария инициализации. Переменная $OPTIONS в данном случае соответствует переменной OPTIONS из файла, путь к которому указан с помощью директивы EnviromentFile.
  • Директива ExecReload позволяет задать команду, которая будет исполняться тогда, когда системный администратор будет перезапускать демон sshd. Она заменяет собой всю функцию reload из старого сценария инициализации.
  • Директива KillMode позволяет установить метод остановки службы. В данном случае systemd будет прерывать работу лишь основного процесса sshd. При этом все дочерние процессы будут продолжать обслуживание открытых сессий SSH. Это не слишком часто используемый метод остановки службы, который, тем не менее, отлично подходит для данного случая. Например, он позволяет избежать закрытия вашей сессии SSH при перезапуске или остановке соответствующей службы с помощью команды systemctl!
  • Директива Restart позволяет установить стратегию управления состоянием службы в случае неожиданного завершения ее работы. В данном случае параметр on-failure означает, что практически в любом случае при неожиданном завершения работы рассматриваемой службы systemd попытается перезапустить ее. При этом в случае корректного завершения работы службы sshd, например, в результате использования команды systemctl stop, перезапуск службы не будет осуществляться.
  • Директива RestartSec позволяет установить длительность периода ожидания, в данном случае равную 42 секундам, по истечении которого будет осуществлен перезапуск службы после неожиданного завершения ее работы.
  • Директива WantedBy используется для того, чтобы при выполнении команды systemctl enable sshd.service в директории multi-user.target.wants по умолчанию создавалась ссылка на юнит-файл рассматриваемой службы. Это один из механизмов, используемых systemd для установления юнит-файлов, которые должны быть запущены для достижения определенных целей. Таким образом, достижение цели multi-user.target, а также любой зависящей от нее цели, будет связано с запуском службы sshd.service.

Разумеется, существует множество других директив, причем все они подробно описаны в документации systemd. Но если вы желаете разработать свой собственный юнит-файл для простой службы, вам не придется использовать даже все те директивы, которые приведены в списке выше. Вам наверняка потребуется лишь несколько директив, а именно: After, ExecStart и WantedBy. Если вы рассмотрите содержимое директории /usr/lib/systemd/system/ вашей системы Fedora, вы обнаружите большое количество юнит-файлов с расширением .service, которые могут использоваться в качестве шаблонов.

Дополнительные рекомендации

Еще одной примечательной особенностью systemd является сохранение совместимости с SysVinit. В прошлом некоторые системные администраторы создавали собственные сценарии запуска служб, такие, как /etc/rc.d/rc.local. Если вы также создавали подобные сценарии и не готовы к их преобразованию, не беспокойтесь - система инициализации systemd будет исполнять их в случае активации службы rc-local.service, которая включена в комплект ее поставки.

Помните о том, что при создании системных служб для дистрибутива Fedora лучше всего размещать соответствующие юнит-файлы в директории /etc/systemd/system. Обычно в директории /etc хранятся все файлы конфигурации системы и приложений, которые могут модифицироваться системным администратором. Это позволит вам избежать необходимости модификации файлов из директории /usr/lib/systemd/system, которая обслуживается системой для работы с пакетами программного обеспечения дистрибутива Fedora.

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