Динамічні unit-файли з systemd-генератором дозволяють автоматично будувати служби на льоту — залежно від конфігів, середовища або даних. Це чистіше за хаос у /etc, гнучкіше за статичні шаблони та зручно для автоматизація задач на сервер Linux. Сьогодні покажу, як створити свій генератор, використати шаблони, drop-in, протестувати все без паніки і мати безпечний відкат у разі чого. 🚀
Як працюють systemd-генератори
systemd-генератори — це невеликі програми (часто bash скрипти), які запускаються під час старту systemd або після daemon-reload. Вони читають вашу конфігурацію та генерують unit-файли у /run/systemd/generator (або .../generator.late). Важливі факти:
- Місце розташування генератора:
/usr/lib/systemd/system-generatorsабо/etc/systemd/system-generators. - Вихід генератора — тільки в
/run/systemd/generator(+ підкаталоги*.wantsдля залежностей). - Генератори мають бути дуже швидкими: ніяких довгих мережевих запитів, тільки читання файлів і створення unit-ів.
Покроковий How-to: динамічні служби з конфігів
Створимо генератор, який зчитує .env-файли з /etc/dynunits та для кожного з них збирає інстанс myrunner@name.service з автозапуском у multi-user.target.
1) Підготовка каталогу конфігів
sudo install -d -m 0755 /etc/dynunits
sudo tee /etc/dynunits/hello.env >/dev/null <<'EOF'
# Команда, яку треба виконати як сервіс
COMMAND=/usr/bin/bash -lc 'echo "Hello from myrunner@hello"; date; sleep 2'
EOF
sudo chmod 600 /etc/dynunits/*.env
2) Створення генератора
Розміщуємо генератор у /etc/systemd/system-generators/ (адмінський рівень). Він читає кожен *.env і генерує інстанс:
sudo install -d -m 0755 /etc/systemd/system-generators
sudo tee /etc/systemd/system-generators/dynunits-generator >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
OUT="${1:-/run/systemd/generator}"
CONFIG_DIR="/etc/dynunits"
# Kill-switch для безпечного відкату
[[ -f "$CONFIG_DIR/.disable" ]] && exit 0
mkdir -p "$OUT" "$OUT/multi-user.target.wants"
shopt -s nullglob
for f in "$CONFIG_DIR"/*.env; do
inst="$(basename "$f" .env)"
cat >"$OUT/myrunner@${inst}.service" <<UNIT
[Unit]
Description=Dynamic runner for ${inst}
After=network.target
[Service]
Type=simple
EnvironmentFile=$f
ExecStart=/usr/bin/bash -lc "\${COMMAND}"
Restart=on-failure
[Install]
WantedBy=multi-user.target
UNIT
ln -sf "../myrunner@${inst}.service" \
"$OUT/multi-user.target.wants/myrunner@${inst}.service"
done
EOF
sudo chmod 0755 /etc/systemd/system-generators/dynunits-generator
3) Перевантаження systemd та перевірка
sudo systemctl daemon-reload
systemctl list-units 'myrunner@*.service'
# Запуск інстансу
sudo systemctl start myrunner@hello.service
# Логи інстансу
journalctl -u myrunner@hello.service -b --no-pager
# Валідація синтаксису юніта
systemd-analyze verify /run/systemd/generator/myrunner@hello.service
Побачили «Hello…» і дату в журналі? Вітаю, генератор працює! 🔧
Шаблони unit-файлів та drop-in
У прикладі вище ми генеруємо повні інстанси. Так само можна згенерувати лише базовий шаблон myrunner@.service і окремі drop-in. Drop-in — це конфіг-заплатки у каталозі /etc/systemd/system/<unit>.d/*.conf або у /run/systemd/generator.late. Наприклад, щоб обмежити ресурси для всіх інстансів:
sudo install -d -m 0755 /etc/systemd/system/myrunner@.service.d
sudo tee /etc/systemd/system/myrunner@.service.d/override.conf >/dev/null <<'EOF'
[Service]
CPUAccounting=yes
MemoryMax=200M
EOF
sudo systemctl daemon-reload
sudo systemctl restart myrunner@hello.service
У ролі «джерела правди» для інстансів можна використовувати файли .env, а для політик — drop-in. Це дає чисту модель керування і добре масштабується на сервер Linux.
Альтернативні способи без генератора
- Класичні шаблони: створіть статичний
/etc/systemd/system/myrunner@.service, а параметри підсовуйте через/etc/systemd/system/myrunner@name.service.d/env.conf. Увімкнути так:systemctl enable --now myrunner@name.service. - Періодичні задачі: замість сервісів згенеруйте
*.timer+*.serviceпари. Це добре лягає на сценарії «cron та systemd timers» для регулярних бекапів чи синків.
GUI-спосіб (через Cockpit)
Генератор з GUI не намалюєте, але тестувати зручно. Установіть Cockpit, увімкніть сокет і керуйте службами у веб-інтерфейсі: старт/стоп, логи, автозапуск.
# Debian/Ubuntu-подібні
sudo apt update && sudo apt install -y cockpit
sudo systemctl enable --now cockpit.socket
# Далі відкрийте https://<IP>:9090 і перейдіть у Services -> myrunner@hello.service
Тестування і безпечний відкат
Перевірки перед продом
systemd-analyze verifyдля кожного юніта у/run/systemd/generator.- Запуск у «пісочниці»:
systemd-run --unit=probe --pty bashі ручний виклик команд зCOMMAND=.... - Тестовий вузол або VM, якщо у вас чутливий прод.
Механіки відкату
- Kill-switch: створіть файл
/etc/dynunits/.disableі зробітьsystemctl daemon-reload— генератор нічого не згенерує. - Видаліть або перемістіть проблемний
*.env, перезавантажте daemon. - Drop-in скасовуються командою:
sudo systemctl revert myrunner@.service. - Якщо система не вантажиться, у GRUB додайте параметр ядра
systemd.unit=rescue.target, або замаскуйте сервіс:systemd.mask=myrunner@hello.service.
FAQ
Чому мій генератор не запускається після daemon-reload?
Перевірте шлях і права: файл має бути виконуваним у /etc/systemd/system-generators/ або /usr/lib/systemd/system-generators/. Далі подивіться журнал: journalctl -b -u systemd або помилки stderr генератора у загальному журналі.
Де правильно зберігати згенеровані файли?
Тільки у /run/systemd/generator або /run/systemd/generator.late. Ніколи не пишіть у /etc/systemd/system з генератора.
Чи можна викликати мережеві утиліти у генераторі?
Ні, уникайте довгих або ненадійних дій. Генератор має бути миттєвим. Усі важкі речі — у самих сервісах.
Що робити, якщо інстанс не з’являється у списку?
Переконайтеся, що *.env існує, не порожній, має права читання root-ом, і немає .disable. Потім: systemctl daemon-reload і systemd-analyze verify.
Чи безпечно підставляти команди з .env у ExecStart?
Так, якщо ви контролюєте вміст і права доступу. Ставте chmod 600 на .env, не приймайте дані від недовірених користувачів, додавайте NoNewPrivileges=yes, PrivateTmp=yes тощо у drop-in для захисту.
Порада від Kernelka
Я завжди тримаю маленький «стоп-кран»: файл .disable для генератора і альтернативний користувацький доступ по консолі, щоб у разі збою швидко відкотитися. Також рекомендую вести git у /etc/dynunits — зміни видно, відкат в один клік. 🌟
Підсумок
- systemd-генератор дає гнучкі динамічні unit-файли без сміття у /etc.
- Комбінуйте шаблони і drop-in для чистої архітектури.
- Тестуйте:
systemd-analyze verify, журнали, пісочниці. - Тримайте kill-switch і план безпечного відкату.
- Для періодики краще підійдуть «cron та systemd timers».