Добавить сервис в systemd

В этой заметке я расскажу о том, как настроить сервис Linux для автоматического запуска демона после сбоя или перезагрузки. В качестве примера я буду использовать Nginx и PHP но вы можете этот способ для любых приложений.

Уже прошло достаточно много времени с того момента, как большинство популярных дистрибутивов Linux перешли на системный менеджер systemd. Тем не менее, до сих пор старая система инициализации SysV продолжает параллельно функционировать на ряду с systemd. И как и раньше, большинство пакетов после установки добавляют стартовый скрипт в каталог init.d.

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

Для примера мы установили OpenVPN, для которого по умолчанию используется механизм запуска System V. Во время установки пакета в каталог /etc/init.d будет скопирован скрипт инициализации, который будет отвечать за автоматический запуск приложения после перезагрузки. Если в один прекрасный день OpenVPN упадет, без дополнительных настроек, нужно будет каждый раз запускать этот процесс вручную.

Теперь возьмем ситуацию, когда для запуск сервиса выполнен через systemd. Если произойдет крэш или даже если вы захотите специально убить процесс, то systemd автоматически это обнаружит и повторно запустит процесс. При этом systemd обратно совместим с командами System V и сценариями инициализации.

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

/etc/systemd/system/

В простом варианте юнит состоит из трех секции: [Unit], [Service], [Install].

Секция [Unit]

Описание юнита:

Description=MyUnit

Далее указываем порядок загрузки, после какого сервиса systemd запустить наш юнит:

After=syslog.target
After=network.target

Секция [Service]

Задаем тип сервиса:

Type=simple

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

Type=forking

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

Расположение pid-файла:

PIDFile=/run/service.pid

Рабочий каталог приложения:

WorkingDirectory=/usr/local/service

Пользователь и группа, под которым будет запускаться сервис:

User=unit
Group=unit

Задаем приоритет убийства процесса при нехватке памяти:

OOMScoreAdjust=-100

Минимальное значение -1000 — полный запрет.

Команды запуска, остановки и перезапуска сервиса:

ExecStart=/usr/sbin/service
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID

Таймаут запуска или остановки сервиса:

TimeoutSec=300

Контроль запуска сервиса и автоматический запуск в случае крєша:

Restart=always

Секция [Install]

Уровень запуска сервиса:

WantedBy=multi-user.target

В итоге мы получили следующий юнит:

[Unit]
Description=MyUnit
After=syslog.target
After=network.target

[Service]
Type=forking
PIDFile=/run/service.pid
WorkingDirectory=/usr/local/service
User=munit
Group=unit
OOMScoreAdjust=-100

ExecStart=/usr/sbin/service
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
TimeoutSec=300

[Install]
WantedBy=multi-user.target

Добавляем юнит в каталог:

nano /etc/systemd/system/unit_name

Включаем, запускаем и смотрим статус юнита:

systemctl enable unit_name
systemctl start unit_name
systemctl -l status unit_name

А теперь привожу примеры работающих юнитов.

Юнит systemd для запуска Nginx:

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Юнит systemd для запуска php-fpm:

[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target

[Service]
Type=simple
PIDFile=/usr/local/php-fpm/var/run/php-fpm.pid
ExecStart=/usr/local/php-fpm/sbin/php-fpm --nodaemonize --fpm-config /usr/local$
ExecReload=/bin/kill -USR2 $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target