- Systemd: пишем собственные .service и .target
- Чего я хотел?
- Что я сделал?
- Что такое service?
- Что такое target?
- Как я это сделал?
- Какие были проблемы?
- Результат
- Cheat sheet
- Creating User’s Services With systemd
- 1. Overview
- 2. Creating and Adding a Sample Service
- 3. The user Option
- 4. Enabling, Disabling, and the Service’s Lifetime
- 5. Toggling Services of All Users
- 6. Extending Service’s Life
- 7. Inspecting Service’s Log
- 8. Conclusion
Systemd: пишем собственные .service и .target
У меня появился 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 команды (запуск 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.
Как я это сделал?
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
[Unit] Description=Working/Programming target Requires=mysqld.service Requires=apache2.service # Сюда я могу дописывать новые сервисы “Requires=another.service”, если они мне понадобятся в повседневной работе.
Какие были проблемы?
- Запустится какой-то другой сервис, который в своей декларации указывает, что он конфликтует с нашим сервисом.
- Выполнится 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
- Вызывайте 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 User’s Services With systemd
The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.
To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.
Connect your cluster and start monitoring your K8s costs right away:
1. Overview
Most Linux distributions use systemd as a contemporary service manager. Usually, we need to become root to control the services. However, we can also allow ordinary users to handle services on their own. This method is often called rootless.
In this tutorial, we’ll learn to install, manage and control services on a per-user basis.
2. Creating and Adding a Sample Service
First, let’s write a simple Bash script user_service for the service:
#!/bin/bash while true do now=$(date) me=$(whoami) echo "User $me at $now" sleep 10 done
So, the script prints the date and user name. Then, we need to copy the script to the /usr/local/bin directory and make sure that the user has executable permission on it.
Next, let’s prepare the service’s unit file user_service.service:
[Unit] Description=Script Daemon For Test User Services [Service] Type=simple #User= #Group= ExecStart=/usr/local/bin/user_service Restart=on-failure StandardOutput=file:%h/log_file [Install] WantedBy=default.target
Since the entries User and Group are meaningless for user service, we’ve commented them out. Next, we redirect the script’s output to log_file with the StandardOutput entry. It’s worth noting that the %h modifier stands for the user’s home directory.
Now, with the sudo privilege let’s copy the unit file to the /etc/systemd/user directory. In this way, systemd regards the service as the user’s one. Moreover, the service is available for all users.
3. The user Option
We can manage services as regular users with the help of the user option of systemctl. Thus, services can be enabled/disabled, started/stopped, and so on without the sudo privilege.
So, let’s install the service:
$ systemctl --user daemon-reload
$ systemctl --user start user_service.service
Next, let’s check its status:
$ systemctl --user status user_service.service ● user_service.service - Script Daemon For Test User Services Loaded: loaded (/etc/xdg/systemd/user/user_service.service; disabled; vendor preset: enable> Active: active (running) since Thu 2023-01-12 19:23:14 CET; 28s ago Main PID: 4935 (user_service) Tasks: 2 (limit: 18982) Memory: 580.0K CPU: 16ms CGroup: /user.slice/user-1000.slice/[email protected]/app.slice/user_service.service ├─4935 /bin/bash /usr/local/bin/user_service └─4972 sleep 10 sty 12 19:23:14 ubuntu systemd[1511]: Started Script Daemon For Test User Services.
Again, we should note that we’ve done that all without the sudo command.
4. Enabling, Disabling, and the Service’s Lifetime
Now let’s enable or disable the service with systemctl:
$ systemctl --user enable user_service.service Created symlink /home/joe/.config/systemd/user/default.target.wants/user_service.service → /etc/xdg/systemd/user/user_service.service.
Thus, once enabled, the service starts automatically after our login. Then, it’ll be running as long as we have some open sessions. In other words, the service instance is bound to the user, not to the session.
$ systemctl --user disable user_service.service
5. Toggling Services of All Users
With the root privilege, we can enable or disable the service for all users with the global option of systemctl:
$ sudo systemctl --global enable user_service.service Created symlink /etc/systemd/user/default.target.wants/user_service.service → /etc/systemd/user/user_service.service.
Consequently, all users obtain their own running instance of the service immediately after login.
6. Extending Service’s Life
Let’s assume that we intend our service to perform long-running tasks, e.g., calculation. So, we should extend its life beyond the end of the last session. In that case, we use loginctl with the enable-linger command:
Now, our services start right after the system boot and run till the shutdown. We should keep in mind that this applies to all our systemd services. Finally, we can turn it off with disable-linger.
7. Inspecting Service’s Log
Now let’s check the service’s log with the journalctl command. Once again, we’ll use the user option:
$ journalctl --user -u user_service # . Jan 12 19:50:20 ubuntu systemd[1511]: Stopped Script Daemon For Test User Services. Jan 12 19:50:24 ubuntu systemd[1511]: Started Script Daemon For Test User Services.
8. Conclusion
In this article, we looked at user services managed by systemd.
First, we created a simple service and added it to the systemd using the administrator privilege. Then, we managed the service as a non-sudoer thanks to the user option of systemctl.
Subsequently, we took a look at the lifetime of service and learned how to start the service without login. Finally, we examined the service’s log.