У меня появился 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. Я попытался сделать ее компактной, и если упустил из внимания какие-то дополнительные вопросы, спрашивайте в комментариях!
Systemd, интерактивные скрипты и таймеры
Введение
При разработке под linux возникают задачи создания интерактивных скриптов, выполняемых при включении или завершении работы системы. В system V это делалось легко, но с systemd вносит коррективы. Зато оно умеет свои таймеры.
Зачем нужны target
Пример target при включении(обзор возможности) с запуском интерактивного скрипта
Описание самого target:
Данный target запустится, когда будет запущен multi-user.target и вызовет installer.service. При этом таких сервисов может быть несколько.
И наконец, пример выполняемого скрипта:
Самое главное — выбрать final.target — target, к которому система должна придти при запуске. В процессе запуска systemd пройдёт по зависимостям и запустит всё нужное.
Выбрать final.target можно разными способами, я использовал для этого опцию загрузчика.
Итоговый запуск выглядит так:
Подготовка прошивки к запуску
При создании прошивок всегда возникает задача восстановления состояния системы при старте и его сохранении при выключении. Под состоянием подразумеваются конфигурационные файлы, дампы базы данных, настройки интерфейсов и тд.
Systemd запускает процессу в одном таргете параллельно. Есть зависимости, которые позволяют определить последовательность запуска скриптов.
Важны файлы сервисов, именно они выставляют последовательность их запуска
Как видно, я поставил зависимости, что бы сначала отработал мой скрипт, а только потом поднималась сеть и стартовала СУБД.
И второй сервис(подготовка zabbix)
Здесь немного сложнее.Запуск так же в multi-user.target, но ПОСЛЕ запуска СУБД postgresql и моего setting_restore. Но ПЕРЕД запуском служб zabbix.
Сервис с таймером для logrotate
Systemd может заменить CRON. Серьезно. Причем точность не до минуты, а до секунды(а вдруг понадобится).А можно создать монотонный таймер, вызываемый по таймауту от события.
Именно монотонный таймер, считающий время от запуска машины, я и создал.
Для этого потребуется 2 файла
logrotateTimer.service — собственно описание сервиса:
Всё просто — описание команда запуска.
Второй файл logrotateTimer.timer — вот он и задает работу таймеров:
Интерактивный скрипт при выключении и свой таргет выключения
В другой разработке мне пришлось делать более сложный вариант выключения машины — через собственный таргет, что бы выполнить множество действий. Обычно рекомендуется создать сервис oneshot с опцией RemainAfterExit, но это не дает создать интерактивный скрипт.
А дело в том, что команды, запускаемые опцией ExecOnStop выполняются вне TTY! Проверить просто — вставьте команду tty и сохраните её вывод.
Поэтому я реализовал выключение через свой таргет. На 100% правильность не претендую, но это работает!
Как это делалось(в общих чертах):
Создал таргет my_shutdown.target, который ни от кого не зависел:
my_shutdown.target
При переходе в этот таргет(через systemctl isolate my_shutdwn.target), он запускал сервис my_shutdown.service, задача которого простая — выполнить скрипт my_shutdown.sh:
Примечание. Использование файлов /tmp/reboot и /tmp/shutdown. Нельзя вызвать target с параметрами. Можно только service.
Но я использую target, что бы иметь гибкость в работе и гарантированный порядок выполнения действий.
Однако, самое интересное было потом. Машину же надо выключить/перезагрузить. И тут есть 2 варианта:
Я выбрал первый вариант. В systemd reboot(как и poweroff) являются симлинками на systemd.
Поэтому их можно заменить на свои скрипты:
reboot
Systemd за пять минут
Наша компания занимается администрированием веб-серверов на базе CentOS. Довольно часто наши клиенты используют веб-приложения на базе python, ruby или java. Для автозапуска подобных приложений есть готовые шаблоны для написания стартап-скриптов. Но прогресс не стоит на месте, вышел уже второй релиз CentOS 7 и, следуя старой традиции «не ставить dot-zero релизы на продакшен», мы начинаем предлагать клиентам сервера на базе CentOS 7.1 (1503).
В CentOS7, так же как и в его родителе RHEL7, используется systemd — менеджер системы и служб для Linux, совместимый со скриптами инициализации SysV и LSB. systemd обеспечивает возможности агрессивной параллелизации и много всего прочего.
Огромный монстр с множеством возможностей, гибкими настройками и мегабайтами документации…
Но что делать, если стоит задача быстро-быстро, вот прямо вчера, сделать автозапуск некоего сервиса?
Давайте выжмем из документации минимально необходимый набор информации для создания простых старт-стоп скриптов.
Systemd запускает сервисы описанные в его конфигурации.
Конфигурация состоит из множества файлов, которые по-модному называют юнитами.
Все эти юниты разложены в трех каталогах:
/usr/lib/systemd/system/ – юниты из установленных пакетов RPM — всякие nginx, apache, mysql и прочее
/run/systemd/system/ — юниты, созданные в рантайме — тоже, наверное, нужная штука
/etc/systemd/system/ — юниты, созданные системным администратором — а вот сюда мы и положим свой юнит.
[Название секции в квадратных скобках]
имя_переменной = значение
Для создания простейшего юнита надо описать три секции: [Unit], [Service], [Install]
В секции Unit описываем, что это за юнит:
Названия переменных достаточно говорящие:
Далее следует блок переменных, которые влияют на порядок загрузки сервисов:
Запускать юнит после какого-либо сервиса или группы сервисов (например network.target):
After=syslog.target
After=network.target
After=nginx.service
After=mysql.service
В итоге переменная Wants получается чисто описательной.
Если сервис есть в Requires, но нет в After, то наш сервис будет запущен параллельно с требуемым сервисом, а не после успешной загрузки требуемого сервиса
В секции Service указываем какими командами и под каким пользователем надо запускать сервис:
(по умолчанию): systemd предполагает, что служба будет запущена незамедлительно. Процесс при этом не должен разветвляться. Не используйте этот тип, если другие службы зависят от очередности при запуске данной службы.
systemd предполагает, что служба запускается однократно и процесс разветвляется с завершением родительского процесса. Данный тип используется для запуска классических демонов.
Также следует определить PIDFile=, чтобы systemd могла отслеживать основной процесс:
Команды на старт/стоп и релоад сервиса
Тут есть тонкость — systemd настаивает, чтобы команда указывала на конкретный исполняемый файл. Надо указывать полный путь.
Таймаут в секундах, сколько ждать system отработки старт/стоп команд.
Попросим systemd автоматически рестартовать наш сервис, если он вдруг перестанет работать.
Контроль ведется по наличию процесса из PID файла
В секции [Install] опишем, в каком уровне запуска должен стартовать сервис
multi-user.target или runlevel3.target соответствует нашему привычному runlevel=3 «Многопользовательский режим без графики. Пользователи, как правило, входят в систему при помощи множества консолей или через сеть»
Вот и готов простейший стартап скрипт, он же unit для systemd:
myunit.service
Кладем этот файл в каталог /etc/systemd/system/
Смотрим его статус systemctl status myunit
Если нет никаких ошибок в юните — то вывод будет вот такой:
linux-notes.org
Systemd Unit — это скрипт, который выполняет различные действия ( все то, что пропишешь в него). Я обычно пишу такие скрипты для запуска различных служб которые компилирую руками или когда использую готовое ПО.
Что дадут вам Systemd юниты?
Units — это объекты, которыми может управлять система (В основном — стандартизированное представление системных ресурсов, которыми может управлять набор демонов).
Units, в некотором смысле можно назвать службами или заданиями, однако юнит имеет гораздо более широкое определение, поскольку он может использоваться для абстрактных служб, сетевых ресурсов, устройств, монтирования файловой системы и изолированных пулов ресурсов.
Некоторые функции, которые легко реализовать:
Есть много других преимуществ, которые имеют системные юниты над рабочими элементами других систем, но это должно дать вам представление о мощности, которую можно использовать с помощью собственных конфигурационных директив.
Как я могу найти/ Где находятся Systemd Unit в Unix/Linux?
Файлы, определяющие, как systemd будет обрабатывать unit, можно найти в разных местах, каждое из которых имеет разные приоритеты и смысл.
Все копии системных файлов системы (я имею ввиду всех юнитов) можно найти в /lib/systemd/system каталоге. Файлы, хранящиеся здесь, могут быть запущены и остановлены по требованию, но во время сеанса. Это будет общий файл ванильной единицы, который часто записывается сторонними разработчиками проекта, которые должны работать в любой системе, которая развертывает systemd в своей стандартной реализации. Вы не должны редактировать файлы в этом каталоге. Вместо этого вы должны переопределить файл (если необходимо) используя другое расположение файла, которое заменит файл в этом месте.
Если вы хотите изменить работу некоторого устройства, то для этого нужно перейти в /etc/systemd/system директорию, потому что — файлы, найденные в этом каталоге, имеют приоритет над любыми другими местами в файловой системы.
Если вы хотите переопределить только определенные директивы из системных unit файлов, вы можете фактически предоставить фрагменты unit файла в подкаталоге. Они будут добавлять или изменять директивами копии системы, позволяя указать только параметры, которые вы хотите изменить.
Также есть место для определения run-time unit-ов в /run/systemd/system. Файлы, найденные в этом каталоге, имеют приоритет между /etc/systemd/system и/lib/systemd/system.
Файлы в этом месте дают меньший приоритет, чем прежнее местоположение, но больший приоритет, чем последнее. Сам процесс systemd использует это местоположение для динамически создаваемых unit файлов, созданных во время выполнения. Этот каталог можно использовать для изменения поведения устройства системы в течение всего сеанса. Все изменения, внесенные в этот каталог, будут потеряны при перезагрузке сервера.
Типы systemd Unit файлов
Самый простой способ определить тип устройства — это посмотреть на его суффикс, который добавляется к концу имени ресурса (юнита). В следующем списке описаны типы unit-ов, доступных для systemd:
Структура systemd Unit файлов
Внутренняя структура файлов организована с помощью разделов. Разделы обозначаются двумя квадратными скобками «[» и «]» с именем раздела, заключенного внутри. Каждый раздел продолжается до начала следующего раздела или до конца файла.
Названия разделов хорошо определены и учитывают регистр. Таким образом, раздел [Unit] не будет интерпретироваться правильно, если он записан как [UNIT]. Если вам нужно добавить нестандартные разделы для анализа (отличными от systemd), вы можете добавить X-префикс к имени раздела.
В этих разделах поведение устройства и метаданные определяются с помощью простых директив с использованием формата ключа-значения с назначением, обозначенным знаком равенства, например:
В случае переопределения файла (например, содержащегося в каталоге unit.type.d) директивы могут быть сброшены путем назначения пустой строкой. Например, копия единичного файла системы может содержать директиву, заданную таким образом:
Значение default_value можно исключить в файле переопределения, указав Directive1 (без значения), например:
Сейчас рассмотрим каждый из юнитов по отдельности.
Первый раздел, найденный в большинстве юнит-файлов, — это раздел [Unit]. Обычно его используют для определения метаданных устройства и настройки отношения устройства к другим устройствам.
Хотя порядок разделов не имеет значения для systemd при парсинге файла, этот раздел часто размещается сверху, потому что он предоставляет обзор устройства. Некоторые общие директивы, которые вы найдете в разделе [Unit]:
Используя эти директивы и несколько других, можно установить общую информацию об устройстве и его взаимосвязь в операционной системой.
[Install]
В самом конце файла, расположен — [Install] секция. Этот раздел является дополнительным и используется для определения поведения или юнита, если он включен или отключен. Включение устройства означает, что он автоматически запускается при загрузке. По сути, это достигается путем фиксации рассматриваемого устройства на другом блоке, который находится где-то в строке единиц, которые нужно запустить при загрузке.
Из-за этого, только юниты, которые могут быть включены, будут иметь этот раздел. Директивы внутри, говорят что должно произойти, когда устройство включено:
[Service]
Раздел [Service] используется для предоставления конфигурации, которая применима только для служб. Одной из основных вещей, которые должны быть указаны в разделе [Service], является Type= служба. Это классифицирует услуги по их процессу и демонизирующему поведению. Это важно, потому что он сообщает systemd, как правильно управлять службой и узнать его состояние.
Директива Type= может быть одной из следующих:
При использовании определенных типов служб, могут потребоваться некоторые дополнительные директивы. Например:
До сих пор мы обсуждали некоторую предварительную информацию, но фактически не говорили, как управлять нашими услугами. Для этого имеются следующие директивы:
[Socket]
Socket очень часто встречаются в конфигурациях systemd, потому что многие службы реализуют активацию на основе сокетов, чтобы обеспечить лучшую распараллеливание и гибкость. Каждый блок socket-а должен иметь соответствующий сервисный модуль, который будет активирован, когда сокет получает активность.
Разрушая управление сокета вне самой службы, сокеты могут быть инициализированы раньше, и связанные службы могут часто запускаться параллельно. По умолчанию имя сокета будет пытаться запустить службу с тем же именем после получения соединения. Когда служба инициализируется, сокет будет передан ему, что позволит ему начать обработку любых буферизованных запросов.
Чтобы указать фактический сокет, эти директивы являются общими:
Существует больше типов директив для установки соединений, но те которые указаны выше, являются наиболее распространенными.
Другие характеристики сокетов можно контролировать с помощью дополнительных директив:
[Mount]
Модули монтирования позволяют управлять точкой монтирования изнутри systemd. Точки монтирования называются в соответствии с управляемым им каталогом с применением алгоритма перевода.
Например, ведущая косая черта (слеш или «/») удаляется, все остальные косые черты переводится в тире «-», и все тире и непечатаемые символы заменяются escape-кодами стиля С. Результат этого перевода используется как имя узла монтирования. Unit-ы монтирования будут иметь неявную зависимость от других монтировок над ней в иерархии.
Mount юниты часто переводятся непосредственно из файла /etc/fstab во время процесса загрузки. Для автоматически созданных определений единиц и те, которые вы хотите определить в единичном файле:
[Automount]
[Automount] Раздел довольно прост, разрешены только следующие два варианта:
Swap модули используются для настройки подкачки (свопинга) в системе. Юниты должны быть названы по названию файла или устройства подкачки, используя тот же перенос файловой системы, о котором говорилось выше.
Как и mount параметры, блоки swap могут быть автоматически созданы из /etc/fstab или могут быть сконфигурированы через выделенный unit файл.
Раздел [Swap] может содержать следующие директивы для конфигурации:
Блок path, определяет путь файловой системы, который systmed может отслеживать изменения. Должен существовать другой блок, который будет активирован, когда определенная активность будет обнаружена в местоположении пути. Активность пути определяется тем, что он не влияет на события.
Раздел [Path] может содержать следующие директивы:
[Timer]
Timer юнит, используются для планирования задач для работы в определенное время или после определенной задержки. Этот тип устройства заменяет или дополняет некоторые функции cron-а и демонов. Должен быть предоставлен соответствующий блок, который будет активирован, когда таймер будет достигнут.
Раздел [Timer] может содержать некоторые из следующих директив:
[Slice]
Хватит теории, перейдем к практике.
Пишем systemd Unit файл
Первое что стоит сделать — так это проверить инициализацию:
PS: Вот небольшая статья:
Файлы шаблонов блоков могут быть определены, потому что они содержат символ @ после имени базового блока и перед суффиксом блокового типа. Имя файла блока с шаблоном может выглядеть следующим образом:
Из созданного блока ( что выше) можно создать экземпляр др блока, который выглядит следующим образом:
Мощность файлов шаблонных модулей в основном проявляется благодаря возможности динамически подставлять соответствующую информацию в определение устройства в соответствии со средой (ENV). Это делается путем установки директив в файл шаблона как обычно, но заменяя определенные значения или части значений спецификаторами переменных.
Ниже приведены некоторые из наиболее распространенных спецификаторов, которые будут заменены, когда юнит-экземпляра интерпретируется с соответствующей информацией:
Используя приведенные выше идентификаторы в шаблоне файла, Systemd заполнит правильные значения при интерпретации шаблона для создания юнит-экземпляра.
Примеры systemd Unit файлов
Рассмотрим следующий пример, — это написания systemd скрипта для запуска tomcat-а. Для этого, открываем файл:
И записываем в него следующий код:
Добавим томкат в автозагрузку ОС:
Чтобы проверить статус, выполняем:
Как видим, все четко работает. На этом, у меня все! Больше примеров будет дальше. Я буду добавлять их по мере необходимости. Но в основном, я использую шаблон что выше ( только немного его видоизменяю).
Статья «Пишем systemd Unit файл» завершена.
10 thoughts on “ Пишем systemd Unit файл ”
Вроде даже в книгах читал по модули systemd, но тут очень хорошо так на пальцах всё показано, для дальнейшего понимания очень полезно. Спасибо!
Можно ли как-то указать куда писать лог-файл. Есть python программа, unit к ней оформил, всё работает, только вот лог она пишет в файл syslog, а нужно в другой в /var/log/python.log например.
Да, можно. Нужно прописать в [Service] секции, следующии строки:
На форуме капча не работает зарегистрироваться нельзя так что пишу сюда.
Пытаюсь оформить сервис для vmpsd. Не запускается.
Если добавить:
StandardOutput=
StandardError=
То получаю:
Failed to parse output specifier, ignoring:
Что-то не так делаешь, нужно привести к следующему виду:
[Service]
Type=forking
StandardOutput=/var/StandardOutput.log
StandardError=/var/StandardError.log
У меня такая же ошибка как у vasyun. Type=idle, если сделать forking, то вообще юнит не запускается, приходиться жать Ctrl-C.
Чтобы писать лог в файл, нужно указать
StandardOutput=file:/path/to/file
StandardError=file:/path/to/file
Т.е. после знака «=» нужен «file:». После этого всё пишет.
Спасибо Автор! Ваш труд оказался полезным! Всех благ Вам!
Добрый день!
Можете объяснить точный функционал опции After?
Она несет только информацию для пользователя, что именно он должен запустить перед запуском текущего юнита?
Или все что там прописано автоматически запускается при запуске текущего юнита?
Первое и не просто для пользователя, а для правильной последовательности запуска того юнита, где встречается after и в данном случае не перед, а после.
Взято с другого сайта:
Запускать юнит после какого-либо сервиса или группы сервисов (например network.target):
After=network.target
Добавить комментарий Отменить ответ
Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.