Юнит-файлы нужны для создания функциональности, которая необходима на сервере.
Обычно под юнитом systemd
понимается сервис, но на самом существует множество видов юнитов. Посмотрим полный список юнитов:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$ systemctl -t help
Available unit types:
service
mount
swap
socket
target
device
automount
timer
path
slice
scope
|
Директории с юнитами:
/usr/lib/systemd/system
: системные юнит-файлы. Они устанавливаются из пакетов вместе с nginx, mariadb и т.д.
/etc/systemd/system
: юниты, созданные вручную, они затеняют системные юнит-файлы.
/run/systemd/system
-runtime-юниты, которые были сгенерированны автоматически.
Пользовательские юнит файлы пользователя с именем username
хранятся по аналогии в следующих директориях:
/usr/lib/systemd/username
/etc/systemd/username
/run/sustemd/username
Для примера создадим юнит для prometheus
:
Посмотреть юнит-файл: systemctl cat prometheus
Редактировать юнит-файл: sudo systemctl edit --full prometheus
Откатить все изменения в юнит-файле: systemctl revert prometheus
Создадим юнит-файл $ sudo systemctl edit --force --full prometheus
и добавим содержимое(ниже есть краткая версия):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
# открываем секцию Unit
[Unit]
# Всё что указано здесь будет отображаться в service status
Description=Prometheus
# В systemd зависимости определяются правильным построением файлов юнитов. Например, юнит А требует, чтобы уже был запущен юнит B, для этого добавим строки Requires=B и After=B в раздел [Unit] юнит-файла A. Если зависимость является необязательной, укажите Wants=B и After=B соответственно. Обратите внимание, что Wants= и Requires= не подразумевают After=. Если After= не указать, то юниты будут запущены параллельно.
Wants=network.target
After=network.target
# Здесь указано всё о запускаемой программе
[Service]
# systemd запустит эту службу незамедлительно. Процесс при этом не должен разветвляться (fork). Если после данной службы должны запускаться другие, то следует использовать Type=forking.
Type=simple
# указываем пользователя и группу, от имени которых будет запускаться процесс:
User=prometheus
Group=prometheus
# указываем строку запуска приложения
ExecStart=/usr/local/bin/prometheus \
--config.file /usr/local/etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/ \
--storage.tsdb.retention.size "$VAL"
# указываем как делать reload сервиса
ExecReload=/bin/kill -HUP $MAINPID
# домашняя директория, отткуда будет запускаться бинарник, для прометеуса это не важно, тут для примера.
WorkingDirectory=/usr/local/etc/prometheus
# запущенное приложение должно отправлять сигнал что оно живо sd_notify(0, "WATCHDOG=1"), если в течении 10 секунд сигнала не будет, то система решит, что приложение зависло и перезапустит его.
WatchdogSec=10
# определяем перезапуск после сбоя
Restart=on-failure
# перед повторным запуском будет задержка, указанная здесь
RestartSec=10s
# Umask для процесса и его потомков. umask - инвертированный chmod.
UMask=0002
# ну и передадим переменную окружения VAL=26G
Environment=VAL=26G
# определяем, что директории /home, /root и /run/user будут недоступны и невидимы процессу
ProtectHome=true
# определяем что каталоги /usr, /boot, /efi и /etc будут доступны только read-only режиме
ProtectSystem=full
# определяем запуск сервиса на run lvl: multi-user runlevel
[Install]
WantedBy=multi-user.target
|
Run Lvl |
Target Units |
Description |
0 |
runlevel0.target, poweroff.target |
Shut down and power off |
1 |
runlevel1.target, rescue.target |
Set up a rescue shell |
2,3,4 |
runlevel[234].target, multi-user.target |
Set up a non-gfx multi-user shell |
5 |
runlevel5.target, graphical.target |
Set up a gfx multi-user shell |
6 |
runlevel6.target, reboot.target |
Shut down and reboot the system |
Type=oneshot
удобен для сценариев, которые выполняют одно задание и завершаются. Если задать параметр RemainAfterExit=yes
, то systemd будет считать процесс активным даже после его завершения.
Краткая версия:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
[Unit]
Description=Prometheus
Wants=network.target
After=network.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/prometheus \
--config.file /usr/local/etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/ \
--storage.tsdb.retention.size "$VAL"
ExecReload=/bin/kill -HUP $MAINPID
WorkingDirectory=/usr/local/etc/prometheus
WatchdogSec=10
Restart=on-failure
RestartSec=10s
UMask=0002
Environment=VAL=26GB
ProtectHome=true
ProtectSystem=full
[Install]
WantedBy=multi-user.target
|
Если юнит редактировался не с помощью systemctl edit --full prometheus
, а вручную, нужно перечитать все юниты:
1
|
$ sudo systemctl daemon-reload
|
Теперь включим и запустим юнит prometheus
"
1
2
3
4
|
$ sudo systemctl enable prometheus
$ sudo systemctl start prometheus
# одной командой
$ sudo systemctl enable --now prometheus
|
Команда systemctl edit prometheus
создаст override-файл, в котором можно описать директивы, которые хотим добавить или изменить в существующем юните.
Чтобы посмотреть логи по юниту:
1
2
3
4
5
6
7
8
9
|
$ sudo journalctl --unit=prometheus
# с момента старта системы
$ sudo journalctl -b --unit=prometheus
# по PID
$ sudo journalctl _PID=4111
# c tail -f
$ sudo journalctl --unit=prometheus
# за последние 10 минут
$ sudo journalctl --unit=prometheus --since="10 min ago"
|
Пример создания юнита для монтирования. Так же добавим automount при обращении к директории.
Важное замечание: нужно сгенеровать имя юнита из пути, куда будет производиться монтирование, это можно сделать с помощью systemd-escape
.
several man pages
Per systemd.mount man page:
Where=
Takes an absolute path of a directory of the mount point. If the mount point does not exist at the time of mounting, it is created. This string must be reflected in the unit filename. (See above.) This option is mandatory.
The “see above” part is:
Mount units must be named after the mount point directories they control. Example: the mount point /home/lennart must be configured in a unit file home-lennart.mount. For details about the escaping logic used to convert a file system path to a unit name, see systemd.unit(5).
systemd-escape -p –suffix=mount “/home/goto/net_backups”
1
2
|
systemd-escape -p --suffix=mount "/home/goto/net_backups"
home-goto-net_backups.mount
|
На Centos 7 с помощью команды sudo systemctl edit --force --full home-goto-net_backups.mount
не удалось создать юнит. Поэтому я создал его с помощью vi
по пути /etc/systemd/system/home-goto-net_backups.mount
и добавил следующее содержимое:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[Unit]
Description=CIFS
Requires=network-online.target
After=network-online.service
[Mount]
What=//samba.dmz.lan/accountants_folder/backup1c
Where=/home/goto/net_backups
Options=user,rw,credentials=/root/.smbclient,nounix,iocharset=utf8,file_mode=0777,dir_mode=0777,nofail
Type=cifs
[Install]
WantedBy=multi-user.target
|
Теперь перечитаем все юниты и запустим наш:
1
2
|
sudo systemctl daemon-reload
sudo systemctl start home-goto-net_backups.mount
|
Сейчас остановим home-goto-net_backups.mount
и напишем ещё один юнит etc/systemd/system/media-nfs.automount
для автомонтирования при обращении к целевой директории.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
sudo systemctl start home-goto-net_backups.mount
systemctl cat /etc/systemd/system/home-goto-net_backups.automount
# /etc/systemd/system/home-goto-net_backups.automount
[Unit]
Description=CIFS automount share
Requires=network-online.target
After=network-online.service
[Automount]
Where=/home/goto/net_backups
TimeoutIdleSec=301
[Install]
WantedBy=multi-user.target
|
Теперь остаётся включить и сразу запустить юнит. Это можно сделать одной командой:
1
|
sudo systemctl enable --now home-goto-net_backups.automount
|
Полезное: