Хабр Geektimes Тостер Мой круг Фрилансим

Мегапосты: Серфинг на асфальтеЭволюция Wi-FiОлдфаг про E3

Публикации

Новости

Пользователи

Хабы

Компании

Песочница

Войти Регистрация

bucefal91 22 января 2016 в 08:29

Systemd: пишем собственные .service и .target

Настройка Linux,

Системное администрирование,

*nix,

Серверное администрирование

У меня появился Linux на домашнем компьютере, и я поспешил обжиться в новой ОС. Она была установлена с systemd init process. Это было мое первое знакомство с этим новым инструментом. Cвой ноутбук я использую для каждодневной жизни и для программирования. Мне хотелось включать рабочие программы (Apache2 и MySQL) только на время, пока я их использую, чтобы не тратить впустую ресурсы своего компьютера. Дополнительно, для тестирования я написал bash скрипт, который выгружает содержимое одной из MySQL БД c жесткого диска в ОЗУ (в tmpfs) – так тесты выполняются значительно быстрее. По идее, я мог бы начинать свой рабочий день вот так:

systemctl start apache2.service
systemctl start mysqld.service
/root/scripts/mysqld-tmpfs start

И заканчивать его:

systemctl stop apache2.service
systemctl stop mysqld.service
/root/scripts/mysqld-tmpfs stop

Но мне хотелось сделать вещи “как надо”.

Чего я хотел?

Я хотел достичь 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. В какой-то момент в консоли можно набрать:

systemctl isolate my-another.target

Все сервисы из my-another.target будут включены, и все включенные сервисы не из my-another.target будут выключены. Это очень похоже на переключение runlevel в openRC. Однако, systemd поддерживает включение более чем 1 target. Вот пример:

# Эксклюзивно включаем my-favorite.target и выключаем все остальные сервисы
systemctl isolate my-favorite.target
# К уже запущенным сервисам и targets добавляем еще 1 target
systemctl start my-another.target

После выполнения этих команд в системе будет работать объединение сервисов из my-favorite.target и my-another.target.

Как я это сделал?

В итоге у меня получился вот такой mysqld-tmpfs.service файл:

Description=Mount a MySQL database into tmpfs.
# Мой /root/scripts/tmpfs скрипт может работать как при включенном, так и при выключенном mysql сервисе. Но если бы mysql сервис нужен был включенным, к примеру, то я бы добавил эти строки:
#Requires=mysqld.service
#After=mysqld.service

[Service]
# Даем знать systemd, что этот сервис представляет из себя лишь 1 процесс. Man page хорошо описывает доступные опции.
Type=oneshot
# Выполнить эту команду при запуске сервиса.
ExecStart=/root/scripts/mysqld-tmpfs start
# Выполнить эту команду при остановке сервиса.
ExecStop=/root/scripts/mysqld-tmpfs stop
# Даем знать systemd, что сервис нужно считать запущенным, даже если основной процесс прекратил свою работу. Как раз то, что мне нужно: мой процесс выполнит монтировку и после этого прекратит свою работу, но должен считаться активным, т.к. монтировка осталась в системе.
RemainAfterExit=yes

И вот такой programming.target файл:

[Unit]
Description=Working/Programming target
Requires=mysqld.service
Requires=apache2.service
# Сюда я могу дописывать новые сервисы “Requires=another.service”, если они мне понадобятся в повседневной работе.

Какие были проблемы?

При остановке 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

Результат

Я достиг своей цели. Начиная рабочий день:

systemctl start programming.target

Когда нужно выполнить тесты на своем проекте:

systemctl start mysqld-tmpfs.service

Когда я хочу демонтировать БД из tmpfs в жесткий диск (хотя на практике я так почти не делаю, а просто оставляю БД в tmpfs на целый день, и при выключении systemd за меня запускает демонтировку из tmpfs в жесткий диск):

systemctl stop mysqld-tmpfs.service

Когда я закончил работать и хочу остановить рабочие программы:

systemctl stop programming.target

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. Я попытался сделать ее компактной, и если упустил из внимания какие-то дополнительные вопросы, спрашивайте в комментариях!

Теги:

systemd

systemctl

linux

Добавить метки

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

Сохранить

Редакторы Хабра врываются в велосезон, каждый по-своему

Как это было
Реклама

Читают сейчас

User Inyerface — как не надо мучать пользователя
15,8k 51

В официальной рекламе Google обнаружили русские матерные слова
6,4k 16

Прекратите усердствовать с комментариями в коде
9,4k 65

Дешёвые и дорогие батарейки ААА
106k 343

Yota — или как можно узнать всё
21,1k 140

Криптографический алгоритм «Кузнечик»: просто о сложном
6,8k 13

+17

235

48,6k

9

Выберите рекомендации для отправки автору:

Указан только блог Орфографические ошибки Пунктуационные ошибки Отступы Текст-простыня Короткие предложения Смайлики Много форматирования Картинки Ссылки Оформление кода Рекламный характер

Отправить

Нарушение
Опишите суть нарушения

Отправить

23,0

Карма

70,5

Рейтинг

7

Подписчики

0

Подписки

bucefal91

Пользователь

Поделиться публикацией

Похожие публикации

24 июня 2019 в 15:17
Знакомство с виртуальными интерфейсами Linux: туннели

+32 11,7k 192 0

19 июня 2017 в 21:49
Автомонтирование файловых систем с systemd

+34 20,2k 204 29

14 августа 2015 в 08:48
Управление логгированием в systemd

