- Systemd: пишем собственные .service и .target
- Чего я хотел?
- Что я сделал?
- Что такое service?
- Что такое target?
- Как я это сделал?
- Какие были проблемы?
- Результат
- Cheat sheet
- Как создать свой сервис для Linux
- Основы создания сервиса Linux
- Сервисы типа oneshot — долой rc.local
- Создание сервиса из любой программы
- Зависимости и порядок запуска
- Порядок запуска сервисов
- Зависимости
- Внедрение в зависимости к чужим сервисам
- Как в Linux пользоваться командой systemctl
- Общий синтаксис
- Шпаргалка по часто используемым командам systemctl
- Автозапуск
- Редактирование сервисов
Systemd: пишем собственные .service и .target
У меня появился Linux на домашнем компьютере, и я поспешил обжиться в новой ОС. Она была установлена с systemd init process. Это было мое первое знакомство с этим новым инструментом. Cвой ноутбук я использую для каждодневной жизни и для программирования. Мне хотелось включать рабочие программы (Apache2 и MySQL) только на время, пока я их использую, чтобы не тратить впустую ресурсы своего компьютера. Дополнительно, для тестирования я написал bash скрипт, который выгружает содержимое одной из MySQL БД c жесткого диска в ОЗУ (в tmpfs) – так тесты выполняются значительно быстрее. По идее, я мог бы начинать свой рабочий день вот так:
И заканчивать его:
Но мне хотелось сделать вещи “как надо”.
Чего я хотел?
Я хотел достичь 2 целей:
- Мне было лень писать 2 команды (запуск apache и запуск mysql), т.к. я знал, что обе программы всегда будут выключаться и включаться синхронно. Хотелось выполнять эту операцию одной командой.
- Дело попахивало неприятностями, если компьютер перезагрузится пока моя база данных будет сидеть в 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 файл:
Какие были проблемы?
При остановке programming.target почему-то нижележащие apache2.service и mysqld.service не останавливались. Почитав как следует man page, я нашел проблему: systemd останавливает сервисы “лениво” — если никто не требует запущенный сервис, и он не был запущен явным образом, а как зависимость для какого-то другого сервиса, то systemd остановит его только при одном из 3 обстоятельств:
- Запустится какой-то другой сервис, который в своей декларации указывает, что он конфликтует с нашим сервисом.
- Выполнится systemctl isolate some-another.target или systemctl stop this.service.
- Наш сервис может запросить в своей декларации останавливать себя не ленивым образом, а активным, добавив вот такую строку в [Unit] секцию: StopWhenUnneeded=true
Декларации “чужих” сервисов можно менять создавая файлы /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:
- Вызывайте systemctl daemon-reload, если вы изменили декларацию чего-либо (systemd считает файлы декларации заново)
- systemctl start my-name.(service|target) – запуск сервиса или target
- systemctl stop my-name.(service|target) – остановка сервиса или target
- systemctl enable my-name.service – сервисы могут декларировать при каких включенных targets они должны включаться. Для этого используется [Install] секция в файле декларации сервиса. Вы, как сисадмин, имеете власть на установку этого “пожелания” сервиса. Часто сервисы “устанавливаются” в target по умолчанию multi-user.target или в похожее.
- systemctl disable my-name.service – обратная операция по отношению к enable: деассоциировать связь между my-name.service и targets, которые он запросил в [Install] секции своей декларации.
- systemctl isolate my.target — включить все сервисы из my.target и выключить все остальные включенные сервисы.
- systemctl status my-name.(service|target) — узнать статус (запущен/остановлен) у сервиса или target.
Надеюсь, эта статья кому-то поможет при осваивании systemd. Я попытался сделать ее компактной, и если упустил из внимания какие-то дополнительные вопросы, спрашивайте в комментариях!
Источник
Как создать свой сервис для 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 перехватит стандартные потоки ввода-вывода и будет следить за жизнью процесса.
Для демонстрации напишем программу на Python, которая просто выводит сообщение в бесконечном цикле. Сохраним в /usr/local/bin/test.py следующее:
Затем создадим для нее файл сервиса в /lib/systemd/system/smart-test.service :
Теперь можно запустить наш сервис и убедиться, что он работает:
Стандартный вывод программы пишется в journald, и его можно увидеть в journalctl -u smart-test . Ради интереса посмотрим на работу опции Restart=on-failure . Остановим наш процесс с помощью kill -9 $
Для настоящих демонов нужно использовать тип 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.
При копировании настроек из пакетов дистрибутива нужно быть осторожным. Разработчики дистрибутивов тоже люди и вполне могут создавать неоптимальные или ошибочные конфигурации. Кроме того, если что-то работает для одного сервиса, совсем не факт, что оно же подойдет для другого, так что сверяйтесь с документацией и тестируйте перед выпуском.
Жесткие зависимости можно указать с помощью опции 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 имеет приоритет.
Источник
Как в Linux пользоваться командой systemctl
Вместе с подсистемой systemd появилась команда systemctl. Она позволяет управлять основными процессами Linux. Ниже представлена небольшая инструкция в виде шпаргалки наиболее используемых команд.
Общий синтаксис
Без параметров, systemctl показывает список запущенных служб, точек монтирования, устройств и других юнитов.
Примерный вывод команды:
1) название юнита;
2) тип юнита (например, service: служба или демон, mount: точка монтирования, device: устройства);
3) состояние юнита (загружен или нет);
4) обобщенный статус юнита (active: выполняется, inactive: не был запущен, maintenance: требуется внимание администратора);
5) текущий статус (запущен или нет);
6) описание.
Шпаргалка по часто используемым командам systemctl
1. Посмотреть статус службы:
systemctl status network
* покажет статус службы на примере сети network
2. Запустить сервис:
systemctl start mysql
* запустит сервис баз данных на примере mysql
3. Остановить службу:
systemctl stop ntpd
* остановит сервис времени ntpd
4. Перезапустить службу:
systemctl restart nginx
* перезапустит веб-сервер nginx
5. Включить автозапуск службы:
systemctl enable apache
* разрешит автозапуск веб-сервера apache
6. Отключить автозапуск службы:
systemctl disable firewalld
* запретит автозапуск брандмауэра firewalld
7. Выполнить команду на удаленной системе:
systemctl —host root@192.168.0.15 stop cron
* остановит cron на компьютере с IP-адресом 192.168.0.15, подключившись под учетной записью root.
8. Перезагрузить сервер:
* перезагрузит локальный сервер.
9. Проверка работы сервиса.
Выполняется с помощью опции is-active:
systemctl is-active docker
* в данном примере мы проверим работу службы docker.
а) Если сервис запущен, мы увидим:
б) Если не запущен:
в) Если такого сервиса нет в системе:
Если сервис не работает или его нет в системе, команда вернет код ошибки, таким образом конструкция:
systemctl is-active docker && docker run hello-world
. приведет к выполнению команды docker run hello-world только в том случае, если сервис docker работает.
Автозапуск
Подсистему systemd также можно использовать для автозапуска сервисов или скриптов. Для этого в каталоге /usr/lib/systemd/system создаем юнит (файл) с расширением .service. Подробнее разберем на примере сервиса bind:
Содержимое может быть следующего содержания:
[Unit]
Description=Berkeley Internet Name Domain (DNS)
Wants=nss-lookup.target
Wants=named-setup-rndc.service
Before=nss-lookup.target
After=network.target
After=named-setup-rndc.service
[Service]
Type=forking
Environment=NAMEDCONF=/etc/named.conf
EnvironmentFile=-/etc/sysconfig/named
Environment=KRB5_KTNAME=/etc/named.keytab
PIDFile=/run/named/named.pid
ExecStartPre=/bin/bash -c ‘if [ ! «$DISABLE_ZONE_CHECKING» == «yes» ]; then /usr/sbin/named-checkconf -z «$NAMEDCONF»; else echo «Checking of zone files is disabled»; fi’
ExecStart=/usr/sbin/named -u named -c $
ExecReload=/bin/sh -c ‘/usr/sbin/rndc reload > /dev/null 2>&1 || /bin/kill -HUP $MAINPID’
ExecStop=/bin/sh -c ‘/usr/sbin/rndc stop > /dev/null 2>&1 || /bin/kill -TERM $MAINPID’
PrivateTmp=true
* как правило, файл разделен на 3 части:
- Unit — позволяет определить метаданные для юнита.
- Service — раздел для основной конфигурации юнита.
- Install — определение поведения для юнита при его включении или отключении.
Подробнее можно почитать о структуре и возможных опциях на странице https://linux-notes.org/pishem-systemd-unit-fajl/
После внесения изменений и сохранения файла, необходимо перечитать изменения командой:
Теперь можно разрешить автозапуск:
systemctl enable named
Редактирование сервисов
Если мы хотим внести изменения в юнит-файл сервиса, который был установлен с последним, необходимо использовать drop-in файл или файл переопределения настроек. В противном случае, после обновления программы наши изменения могут быть удалены.
И так, мы для примера взяли юнит для bind. Чтобы создать для него drop-in файл, вводим:
systemctl edit named
И вносим, например, такие изменения:
* будет создан файл /etc/systemd/system/named.service.d/override.conf, который будет переопределять настройки основного юнит-файла. В данном примере, мы указываем на необходимость перезапуска сервиса при сбое.
Чтобы убедиться в использовании Drop-In файла смотрим статус сервиса:
Источник