Ubuntu. Создание юнита для Systemd

05.07.2023

Теги: LinuxSystemdUbuntuКонфигурацияНастройкаТеория

Основная цель Systemd — инициализировать компоненты, которые должны запускаться после загрузки ядра Linux. Кроме того, с помощью команды systemctl позволяет управлять службами и демонами сервера.

Systemd запускает сервисы описанные в его конфигурации. Конфигурация состоит из множества файлов, которые часто называют юнитами. Все эти юниты разложены в трех директориях.

  • /lib/systemd/system — юниты из установленных пакетов — nginx, apache, mysql
  • /run/systemd/system — нужен самому Systemd для динамически создаваемых юнитов
  • /etc/systemd/system — юниты, созданные администратором сервера

Юнит представляет из себя текстовый файл с форматом, похожим на файлы ini. Для создания простейшего юнита надо описать три секции — [Unit], [Service], [Install].

В секции [Unit] описываем, что это за юнит, директива Description описывает назначение (краткое описание).

[Unit]
Description=SSH tunnel to TimeWeb server at startup

Далее следуют директивы, которые влияют на порядок загрузки сервисов.

Запускать наш сервис можно после запуска network-online.target

After=network-online.target

Для запуска сервиса необходим запущенный сервис mysql.service

Requires=mysql.service

Для запуска сервиса желателен запущенный сервис redis.service

Wants=redis.service

Директива After отвечает за порядок запуска юнитов. Если юнит foo.service содержит директиву After=bar.service и оба юнита запускаются, запуск foo.service откладывается до тех пор, пока не завершится запуск bar.service (не обязательно успешно).

Директива Requires означает, что если текущий юнит запускается, то и все юниты из этой директивы также будут запущены. Если юнит из Requires не запустится успешно, и этот юнит есть в After, текущий юнит не будет запущен — нет смысла запускать, все равно не сможет нормально работать. Текущий юнит будет остановлен (или перезапущен), если один из юнитов в Requires будет явно остановлен (или перезапущен).

Тип зависимости Requires не означает, что все юниты в нем должны быть в активном состоянии, когда текущий юнит находится в активном состоянии. Остановка (или перезапуск) текущего юнита не означает остановку (или перезапуск) юнитов из Requires — это он от них зависит, а не они от него.

Директива BindsTo очень похожа на Requires, но зависимость сильнее. Если юнит в BindsTo остановлен (неважно по какой причине), то текущий юнит также будет остановлен. Это односторонняя зависимость — остановка или перезапуск текущего юнита никак не влияют на юнит в BindsTo.

[Unit]
Description=Service Two
# Если юнит one.service перешел в неактивное состояние по любой причине, то текущий юнит
# также будет остановлен. Это односторонняя зависимость two.service от one.service.
BindsTo=one.service

Директива PartOf похожа на BindsTo, но срабатывает только при остановке и перезапуске. Когда Systemd останавливает или перезапускает юнит в BindsTo, то и текущий юнит будет остановлен или перезапущен. Это односторонняя зависимость — остановка или перезапуск текущего юнита никак не влияют на юнит в PartOf.

[Unit]
Description=Service Two
# Когда Systemd останавливает или перезапускает юнит one.service, этот юнит также будет
# остановлен или перезапущен. Это односторонняя зависимость two.service от one.service.
PartOf=one.service

Не следует путать зависимость и порядок запуска — это абсолютно независимые директивы. Директива Requires требует только запуска перечисленных юнитов, без определения порядка запуска. Директива After требует только дождаться окончания запуска перечисленных юнитов (не обязательно успешного). Совместное использование двух директив изменяет этот порядок вещей.

Если юнит есть в Requires, но нет в After, то текущий юнит будет запущен параллельно с требуемым юнитом, а не после завершения запуска требуемого юнита (не обязательно успешного). Если юнит есть в Requires и After, то текущий юнит будет запущен только после успешного завершения запуска требуемого юнита.

В секции [Service] указываем какими командами и под каким пользователем надо запускать сервис.

[Service]
Type=simple

Тип сервиса Type может принимать значения

  • simple (по умолчанию) — служба будет запущена незамедлительно. Процесс при этом не должен разветвляться. Не используйте этот тип, если другие службы зависят от очередности при запуске данной службы.
  • forking — служба запускается однократно и процесс разветвляется с завершением родительского процесса. Данный тип используется для запуска классических демонов.

Директивы User и Group — пользователь и группа, под которым нужно стартовать сервис.

[Service]
Type=simple
User=evgeniy
Group=evgeniy

Директива PIDFile — если тип службы отмечен как forking, эта директива используется для установки пути файла, который должен содержать идентификационный номер процесса основного «ребенка», который должен контролироваться.

Директива WorkingDirectory — рабочий каталог службы, он делается текущим перед запуском стартап команд.

Директива Environment задает переменные окружения, которые должны быть доступны нашей службе.

Директивы ExecStart, ExecStop, ExecReload задают команды на start, stop и reload сервиса. Тут есть тонкость — Systemd настаивает, чтобы команда указывала на конкретный исполняемый файл — нужно указывать полный путь.

Директива Restart предписывает перезапускать наш сервис, если он вдруг перестанет работать.

[Service]
Type=simple
User=evgeniy
Group=evgeniy
ExecStart=/usr/bin/ssh -N -F /home/evgeniy/.ssh/config remote-forward-timeweb-server
Restart=always
RestartSec=5

Директива RestartSec — если автоматический перезапуск службы включен, это указывает время ожидания перед попыткой перезапуска службы.

Директива TimeoutSec устанавливает время, в течение которого система будет ждать запуска или остановки службы, прежде чем пометить ее как неудачную или убитую принудительно.

В секции [Install] описываем, на каком уровне запуска должен стартовать сервис.

[Install]
WantedBy=multi-user.target

В итоге у нас получился вот такой юнит для Systemd.

[Unit]
Description=SSH tunnel to TimeWeb server at startup
After=network-online.target
Requires=network-online.target
[Service]
Type=simple
User=evgeniy
Group=evgeniy
ExecStart=/usr/bin/ssh -N -F /home/evgeniy/.ssh/config remote-forward-timeweb-server
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

После создания нового юнита необходимо перезапустить Systemd.

$ sudo systemctl daemon-reload

Дополнительно

Поиск: Linux • Systemd • Ubuntu • Конфигурация • Настройка • Теория • юнит

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.