+38 71k 415 15

Заказы

Разработать виджет для интеграции YML-файла в CMS
5 откликов 34 просмотра

3000
за проект

Исправить MTU
2 отклика 52 просмотра

2000
за проект

Разработка API. Backend, Python flask №4
6 откликов 43 просмотра

10000
за проект

Виртуальная новостная студия в Unreal Studio
5 откликов 56 просмотров

40000
за проект

Настройка Чатбота на странице в Facebook
5 откликов 22 просмотра

5000
за проект

Все заказы Разместить заказ

Комментарии 9

evg_krsk 22 января 2016 в 11:18

+1

Хорошая статья для новичков, спасибо.

alxsad 22 января 2016 в 15:35

+2

Мне кажется, в вашем кейсе можно было бы написать просто баш скрипт.

bucefal91 22 января 2016 в 17:44

0

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

Enchant 22 января 2016 в 16:30

–2

Правильней и проще было бы использовать docker.

bucefal91 22 января 2016 в 17:41

–1

Я с докером еще не работал, только слышал о нем. Ну и init process, мне кажется, именно для таких вещей как диспетчинг сервисов и был создан. Закончу со своим домашним линуксом и пойду докер осваивать :)

ValdikSS 24 января 2016 в 21:47

+1

Чего?! Это неправильней и не проще.

Prototik 22 января 2016 в 20:47

+1

Мне было лень писать 2 команды (запуск apache и запуск mysql), т.к. я знал, что обе программы всегда будут выключаться и включаться синхронно. Хотелось выполнять эту операцию одной командой.

systemctl start apache2.service mysqld.service
Ну это так, к слову. А ещё можно было настроить активацию на сокетах и вообще не пришлось бы тыкать systemctl лишний раз — нужные сервисы сами бы включились по требованию.

bucefal91 22 января 2016 в 23:07

0

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

ValdikSS 24 января 2016 в 21:49

0

Не обязательно делать target, вполне можно было сделать сервис с опцией PartOf и Wants.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Что обсуждают

Сейчас

Вчера

Неделя

Гопники теперь и на зарубежных рынках или «Почему так сложно найти нормального программиста?»
46,4k 294

Корпоративный квест
9,8k 29

Несколько простых, но полезных советов по работе с геттерами в Vuex
2k 12

Власти выделяют средства на создание российского конкурента «Википедии»
6,5k 40

User Inyerface — как не надо мучать пользователя
15,8k 51

Прекратите усердствовать с комментариями в коде
9,4k 65

Статическое распределение памяти в микроконтроллерах
4,8k 56

User Inyerface — как не надо мучать пользователя
15,8k 51

Криптографический алгоритм «Кузнечик»: просто о сложном
6,8k 13

Текстурирование, или что нужно знать, чтобы стать Художником по поверхностям. Часть 4. Модели, нормали и развертка
1,3k 12

Софт для Boeing-737 Max писался аутсорсерами, зарабатывающими $9 в час
111k 533

Не очередной язык программирования
30,4k 419

Дешёвые и дорогие батарейки ААА
106k 343

Гопники теперь и на зарубежных рынках или «Почему так сложно найти нормального программиста?»
46,4k 294

«Мы всегда верили в конкуренцию и право выбора пользователя» © Яндекс
43,4k 261

Самое читаемое

Сутки

Неделя

Месяц

User Inyerface — как не надо мучать пользователя
+36 15,8k 41 51

Дешёвые и дорогие батарейки ААА
+318 106k 374 343

Yota — или как можно узнать всё
+38 21,1k 53 140

Прекратите усердствовать с комментариями в коде
+26 9,4k 57 65

5 распространенных ошибок начинающих программистов на Python
+27 26,8k 200 61

Софт для Boeing-737 Max писался аутсорсерами, зарабатывающими $9 в час
+144 111k 122 533

Дешёвые и дорогие батарейки ААА
+318 106k 374 343

Будни техподдержки: байки про то, что бывает, когда не можешь дойти до юзера
+143 48,7k 83 120

Гопники теперь и на зарубежных рынках или «Почему так сложно найти нормального программиста?»
+42 46,4k 91 294

«Мы всегда верили в конкуренцию и право выбора пользователя» © Яндекс
+193 43,4k 48 261

Telegram. Безлимитный сетевой диск. Бесплатный
+139 121k 481 227

Софт для Boeing-737 Max писался аутсорсерами, зарабатывающими $9 в час
+144 111k 122 533

Старикам здесь не место? Программируем после тридцати пяти
+202 106k 380 625

Дешёвые и дорогие батарейки ААА
+318 106k 374 343

Сканер портов в личном кабинете Ростелекома
+288 105k 150 194

Рекомендуем
Разместить

Эволюция беспроводных сетей или что нам сулит приход Wi-Fi 6

Го в Docker. Почему все-таки Red Hat OpenShift

Ваш аккаунт

Войти

Регистрация

Разделы

Публикации

Новости

Хабы

Компании

Пользователи

Песочница

Информация

Правила

Помощь

Документация

Соглашение

Конфиденциальность

Услуги

Реклама

Тарифы

Контент

Семинары

Если нашли опечатку в посте, выделите ее и нажмите Ctrl+Enter, чтобы сообщить автору.

© 2006 – 2019 «TM»

Настройка языка

О сайте

Служба поддержки

Мобильная версия

Настройка языка

Интерфейс

Русский

English

Язык публикаций

Русский

Английский

Сохранить настройки