Как создать сервис linux
У меня появился Linux на домашнем компьютере, и я поспешил обжиться в новой ОС. Она была установлена с systemd init process. Это было мое первое знакомство с этим новым инструментом. Cвой ноутбук я использую для каждодневной жизни и для программирования. Мне хотелось включать рабочие программы (Apache2 и MySQL) только на время, пока я их использую, чтобы не тратить впустую ресурсы своего компьютера. Дополнительно, для тестирования я написал bash скрипт, который выгружает содержимое одной из MySQL БД c жесткого диска в ОЗУ (в tmpfs) – так тесты выполняются значительно быстрее. По идее, я мог бы начинать свой рабочий день вот так:
Но мне хотелось сделать вещи “как надо”.
Чего я хотел?
Что я сделал?
В итоге я объединил Apache2 и MySQL в один target. Это позволило запускать оба сервиса одной командой. А свой mysqld-tmpfs скрипт я декларировал в виде сервиса в глазах systemd. Будучи сервисом, я уверен, что systemd выполнит его корректную остановку, если система пойдет на перезагрузку или еще в какую-то нештатную ситуацию, и моя БД без потерь сохранится на жесткий диск.
Что такое service?
Это некоторая программа, которая выполняется в фоне и предоставляет полезную функциональность. К примеру, Apache веб сервер. Сервисы можно запускать и останавливать. Некоторые сервисы могут запускаться и останавливаться автоматически по определенным событиям (загрузка ОС, выгрузка ОС и тп). Так же их можно запускать/останавливать вручную. Сервис декларируется в /etc/systemd/system/my-name.service файлах (с суффиксом “.service”).
Что такое target?
Target в systemd очень похож на runlevel в openRC, но это все-таки разные вещи. Во-первых, target позволяет группировать 1 и более сервисов в единый блок. Группируя сервисы в targets, ими проще управлять. Во-вторых, systemd автоматически включает/выключает targets по событиям. “Включение” target означает включение всех сервисов, которые он объединяет в себе. К примеру, если в systemd настроен target по умолчанию my-favorite.target, то при загрузке системы systemd включит все сервисы, которые задекларированы внутри my-favorite.target. В какой-то момент в консоли можно набрать:
Все сервисы из my-another.target будут включены, и все включенные сервисы не из my-another.target будут выключены. Это очень похоже на переключение runlevel в openRC. Однако, systemd поддерживает включение более чем 1 target. Вот пример:
После выполнения этих команд в системе будет работать объединение сервисов из my-favorite.target и my-another.target.
Как я это сделал?
В итоге у меня получился вот такой mysqld-tmpfs.service файл:
И вот такой programming.target файл:
Какие были проблемы?
Декларации “чужих” сервисов можно менять создавая файлы /etc/systemd/system/name-i-alter.service.d/*.conf. Я просто создал /etc/systemd/system/apache2.service/auto-stop.conf и /etc/systemd/system/mysqld.service.d/auto-stop.conf и поместил туда ту строку.
Другая проблема, на которую я, наткнулся была в том, что systemd не очень любит symlinks. Я не большой любитель “загаживать” системные директории типа /etc, /bin, /usr своими локальными продуктами жизнедеятельности, поэтому изначально я попытался свой /etc/systemd/system/mysqld-tmpfs.service сделать symlink на /root/scripts/mysqld-tmpfs.service файл, т.е. хранить сам файл в домашнем каталоге root пользователя. Но systemctl команда отказывалась работать с таким сервисом выдавая малопонятные ошибки. Оказалось, что определенную часть своей внутренней кухни systemd делает именно на symlinks, и ему тогда “трудно” отличать внутреннюю кухню (свои symlinks) от сторонних *.service файлов (если они тоже являются symlinks). Удалив symlink из /etc/systemd/system/mysqld-tmpfs.service и скопировав туда содержимое настоящего файла, я решил эту проблему. Более подробное описание этой проблемы можно прочитать тут: bugzilla.redhat.com/show_bug.cgi?id=955379
Результат
Я достиг своей цели. Начиная рабочий день:
Когда нужно выполнить тесты на своем проекте:
Когда я хочу демонтировать БД из tmpfs в жесткий диск (хотя на практике я так почти не делаю, а просто оставляю БД в tmpfs на целый день, и при выключении systemd за меня запускает демонтировку из tmpfs в жесткий диск):
Когда я закончил работать и хочу остановить рабочие программы:
Cheat sheet
Надеюсь, эта статья кому-то поможет при осваивании systemd. Я попытался сделать ее компактной, и если упустил из внимания какие-то дополнительные вопросы, спрашивайте в комментариях!
Не хотите по-хорошему — уберём вазелин
Как создать свою службу в Linux
Шаг 1. Создание скрипта
Вот шаблон, чтобы не запутаться:
Шаблон от разработчиков находится по адресу /etc/init.d/skeleton
Обратите внимание на блок, ограниченный метками [ ### BEGIN INIT INFO ] и [ ### END INIT INFO ]. Он выделен жирным.
Этот блок полностью закомментирован, то есть при выполнении скрипта его содержимое игнорируется. Однако, он содержит инструкции для утилиты update-rc.d, которая прочитает их, поймёт и раскидает ссылки на этот скрипт по уровням запуска системы.
Подробнее про процессы, службы, уровни запуска и устройство каталогов запуска можно прочитать здесь.
Этот блок обязателен для скрипта, управляющего сервисом. Остановимся на нём подробнее.
Все строки между ними должны быть в формате:
# Инструкция: арг1 [арг2. ]
Строка начинается со знака # и последующего одного пробела. После инструкции должно стоять двоеточие, агрументы разделяются пробелами.
Список инструкций
Данный список взят по одной из ссылок в конце записи, описания нуждаются в доработке
Уровни запуска определяют, в какие из каталогов /etc/rcX.d будут помещены ссылки на текущий скрипт.
Для отслеживания зависимостей важны инструкции Provides, Required- и Should-. Остальные не используются. На основе зависимостей утилита update-rc.d (или insserv) упорядочивает скрипты в каталоге определённого уровня запуска ( /etc/rcX.d ).
Список виртуальных объектов
Та же хрень, что и с предыдущим списком
Шаг 2+3. Оформление скрипта в качестве службы
Допустим, имя нашего самописного скрипта somestuff. Именно так, somestuff, без всяких расширений.
Делаем его исполняемым:
Копируем в хранилище сервисных скриптов:
И делаем прописку в каталогах уровней запуска:
update-rc.d somestuff defaults
Полный путь давать не надо, только имя в /etc/init.d/
Для выписки (удаления всех симлинков на этот скрипт из всех каталогов уровней запуска) делаем:
Как создать сервис linux
Пришло время рассказать о создании собственных юнитов systemd, тем более, что вы сами просили об этом в комментариях. Для примера я напишу сервис, который создает резервную копию директории перед выключением компьютера и подробно прокомментирую каждый этап. Но сначала — немного теории.
Юниты systemd и их разновидности.
Если вы когда-либо играли в стратегии, то наверняка помните, что юнитами там называли единицу любой разновидности войск. Лучник, инженер, большой шагающий робот — все это юниты.
В контексте systemd юниты — это отдельные объекты, которыми управляет эта система. Как и игровые войска, они имеют свои разновидности, у каждой из которых есть свое предназначение. Если вы читали предыдущие статьи, то знаете как минимум одну разновидность таких объектов — сервисы. Вот некоторые другие виды юнитов, о которых я, возможно, расскажу в следующих статьях:
Основными директориями для хранения юнитов в Ubuntu 16.04 являются:
Скриншот выше демонстрирует содержимое директории с файлами юнитов.
Файлы юнитов содержат разделы с набором параметров и их значений, разделенных знаком «=». Слева название параметра, справа значение, все предельно просто. Для начала создадим файл с названием 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. Третья запускает архиватор и сообщает ему, что и куда архивировать. Кстати, не забудьте подставить свои пути вместо русского текста.
Теперь нам осталось лишь добавить наш сервис в автозагрузку и запустить его, чтобы он включился немедленно:
Перезагрузите компьютер и проверьте, появился ли архив в указанном месте.
По желанию можно настроить сохранение резервных копий вне локального компьютера, например, в облачном хранилище. В этом случае может потребоваться проверка подключения к сети и доступности самого хранилища. Можно также настроить создание резервных копий не перед выключением, а по расписанию с помощью юнита timer. Возможно, в следующих статьях я продолжу эту тему и покажу, как это можно сделать, а заодно — продемонстрирую работу юнитов других типов.
Далеко не всегда нужно писать собственный скрипт при создании сервиса systemd. Зачастую сервисные файлы пишутся с целью управления уже существующими программами, путь к которым определяется параметрами ExecStart или ExecStop. Наш скрипт тоже использует существующую программу — tar. По большому счету, можно было обойтись и вовсе без него, сразу прописав все необходимые параметры в сервисном файле. Я включил его создание в статью только с целью демонстрации самой возможности. Но пользовательский скрипт все же будет полезен, если вы захотите усложнить задачу: отослать уведомление на электронную почту, удалить самый старый из архивов и т. д.
В скрипте я допустил ошибку, вследствие чего после перезагрузки компьютера резервной копии не обнаружилось. Установить причину было легко:
Вот так с помощью способа, описанного в прошлой статье, я легко нашел и устранил проблему. Вы тоже можете использовать этот способ, если что-то не заработает сразу. При следующей перезагрузке архив уже лежал в отведенном ему месте:
Как создать свой сервис для Linux
Основы создания сервиса Linux
Если вы еще никогда не делали свои сервисы, начнем с основ. Systemd оперирует абстрактными единицами (unit), которые бывают разных типов, могут предоставлять различные ресурсы (процессы, сокеты, абстрактные «цели») и требовать других ресурсов для запуска.
Сервисы типа oneshot — долой rc.local
Когда-то основным способом добавить выполнение команд в загрузку системы было дописать их в /etc/rc.local. Очевидный недостаток — нет способов следить, насколько успешно они выполнились. В systemd легко создать для такой цели свой сервис типа oneshot, и им можно будет управлять через systemctl, как любым другим. В этом случае systemd выполнит команду и посчитает запуск сервиса успешным, если она завершилась с кодом ноль.
Создание сервиса из любой программы
Теперь можно запустить наш сервис и убедиться, что он работает:
Зависимости и порядок запуска
Опций для настройки зависимости в systemd очень много. Прежде всего нужно отметить, что в нем есть два независимых механизма для указания порядка запуска сервисов и зависимостей между ними.
Порядок запуска сервисов
Причина существования этих опций — способность systemd запускать сервисы параллельно.
Для примера возьмем типичный веб-сервер с набором из веб-приложения FCGI, СУБД и обратного прокси. В каком порядке запускать процесс FCGI и обратный прокси, не так важно. Запросы будут работать, только когда они оба запущены, но «неверный порядок» никак не помешает им запуститься.
Зависимости
Зависимости бывают двух видов: мягкие и жесткие. Если оба сервиса запустились успешно, то никакой разницы между ними нет. Различие вступает в действие, если один из сервисов не смог запуститься: если зависимость мягкая, то зависимые сервисы все равно будут запущены, а если жесткая, то systemd не станет даже пробовать их запустить.
При копировании настроек из пакетов дистрибутива нужно быть осторожным. Разработчики дистрибутивов тоже люди и вполне могут создавать неоптимальные или ошибочные конфигурации. Кроме того, если что-то работает для одного сервиса, совсем не факт, что оно же подойдет для другого, так что сверяйтесь с документацией и тестируйте перед выпуском.
Внедрение в зависимости к чужим сервисам
В системах с System V init добавить что-то в зависимости к чужому сервису можно было, лишь отредактировав его скрипт. Такие изменения, очевидно, не переживут обновления системы, поэтому сделать их постоянными можно было бы только пересборкой пакета.
Systemd: Service File Examples
Most Linux distributions use systemd as a system and service manager.
The systemctl is the main command in systemd, used to control services.
In this tutorial i will show how to create a systemd service file that will allow you to control your service using the systemctl command, how to restart systemd without reboot to reload unit files and how to enable your new service.
I will also show and describe the most important systemd service file options with the live examples of the systemd service files.
Create Systemd Service File
Create a systemd service file /etc/systemd/system/foo-daemon.service (replace the foo-daemon with your service name):
Open the foo-daemon.service file and add the minimal service configuration options that allow this service to be controlled via systemctl :
Once the service file is changed, it needs to reload systemd configuration:
To configure a service to start automatically on boot, you need to enable it:
To check the service logs, run:
Systemd Service File Options
The common configuration items are configured in the generic [Unit] and [Install] sections.
The service specific configuration options are configured in the [Service] section.
Important [Unit] Section Options
A complete list of [Unit] section options:
Important [Install] Section Options
A complete list of [Install] section options:
Important [Service] Section Options
A complete list of [Service] section options: