Linux systemd service создать

Обновлено: 03.07.2024

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

Юниты systemd и их разновидности.

Если вы когда-либо играли в стратегии, то наверняка помните, что юнитами там называли единицу любой разновидности войск. Лучник, инженер, большой шагающий робот — все это юниты.

В контексте systemd юниты — это отдельные объекты, которыми управляет эта система. Как и игровые войска, они имеют свои разновидности, у каждой из которых есть свое предназначение. Если вы читали предыдущие статьи, то знаете как минимум одну разновидность таких объектов — сервисы. Вот некоторые другие виды юнитов, о которых я, возможно, расскажу в следующих статьях:

  • точки монтирования,
  • таймеры,
  • таргеты или цели,
  • устройства.

Основными директориями для хранения юнитов в Ubuntu 16.04 являются:

  • /lib/systemd/system — здесь находятся юниты, созданые автоматически при установке пакетов;
  • /etc/systemd/system — пользовательские, вернее, администраторские юниты.

Юниты systemd и их разновидности

Скриншот выше демонстрирует содержимое директории с файлами юнитов.

Файлы юнитов содержат разделы с набором параметров и их значений, разделенных знаком «=». Слева название параметра, справа значение, все предельно просто. Для начала создадим файл с названием my_backup.service и поместим его в /etc/systemd/system. Впишем в него следующие строки:

Идея этого решения принадлежит человеку с ником mezcalero из IRC чата systemd.

Давайте разберем представленный выше код.

Раздел [Unit] хранит общие сведения о юните. В данном случае он содержит только описание (Description).

Раздел [Service] объединяет сведения, необходимые для выполнения юнитом его задач. Type определяет тип сервиса (не путайте его с разновидностями юнитов), oneshot означает, что сервис должен выполнить разовую задачу и завершиться. ExecStop указывает скрипт, который должен быть выполнен перед остановкой сервиса. Есть еще ExecStart, этот параметр используется чаще и определяет команду, которая должна быть выполнена сразу после запуска сервиса. RemainAfterExit=true предписывает systemd считать процесс активным после его завершения.

Секция [Install] содержит сведения о том, при каких обстоятельствах должен быть запущен сервис. WantedBy=multi-user.target устанавливает запуск при обычной загрузке компьютера.

Но это еще не все. Скрипт, который мы указали в качестве значения ExecStop, на данный момент не существует.

Приведенный выше код нужно поместить в файл /usr/local/bin/my_backup, а затем сделать его исполняемым:

Хотя наш скрипт совсем простой, давайте все же разберем его. Первая строка — обязательное начало любого bash скрипта. Вторая получает текущую дату и время в определенном нами формате и помещает ее в переменную current_date. Третья запускает архиватор и сообщает ему, что и куда архивировать. Кстати, не забудьте подставить свои пути вместо русского текста.

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

Создаем Unit

Обратите внимание: в команде выше я использовал mybackup, а не mybackup.service, хотя подразумевал именно это. Дело в том, что если тип юнита не указан, предполагается, что это .serivice. Полный вариант сработал бы точно так же.

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

По желанию можно настроить сохранение резервных копий вне локального компьютера, например, в облачном хранилище. В этом случае может потребоваться проверка подключения к сети и доступности самого хранилища. Можно также настроить создание резервных копий не перед выключением, а по расписанию с помощью юнита timer. Возможно, в следующих статьях я продолжу эту тему и покажу, как это можно сделать, а заодно — продемонстрирую работу юнитов других типов.

Далеко не всегда нужно писать собственный скрипт при создании сервиса systemd. Зачастую сервисные файлы пишутся с целью управления уже существующими программами, путь к которым определяется параметрами ExecStart или ExecStop. Наш скрипт тоже использует существующую программу — tar. По большому счету, можно было обойтись и вовсе без него, сразу прописав все необходимые параметры в сервисном файле. Я включил его создание в статью только с целью демонстрации самой возможности. Но пользовательский скрипт все же будет полезен, если вы захотите усложнить задачу: отослать уведомление на электронную почту, удалить самый старый из архивов и т. д.

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

Создаем Unit

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

linux

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

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

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

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

Сервисы типа 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 перехватит стандартные потоки ввода-вывода и будет следить за жизнью процесса.

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

ExecStart =/ usr / bin / python3 - u / usr / local / bin / test . py

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

Loaded : loaded ( / usr / lib / systemd / system / smart - test . service ; static ; vendor preset : disabled ) Active : active ( running ) since Fri 2019 - 10 - 25 16 : 25 : 18 + 07 ; 1s ago └─ 19893 / usr / bin / python3 - u / usr / local / bin / test . py

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

