Linux systemctl create service

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

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

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

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

Чего я хотел?

Я хотел достичь 2 целей:

  1. Мне было лень писать 2 команды (запуск apache и запуск mysql), т.к. я знал, что обе программы всегда будут выключаться и включаться синхронно. Хотелось выполнять эту операцию одной командой.
  2. Дело попахивало неприятностями, если компьютер перезагрузится пока моя база данных будет сидеть в 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 обстоятельств:

  1. Запустится какой-то другой сервис, который в своей декларации указывает, что он конфликтует с нашим сервисом.
  2. Выполнится systemctl isolate some-another.target или systemctl stop this.service.
  3. Наш сервис может запросить в своей декларации останавливать себя не ленивым образом, а активным, добавив вот такую строку в [Unit] секцию: StopWhenUnneeded=true
Читайте также:  Linux самые популярные дистрибутивы 2019

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

Источник

Creating a Linux service with systemd

Sep 5, 2017 · 3 min read

While writing web applications, I often need to offload compute-heavy tasks to an asynchronous worker script, schedule tasks for later, or even write a daemon that listens to a socket to communicate with clients directly.

While there might sometimes be better tools for the job — always consider using existing software first, such as a task queue server —writing your own service can give you a level of flexibility you’ll never get when bound by the constraints of third-party software.

Читайте также:  Windows powershell как убрать

The cool thing is th a t it’s fairly easy to create a Linux service: use your favourite programming language to write a long-running program, and turn it into a service using systemd.

The program

Let’s create a small server using PHP. I can see your eyebrows rising, but it works surprisingly well. We’ll listen to UDP port 10000, and return any message received with a ROT13 transformation:

And test it in another terminal:

Cool, it works. Now we want this script to run at all times, be restarted in case of a failure (unexpected exit), and even survive server restarts. That’s where systemd comes into play.

Turning it into a service

Let’s create a file called /etc/systemd/system/rot13.service :

  • set your actual username after User=
  • set the proper path to your script in ExecStart=

That’s it. We can now start the service:

And automatically get it to start on boot:

Going further

Now that your service (hopefully) works, it may be important to dive a bit deeper into the configuration options, and ensure that it will always work as you expect it to.

Starting in the right order

You may have wondered what the After= directive did. It simply means that your service must be started after the network is ready. If your program expects the MySQL server to be up and running, you should add:

Restarting on exit

By default, systemd does not restart your service if the program exits for whatever reason. This is usually not what you want for a service that must be always available, so we’re instructing it to always restart on exit:

You could also use on-failure to only restart if the exit status is not 0 .

By default, systemd attempts a restart after 100ms. You can specify the number of seconds to wait before attempting a restart, using:

Avoiding the trap: the start limit

I personally fell into this one more than once. By default, when you configure Restart=always as we did, systemd gives up restarting your service if it fails to start more than 5 times within a 10 seconds interval. Forever.

There are two [Unit] configuration options responsible for this:

The RestartSec directive also has an impact on the outcome: if you set it to restart after 3 seconds, then you can never reach 5 failed retries within 10 seconds.

The simple fix that always works is to set StartLimitIntervalSec=0 . This way, systemd will attempt to restart your service forever.

It’s a good idea to set RestartSec to at least 1 second though, to avoid putting too much stress on your server when things start going wrong.

As an alternative, you can leave the default settings, and ask systemd to restart your server if the start limit is reached, using StartLimitAction=reboot .

Is that really it?

That’s all it takes to create a Linux service with systemd: writing a small configuration file that references your long-running program.

Systemd has been the default init system in RHEL/CentOS, Fedora, Ubuntu, Debian and others for several years now, so chances are that your server is ready to host your homebrew services!

Источник

Systemd за пять минут

Наша компания занимается администрированием веб-серверов на базе CentOS. Довольно часто наши клиенты используют веб-приложения на базе python, ruby или java. Для автозапуска подобных приложений есть готовые шаблоны для написания стартап-скриптов. Но прогресс не стоит на месте, вышел уже второй релиз CentOS 7 и, следуя старой традиции «не ставить dot-zero релизы на продакшен», мы начинаем предлагать клиентам сервера на базе CentOS 7.1 (1503).

Читайте также:  Версия для mac os что это такое

В CentOS7, так же как и в его родителе RHEL7, используется systemd — менеджер системы и служб для Linux, совместимый со скриптами инициализации SysV и LSB. systemd обеспечивает возможности агрессивной параллелизации и много всего прочего.

Огромный монстр с множеством возможностей, гибкими настройками и мегабайтами документации…

Но что делать, если стоит задача быстро-быстро, вот прямо вчера, сделать автозапуск некоего сервиса?
Давайте выжмем из документации минимально необходимый набор информации для создания простых старт-стоп скриптов.

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

Все эти юниты разложены в трех каталогах:

/usr/lib/systemd/system/ – юниты из установленных пакетов RPM — всякие nginx, apache, mysql и прочее
/run/systemd/system/ — юниты, созданные в рантайме — тоже, наверное, нужная штука
/etc/systemd/system/ — юниты, созданные системным администратором — а вот сюда мы и положим свой юнит.

Юнит представляет из себя текстовый файл с форматом, похожим на файлы .ini Microsoft Windows.

[Название секции в квадратных скобках]
имя_переменной = значение

Для создания простейшего юнита надо описать три секции: [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 могла отслеживать основной процесс:

Команды на старт/стоп и релоад сервиса

ExecStart=/usr/local/bin/bundle exec service -C /work/www/myunit/shared/config/service.rb —daemon
ExecStop=/usr/local/bin/bundle exec service -S /work/www/myunit/shared/tmp/pids/service.state stop
ExecReload=/usr/local/bin/bundle exec service -S /work/www/myunit/shared/tmp/pids/service.state restart

Тут есть тонкость — systemd настаивает, чтобы команда указывала на конкретный исполняемый файл. Надо указывать полный путь.

Таймаут в секундах, сколько ждать system отработки старт/стоп команд.

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

В секции [Install] опишем, в каком уровне запуска должен стартовать сервис

multi-user.target или runlevel3.target соответствует нашему привычному runlevel=3 «Многопользовательский режим без графики. Пользователи, как правило, входят в систему при помощи множества консолей или через сеть»

Вот и готов простейший стартап скрипт, он же unit для systemd:
myunit.service

Кладем этот файл в каталог /etc/systemd/system/

Смотрим его статус systemctl status myunit

Видим, что он disabled — разрешаем его
systemctl enable myunit
systemctl -l status myunit

Если нет никаких ошибок в юните — то вывод будет вот такой:

Запускаем сервис
systemctl start myunit

Смотрим красивый статус:
systemctl -l status myunit

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

Источник

Оцените статью