Tomáš Srb, 20. 3. 2020
Služba se tváří jako klasický daemon, tj:
...možná… některé nástroje vytvořené mimo firmu nepodporují "démonizaci" - buď nepředpokládají že poběží jako služba (nodejs), nebo předpokládají že to za ně vyřeší někdo jiný (filebeat z elku)
Co třeba kolem takových nástrojů udělat obálku v shelu nebo pythonu?
...to by jistě šlo, jenže…
...jenže operační systém používá k spouštění služeb systemd - ten za ně řeší:
Systemd je prezentován jako nástroj na spouštění systémových služeb - i když služba sama může běžet pod libovolným uživatelem, je konfigurován na úrovni administrátora systému a roli správce systému a správce aplikace se snažíme držet oddělené.
Přestože se většina návodů na internetu zabývá systemd jako správcem systémových služeb a init systémem, lze ho používat i po správu služeb, které si může konfigurovat a řídit sám uživatel. Chce to jen vyřešit pár detailů.
Snadno - udělá se soubor .service v adresáři ~/.config/systemd/user. Formát toho souboru je docela dobře popsaný v man systemd.service (https://www.freedesktop.org/software/systemd/man/systemd.service.html), Pro nás je asi podstatné pojmenování toho souboru - každý uživatel může mít nainstalováno několik instancí aplikace a každá potřebuje svou instanci služby - soubor .service tedy generujeme a v jeho jméně se objeví identifikátor instance aplikace.
Zádrhel nastává, když chceme služby ovládat pod uživatelem, na kterého se přepínáme pomocí sudo. Háček je v tom, že provoz uživatelských služeb pro svoji činnost potřebuje:
/run/user/{uid}
XDG_RUNTIME_DIR
namířenou do toho adresářeNěkteré návody doporučují, použít místo sudo ssh, ale to není zrovna ono. Navíc potřebujeme, aby služby běžely hned od startu počítače, tedy i když se nikdo nepřihlásí - to se povolí příkazem:
loginctl enable-linger {user}
Dokonce to má tu skvělou vlastnost, že to hned po zapnutí pro příslušného uživatele spustí instanci systemd… kterou ovšem nelze ovládat, protože chybí DBUS. To se dá překvapivě řešit přidáním těchto služeb:
dbus.service
[Unit]
Description=D-Bus User Message Bus
Requires=dbus.socket
[Service]
ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation
ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
[Install]
Also=dbus.socket
dbus.socket
[Unit]
Description=D-Bus User Message Bus Socket
[Socket]
ListenStream=%t/bus
ExecStartPost=-/bin/systemctl --user set-environment DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus
[Install]
WantedBy=sockets.target
Also=dbus.service
Soubory nakopírujeme do /usr/lib/systemd/user/
. Služby dbus bychom rádi spustili pomocí systemctl… jenže to nemůžeme, protože neběží DBUS. Naštěstí si můžeme vypomoci trikem - systemctl enable
nahradíme symlinkem každého ze souborů do adresáře ~/.config/systemd/user/default.target.wants/
a systemctl start
zastoupí sekvence loginctl disable-linger {user}; loginctl enable-linger {user}
- tak se uživatelský systemd restartuje a dbus.service se nastartuje. Od té chvíle už funguje
sudo -u {user} XDG_RUNTIME_DIR=/run/user/{uid} systemctl --user status
což nám vypíše běžící služby zadaného uživatele. Žel, nenašli jsme způsob jak vypsat nespuštěné uživatelské služby apod. sudo -u {user} XDG_RUNTIME_DIR=/run/user/{uid} systemctl --user
ignoruje parametr --user
a vypíše totéž co systemctl
bez parametrů.
Ne úplně - v /etc/systemd/journald.conf
je třeba nastavit Storage=persistent
. Zatím jsme používali hodnotu auto
, ale ta na uživatelské logy nestačí. Po změně nastavení a restartu služby systemd-journald
lze použít:
sudo -u {user} XDG_RUNTIME_DIR=/run/user/{uid} journalctl --user -u {service}
tl;dr: Spouštěli jsme uživatelské služby stylem init.d, přešli jsme na systemctl a dalo to dost práce.
Hlavním zdrojem optimismu nám byl článek systemd user services are amazing
Zajímavý efekt nastává, když se server s uživatelskými službami nastartuje později, než ldap server s účty příslušných uživatelů. Pak se pro ně systemd nespustí a kupodivu nefunguje ani trik s loginctl disable-linger {user}; loginctl enable-linger {user}
. Pomůže pouze opětovný restart serveru. Naštěstí na produkci LDAP nepoužíváme a tak tam podobná situace nehrozí.