smart - test . service : Main process exited , code = killed , status = 9 / KILL smart - test . service : Service RestartSec = 100ms expired , scheduling restart . smart - test . service : Scheduled restart job , restart counter is at 1.

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

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

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

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

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

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

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

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

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

Зависимости

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

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

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

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

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

Documentation = man : logrotate ( 8 ) man : logrotate . conf ( 5 )

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

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

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

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

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


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

Работа с Systemd и сервисами

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

Многие из этих сервисов не работают все время, как nginx или MySQL. Вы можете распечатать список услуг, которые в настоящее время используются с:

Службы с символом «+» работают, а службы с символом «-» в настоящее время остановлены. Вы можете просмотреть более подробную информацию с:

Поскольку службы работают в фоновом режиме, они не регистрируют свои выходные данные на вашей консоли, а записывают выходные данные в системный журнал. Команда «status» покажет последние несколько строк этого журнала, но вы можете прочитать его непосредственно с помощью:

Эта команда печатает последние 50 записей журнала ( -n ) из сервиса nginx ( -u ). Он настроен на печать всего и начинается снизу, следуя новым записям журнала по мере их создания ( -f ).

Конечно, многие приложения по-прежнему записывают большинство данных в свои журналы доступа или ошибок, поэтому обязательно проверьте их. Журнал отслеживает события, записываемые непосредственно на консоль, что может быть полезно для отладки ваших собственных служб.

Простая настройка сервиса

Systemd для многих вещей используется в Linux; каждый объект, которым он управляет, называется модулем, и имеет соответствующий «файл модуля», определяющий, кем он является. Это могут быть простые сервисы, такие как nginx или MySQL, но они также могут быть такими, как точки монтирования, устройства, сокеты и много других вещей под капотомвсе управляется systemd. Единицы также могут быть целиИспользуется для контроля, когда будут запущены другие службы (т.е. после инициализации сети).

Однако в этом случае вы, вероятно, просто хотите настроить свое приложение как базовую службу. Для этого вам нужно создать новый файл модуля, который вы хотите поместить в /etc/systemd/system/ и имя с .service расширение:

У юнит-файлов есть несколько разных разделов, но в целом они будут выглядеть примерно так:

Во-первых, [Unit] раздел, который определяет кучу метаданных об устройстве. After= Директива может быть использована для задержки активации устройства до запуска другого устройства, например, network или другой сервис, например mysql.service Это не делает его сильно зависимым от этой услуги, хотя вы можете сделать это с помощью Requires= или Wants= директивы. В этом разделе также настраивается максимальное количество попыток запуска устройства до полного отказа systemd; так как вы, вероятно, хотите, чтобы он продолжал пытаться, вы можете установить это в 0, чтобы отключить это поведение.

Далее идет [Service] раздел, относящийся к файлам сервисных единиц. Здесь вы будете настраивать параметры Exec. User запустит службу как определенный пользователь. Вы можете установить это для своей личной учетной записи пользователя, root или пользовательской учетной записи службы. Просто убедитесь, что у пользователя достаточно прав для выполнения своей работы.

Здесь есть несколько различных директив для указания программ для запуска. ExecStartPre будет запущен первым, что позволит вам выполнить любые необходимые настройки до того, как служба действительно запустится. ExecStart основной исполняемый файл ExecStartPost бежит потом, и ExecStop запускается, когда служба отключается. ExecReload это специальная директива, которая используется, когда вы вызываете «reload» вместо restart. Это позволяет выполнять перезагрузку конфигурации во время выполнения при условии, что ваше приложение имеет такую ​​возможность.

Наконец, [Install] раздел, который определяет некоторые дополнительные действия, связанные с тем, как systemd обрабатывает модуль. Это чаще всего используется для указания WantedBy= директива, которая используется для указания systemd, когда следует запускать ваш сервис, и создает символические ссылки между целями и их зависимыми модулями. Если вы не уверены, какую цель использовать, multi-user.target запустит сервисы при запуске после того, как большинство из них загружено.

В целом, конфигурация довольно проста, и все, что вам действительно нужно сделать, это поместить свой исполняемый файл в качестве аргумента в [Service] раздел. Как только ваша служба будет создана, вам придется перезагрузить демон systemctl для обновления с вашими изменениями:

И включите его (который запустит его при загрузке, в соответствии с настройкой модуля):

А затем запустите сервис:

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

Если вам нужно перезапустить службу, вы можете использовать:

Который выполнит ExecStop= директива, выключите устройство, а затем снова включите его.

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

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

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

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

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

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

Секция [Unit]

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

Секция [Service]

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

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

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

Читайте также: