Как создать свой сервис для Linux

linux

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

Основы создания сервиса Linux

Если вы еще никогда не делали свои сервисы, начнем с основ. Systemd оперирует абстрактными единицами (unit), которые бывают разных типов, могут предоставлять различные ресурсы (процессы, сокеты, абстрактные «цели») и требовать других ресурсов для запуска.

Самый распространенный вид ресурса — сервис (service). Файлы с описаниями сервисов и всего прочего лежат в каталоге /lib/systemd/system/. Чтобы systemd нашел новый сервис, достаточно положить в этот каталог свой файл. Если этот сервис ранее не существовал, systemd прочитает файл и загрузит его в память. Однако, если вы редактируете файл ранее запущенного сервиса, не забудьте заставить systemd перечитать файлы командой sudo systemctl daemon-reload!

РЕКОМЕНДУЕМ:
Как защититься от руткитов в Linux

Сервисы типа oneshot — долой rc.local

Когда-то основным способом добавить выполнение команд в загрузку системы было дописать их в /etc/rc.local. Очевидный недостаток — нет способов следить, насколько успешно они выполнились. В systemd легко создать для такой цели свой сервис типа oneshot, и им можно будет управлять через systemctl, как любым другим. В этом случае systemd выполнит команду и посчитает запуск сервиса успешным, если она завершилась с кодом ноль.

Сохраним следующий файл в /lib/systemd/system/dumb-test.service:

Дополнительных действий не требуется, и теперь вы можеыр делать с ним все то же, что с системными сервисами: запустить с помощью sudo systemctl start dumb-test.service, поставить на загрузку с помощью sudo systemctl enable dumb-test.service и так далее.

Создание сервиса из любой программы

Любой долгоживущий процесс можно легко превратить в сервис с помощью опции Type=idle. В этом случае systemd перехватит стандартные потоки ввода-вывода и будет следить за жизнью процесса.

Для демонстрации напишем программу на Python, которая просто выводит сообщение в бесконечном цикле. Сохраним в /usr/local/bin/test.py следующее:

Затем создадим для нее файл сервиса в /lib/systemd/system/smart-test.service:

Теперь можно запустить наш сервис и убедиться, что он работает:

Стандартный вывод программы пишется в journald, и его можно увидеть в journalctl -u smart-test. Ради интереса посмотрим на работу опции Restart=on-failure. Остановим наш процесс с помощью kill -9 ${Main PID} и заглянем в логи:

Для настоящих демонов нужно использовать тип forking, но мы не будем вдаваться в детали — авторы таких пакетов наверняка все уже знают сами.

Зависимости и порядок запуска

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

Порядок запуска сервисов

Порядок запуска сервисов определяется опциями Before и After. Если в настройках сервиса foo написано After=bar.service и оба сервиса должны запуститься, то systemd сначала выполнит попытку запустить bar, а затем foo.

Однако опция After=bar.service сама по себе не поставит сервис на загрузку. Более того, она никак не повлияет на решение запускать foo, даже если запуск bar завершится неудачей.

Причина существования этих опций — способность systemd запускать сервисы параллельно.

РЕКОМЕНДУЕМ:
Советы и трюки для настройки сети в Linux

Для примера возьмем типичный веб-сервер с набором из веб-приложения FCGI, СУБД и обратного прокси. В каком порядке запускать процесс FCGI и обратный прокси, не так важно. Запросы будут работать, только когда они оба запущены, но «неверный порядок» никак не помешает им запуститься.

Если веб-приложение требует данных из базы для инициализации, то мало убедиться, что и процесс FCGI, и СУБД запущены, — приложение нужно запускать только после полного запуска СУБД. Именно для этих случаев и предназначены опции Before/After.

Зависимости

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

Мягкие зависимости указываются с помощью опции Wants= в секции [Unit]. Пример из sshd.service:

Цель sshd-keygen.target, очевидно, генерирует ключ хоста, если он отсутствует. Технически sshd не сможет запуститься без ключа хоста, поэтому, почему авторы решили сделать зависимость мягкой, можно только догадываться. Возможно, они посчитали, что в большинстве случаев ключ уже существует и устаревший ключ лучше неработающего SSH.

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

Жесткие зависимости можно указать с помощью опции Requires. К примеру, в systemd-journal-flush.service есть опция Requires=systemd-journald.service — очевидно, отправить команду journald невозможно, пока он не запущен.

У этой опции существуют вариации, например RequiresMountsFor. Посмотрим в файл logrotate.service:

Для работы logrotate нужен доступ к каталогу с логами и больше ничего. Опция RequiresMountsFor=/var/log позволяет выразить именно это: сервис запустится, как только будет примонтирован каталог, содержащий путь /var/log, даже если он находится не в корневом разделе.

Внедрение в зависимости к чужим сервисам

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

В systemd есть несколько способов решить эту проблему. Если нужно именно внедриться к кому-то в зависимости, можно попробовать опции обратных зависимостей: WantedBy и RequiredBy. Они должны находиться в секции [Install], а не [Unit]. Подводных камня здесь два: они обрабатываются только при установке сервиса с помощью systemctl enable и, как все в systemd, не всегда нормально работают во всех версиях.

Второй вариант, который позволяет менять любые настройки: скопировать файл сервиса в /etc/systemd/system/. Если один файл присутствует в обоих каталогах, то файл из /etc/systemd/system имеет приоритет.

Третий, менее радикальный вариант: создать файл вида /etc/systemd/system/${unit}.d/local.conf и прописать туда только нужные настройки.

Для примера притворимся, что наш сервис smart-test вовсе и не наш, и добавим ему в зависимости sshd.service третьим способом. Создадим файл /etc/systemd/system/smart-test.service.d/local.conf со следующим содержанием:

Теперь sshd.service будет запущен вместе со smart-test.service, даже если раньше был выключен.

Если важен не только сам факт запуска обоих сервисов, но и порядок их запуска, не забудьте указать его с помощью опций Before или After.

Зависимости по умолчанию

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

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

Просмотреть зависимости сервиса и убедиться, что там нет лишнего, можно командой systemctl list-dependencies ${unit}.

РЕКОМЕНДУЕМ:
Мониторинг в Linux с помощью командой строки

Итого

Systemd можно любить или ненавидеть, но игнорировать его невозможно — нужно уметь с ним работать. Надеюсь, эти знания помогут вам обратить systemd себе на пользу.

Понравилась статья? Поделиться с друзьями:
Комментарии: 3
  1. Вячеслав

    Почему это файлы сервисов предлагается хранить в директории, когда пакеты кладут свои файлы? для этого есть /etc/systemd/system/

    1. Gar02

      Вячеслав, может, именно потому, что в «/etc/systemd/system/» лежат символьные ссылки на файлы из «/lib/systemd/system»?

  2. Gar02

    Даниил Батурин, Вы молодец. Благодаря Вашей статье я настроил запуск своего сервиса управления реле USB.

Добавить комментарий