Programujeme letenky

Vývojáři GOL IBE v C.E.E. Group Travelport
Programátorský web
Background image - Futurama office park

systemd --user u nás

Tomáš Srb, 20. 3. 2020

Jak jsme to dělali dosud?

Služba se tváří jako klasický daemon, tj:

Je důvod dělat to jinak?

...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…

Není nějaké standardní řešení?

...jenže operační systém používá k spouštění služeb systemd - ten za ně řeší:

Tak v čem je háček? Proč nepoužít systemd?

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é.

Takže je pro nás systemd nepoužitelný?

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ů.

Jak se ty uživatelské služby konfigurují?

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.

To vypadá jednoduše, co jsme na tom řešili tak dlouho?

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:

Jak z toho ven?

Ně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ů.

Tak mne napadá, co logování, to je bez zádrhele?

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}  

Co jsem tím chtěl říct

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

Update 1. 9. 2022

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í.