Poradnik: systemd — cz. 1
Moim celem jest stworzenie PRAKTYCZNEGO podręcznika skierowanego do administratorów.
Wstęp
Uwagi na temat użytego języka:
- nie chcę tłumaczyć angielskich nazw własnych na język polski, bo nie pomaga to w zrozumieniu przedmiotu
- jest to tekst skierowany do adminów, którzy na co dzień czytają dokumentacje po angielsku - oni mi wybaczą
- czy to jest normalne aby angielskie słowa odmieniać przez polskie przypadki - niestety tak :)
Co to jest systemd?
- wikipedia
- menadżer systemu i usług
- zastępuje rozwiązania takie jak: SysVinit, upstart
- podstawową jednostką organizacyjną jest "unit"
- w unitach można zdefiniować zależności między nimi
Mówiąć o systemach Unix/Linux zwykło się twierdzić, że "wszystko jest plikiem". Myśląc o systemd możemy myśleć, że "wszystko jest unitem".
Skąd się biorą te unity?
- z pakietów/paczek (rpm, deb, ...)
- unity mogą być utworzone/modyfikowane przez administratora
- unity mogą być utworzone/modyfikowane przez użytkowników
- unity również generowane są dynamicznie (generatory)
Jakie dystrybucje używają systemd?
Praktycznie większość: Arch Linux, Fedora, Debian, Ubuntu itd. W opozycji do nich trwają: Gentoo i Slackware. Systemd jest obecny w dwóch najważniejszych dystrybucjach klasy "enterprise": RHEL7 i SLES12.
Notacja
Jeśli używam nawiasów '[opcja]' znaczy, opcja ta może ale nie musi występować - jest opcjonalna. Czyli w poniższym poleceniu
# systemctl [list-units]
mogę, ale nie muszę użyć opcji "list-units".
Jeśli używam '{opcja_1|opcja_2|opcja_3}', to dokładnie jedna ze zbioru opcji MUSI wystąpić. Czyli w poniższym poleceniu
# systemd-tmpfiles {--clean|--create|--remove} /path/to/filename.conf
oznacza, że musisz użyć jednej z opcji: "-‑clean", "-‑create", "-‑remove":
# systemd-tmpfiles --clean /path/to/filename.conf # systemd-tmpfiles --create /path/to/filename.conf # systemd-tmpfiles --remove /path/to/filename.conf
Wersje
Różne wersje systemd w różnych dystrybucjach, czyli różna funkcjonalność. Wersje serwerowe (RHEL7, SLES12) zawierają starsze wersje i pewnie część funkcjonalności, omawiana w tym poradniku, będzie niedostępna. W dystrybucjach bardziej "desktopowych": Arch Linux, Fedora - systemd powinien być bardziej na czasie...
Źródła
Structural and semantic deficiencies in the systemd architecture for r...
A history of modern init systems (1992-2015)
Unity
Wszystkie procesy działające w systemie są procesami potomnymi "systemd init process". (PID=1)
$ ps -q 1 -o pid,comm,command PID COMMAND COMMAND 1 systemd /sbin/init
Ten fakt ma duże znaczenie dla administratorów, gdyż działanie całego systemu zależy od tego, jak działa systemd.
Wstęp
Lokalizacje: (man systemd.unit)
/etc/systemd/system/* /run/systemd/system/* /usr/lib/systemd/system/* $XDG_CONFIG_HOME/systemd/user/* $HOME/.config/systemd/user/* /etc/systemd/user/* /run/systemd/user/* /usr/lib/systemd/user/*
- pliki konfiguracyjne unitów "systemowych" - pochodzące z pakietów instalacyjnych - znajdują się tu: /usr/lib/systemd/system/
- jeśli chcesz zmodyfikować unit z /usr/lib/systemd/system/, to ... zostaw go w spokoju. Zamiast tego utwórz nowy w /etc/systemd/system/
- jeśli chcesz utworzyć własny "systemowy" unit, umieść go tu: /etc/systemd/system/
- konfiguracja globalna użytkowników (pochodząca z pakietów): /usr/lib/systemd/user/
- konfiguracja globalna użytkowników (modyfikacja tych z pakietów i własna twórczość): /etc/systemd/user/
- konfiguracja indywidualna użytkownika: $HOME/.config/systemd/user/
Nie modyfikuj plików unitów z /usr.
Zawsze plik unitu z /etc ma pierwszeństwo przed tym z /usr.
[item]automount - auto montowanie filesystemów (man systemd.automount) takich jak: /proc i /sys[/item][item]mount - montowanie filesystemów (man systemd.mount) takich jak: /boot, swap, /tmp[/item][item]snapshot - snapshoty nie są konfigurowane przez pliki. Są dynamicznie (systemctl snapshot) zapisywanym stanem procesów. (man systemd.snapshot)[/item][item]device - pliki urządzeń (man systemd.device)[/item][item]busname - (tylko gdy system używa "kdbus" ten typ ma zastosowanie) Kdbus ma w przyszłości zastąpić D‑Bus. Systemd ma generator, który na podstawie klasycznej aktywacji szyny dbus1 generuje unitu typu "service" i "busname". Serwis "busname" działa "podobnie" jak "socket" - czeka na próbę aktywacji szyny "po nazwie" i uruchamia usługę "service", przypisaną do tej szyny.[/item][/numlist]
Service / Podstawowe polecenia
Na przykładzie unitów typu "service" - usług/serwisów - poznamy podstawowe polecenia przydatne w codziennej pracy administratora.
Wylistowanie typów unitów:
# systemctl -t help
"Standardowe" opcje unitów (trochę jak stare polecenie "service"):
# systemctl {start|stop|status|restart|reload} name.type
Permanentne włączanie i wyłączanie - trochę podobne do "chkconfig on/off":
# systemctl {enable|disable} name.type
Kombinacja disable-enable:
# systemctl reenable name.type
Domyślny typ unitu, to "service".
Zatem dwa poniższe polecenia są równoważne:
# systemctl start sshd.service # systemctl start sshd
Sprawdzenie statusu unitu:
# systemctl status sshd * sshd.service - OpenSSH Daemon Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2015-10-01 14:17:59 CEST; 1min 12s ago Main PID: 29299 (sshd) CGroup: /system.slice/sshd.service `-29299 /usr/bin/sshd -D Oct 01 14:17:59 archer.local systemd[1]: Started OpenSSH Daemon. Oct 01 14:17:59 archer.local sshd[29299]: Server listening on 0.0.0.0 port 22. Oct 01 14:17:59 archer.local sshd[29299]: Server listening on :: port 22. Oct 01 14:18:33 archer.local systemd[1]: Started OpenSSH Daemon.
"Loaded":
- loaded - plik unitu znaleziony i używany- dodatkowo: plik źródłowy unitu, stan po restarcie (enable/disable/static), predefiniowany stan po restarcie (enable/disable)
- not-found - pliku unitu nie znaleziono
"Static" znaczy, że unit może być uruchomiony - w uproszczeniu - przez system i nie może być uruchomiony "ręcznie" przez administratora. Stan predefiniowany, czyli domyślny stan po instalacji pakietu z oprogramowaniem.
"Active":
[list] [item]active - działa
- running - proces jest uruchomiony
- exited - zakończony sukcesem
- waiting - czeka na "event"
[/item][item]inactive(dead) - nie działa, pytanie: dlaczego :)[/item][/list]
Sprawdzenie statusu unitu, podając PID procesu lub jednego z procesów potomnych:
# systemctl status PID
Zapytanie "czy wystartowano", "czy włączono" daną usługę/unit:
# systemctl is-active name.type # systemctl is-enable name.type
Wylistowanie załadowanych unitów:
# systemctl [list-units]
Wylistowanie wszystkich unitów (także tych wynikających z zależności, ale niezainstalowanych):
# systemctl [list-units] --all
Wylistownie załadowanych serwisów:
# systemctl [list-units] --type=service # systemctl [list-units] -t service
Wylistowanie wszystkich serwisów (również tych, są zdefiniowane jako zależności między unitami, a nie są zainstalowane lub zdefiniowane):
# systemctl [list-units] -t service --all
Wylistowanie niepoprawnie działających unitów:
# systemctl --failed # systemctl --all --failed # systemctl --all --type=error # systemctl --all --type=not-found
Wylistowanie wszystkich "trefnych" unitów:
# systemctl --all --failed --type=error --type=not-found --no-legend
Wylistowanie załadowanych unitów jednego, konkretnego typu:
# systemctl -t {service|socket|busname|target|snapshot|device|mount|automount|swap|timer|path|slice|scope}
Jeśli zmienimy konfiguracja unitów (zawartość plików unitów, ale również plików konfiguracyjnych takich jak: /etc/fstab, na podstawie których systemd generuje unity), to należy poinformować o tym systemd:
# systemctl daemon-reload
Permanentne wyłączenie unitu (głównie serwisów): (mocniejsze niż "disable", bo ani ręcznie, ani jako zależność unit ten nie zostanie uruchomiony):
# systemctl mask name.type
Wyłączenie "maskowania":
# systemctl unmask name.type
Wylistowanie zamaskowanych unitów:
# systemctl list-unit-files | awk '$2 == "masked" {print $1}'
Wylistowanie wszystkich plików unitów (trochę podobne do "chkconfig -‑list"):
# systemctl list-unit-files --all UNIT FILE STATE (...) user.slice static avahi-daemon.socket enabled dbus.socket static dm-event.socket static docker.socket enabled git-daemon.socket disabled krb5-kpropd.socket disabled libvirtd.socket enabled lircd.socket disabled lvm2-lvmetad.socket static (...)
"State":
- enabled - włączony, po restarcie systemu zostanie włączony
- disabled- wyłączony, po restarcie systemu nie zostanie włączony, chyba że inny unit tego wymaga...
- static- może być uruchomiony, jeśli wymaga tego inny unit, ale nie może być uruchomiony przez administratora lub użytkownika
- masked- wyłączony permanentnie
Wylistowanie unitów wymaganych przez dany unit:
# systemctl list-dependencies name.type
Wymuszenie zatrzymania unitu, który "zawisł" ( i/lub ExecStop= nie działa ). Możemy wysłać do wszystkich procesów sygnał:
# systemctl kill [-s [SIG]KILL] [--kill-who=username] name.type
No ale jak taki unit typu "service" wygląda od środka? Najprostszy wygląda tak:
[Service] ExecStart=/path/to/application
"Prawdziwy" serwis wygląda tak (sshd.service):
[Unit] Description=OpenSSH Daemon Wants=sshdgenkeys.service After=sshdgenkeys.service After=network.target [Service] ExecStart=/usr/bin/sshd -D ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=always [Install] WantedBy=multi-user.target
W wielkim skrócie:
- Sekcja [Unit] opisuje meta informacje i zależności
- Sekcja [Service] opisuje co i jak ma być uruchomione
- Sekcja [Install] opisuje do jakiego targetu będzie dołączona ta usługa po jej włączeniu
Więcej powiemy na ten temat w rozdziale poświęconym tworzeniu własnych unitów.
Target
Target, czyli pewna funkcjonalność zdefiniowana przez grupę usług i innych targetów. Podczas uruchamiania systemu celem jest "osiągnięcie" domyślnego targetu. Nie mylić targetu z "runlevelem".
Jaki jest domyślny target?
# systemctl get-default
Jak zmienić domyślny target?
# systemctl set-default name.target
Lista wszystkich targetów:
# systemctl --all -t target
Wylistowanie targetów wymaganych przez konkretny target:
# systemctl list-dependencies multi-user.target | grep '.target$' multi-user.target +-basic.target | +-paths.target | +-slices.target | +-sockets.target | +-sysinit.target | | +-cryptsetup.target | | +-local-fs.target | | +-swap.target | +-timers.target +-getty.target +-remote-fs.target
Kiedyś w dystrybucjach z rodziny RHEL/Fedora "runlevel 3" oznaczał, że serwer jest w pełni funkcjonalny, a "runlevel 5" dodatkowo miał włączone środowisko graficzne. Domyślny "runlevel" określony był w pliku /etc/inittab. Obecnie podobną funkcjonalność można uzyskać ustawiając domyślny target, albo na "multi-user.target", albo na "graphical.target".
Zmiana "stanu" systemu jest możliwa poprzez włączenie/wyłączenia "targetu".
Aby wystartować system w innym targecie niż domyślny, ustaw zmienną "systemd.unit=" w programie ładującym (grub, grub2, lilo,...) podczas uruchamiania systemu.
Wymuszenie osiągnięcia domyślnego targetu:
# systemctl default # systemctl isolate default.target
Target "rescue" przydaje się do naprawy systemu i można go wymusić przy starcie systemu. Jego celem jest załadowanie minimalnego zestawu usług:
# systemctl rescue # systemctl isolate rescue.target
Target "emergency" również służy głównie do naprawy systemu i można go wymusić przy starcie systemu. Po załadowaniu jądra i jego "ramdysku" wyszukana zostaje partycja główna "/" i zamontowana w trybie "tylko do odczytu". Na tym etapie przejmujemy kontrolę nad systemem:
# systemctl emergency # systemctl isolate emergency.target
Przygotowanie systemu do wyłączenia zasilania:
# systemctl halt # systemctl start halt.target --irreversible [--force [--force]]
Wyłączenie systemu razem z zasilaniem:
# systemctl poweroff # systemctl start poweroff.target --irreversible [--force [--force]]
Restart systemu:
# systemctl reboot # systemctl start reboot.target --irreversible [--force]
"Usypianie":
# systemctl suspend # systemctl start suspend.target
"Hibernacja":
# systemctl hibernate # systemctl start hibernate.target
Połączenie "usypiania" z "hibernacją":
# systemctl hybrid-sleep # systemctl start hybrid-sleep.target
Socket
Pytanie: Czy aplikacja (sieciowa) musi być uruchomiona przy starcie, czy też dopiero wtedy, kiedy jest potrzebna?
Oto sshd.socket:
[Unit] Description=SSH Socket for Per-Connection Servers [Socket] ListenStream=22 Accept=yes [Install] WantedBy=sockets.target
Domyślnie "socket" uruchamia "service" o tej samej nazwie.
Teraz zamiast używać sshd.service możemy użyć sshd.socket:
# systemctl stop sshd # systemctl disable sshd # systemctl enable sshd.socket # systemctl start sshd.socket
Od tej chwili to systemd nasłuchuje na porcie :22 w oczekiwaniu na połączenie. (Tak naprawdę to sshd.socket nie uruchomi bezpośrednio usługi sshd.service, tylko "template" sshd@.service - wszystko przez opcję "Accept=yes" - o "template'ach" będzie później...)
Timer
Timer jest odpowiedzialny za uruchomienie usługi w określonym czasie lub/i o określonym interwale.
Domyślnie "timer" uruchamia "service" o tej samej nazwie.
Przykładowo:
# cat /usr/lib/systemd/system/systemd-tmpfiles-clean.timer (...) [Timer] OnBootSec=15min OnUnitActiveSec=1d
Timer systemd-tmpfiles-clean.timer uruchamia usługę systemd-tmpfiles-clean.service: - 15 minut po uruchomieniu serwera - po pierwszym uruchomieniu cyklicznie co jedną dobę
Inne przydatne opcje:
- OnActiveSec= - od momentu aktywacji timera
- OnBootSec= - od uruchomienia serwera
- OnStartupSec= - od pierwszego uruchomienia serwera
- OnUnitActiveSec= - od ostatniej aktywacji timera
- OnUnitInactiveSec= - od ostatniej dezaktywacji timera
- OnCalendar= - cykliczność - podobnie do usługi CRON
- AccuracySec= - dopuszczalne opóźnienie aktywacji
- Unit= - jeśli chcesz uruchomić usługę o innej nazwie
Specyfikacja czasu
(man systemd.time)
Poniższe informacje dotyczą całego systemd, czyli również journald.
Jednostki czasu:
- usec, us
- msec, ms
- seconds, second, sec, s
- minutes, minute, min, m
- hours, hour, hr, h
- days, day, d
- weeks, week, w
- months, month
- years, year, y
Bez określenia (domyślnie) jednostki czasu: 50 znaczy 50 sekund. Można łączyć, np: "2min 200ms".
Cykliczność: "minutely", "hourly", "daily", "monthly", "weekly", "yearly", "quarterly", "semiannually".
Znacznik czasu "timestamp" ma postać: "DayOfWeek YYYY-MM-DD HH:MM:SS": np:
Uwaga:
- * = dowolna wartość
- a/b = a, a+b, a+2*b, a+3*b,...
# "Podobnie" jak dla usługi CROND # skrót › rozwinięcie Sat,Thu,Mon-Wed,Sat-Sun › Mon-Thu,Sat,Sun *-*-* 00:00:00 Mon,Sun 12-*-* 2,1:23 › Mon,Sun 2012-*-* 01,02:23:00 Wed *-1 › Wed *-*-01 00:00:00 Wed-Wed,Wed *-1 › Wed *-*-01 00:00:00 Wed, 17:48 › Wed *-*-* 17:48:00 Wed-Sat,Tue 12-10-15 1:2:3 › Tue-Sat 2012-10-15 01:02:03 *-*-7 0:0:0 › *-*-07 00:00:00 10-15 › *-10-15 00:00:00 monday *-12-* 17:00 › Mon *-12-* 17:00:00 Mon,Fri *-*-3,1,2 *:30:45 › Mon,Fri *-*-01,02,03 *:30:45 12,14,13,12:20,10,30 › *-*-* 12,13,14:10,20,30:00 mon,fri *-1/2-1,3 *:30:45 › Mon,Fri *-01/2-01,03 *:30:45 03-05 08:05:40 › *-03-05 08:05:40 08:05:40 › *-*-* 08:05:40 05:40 › *-*-* 05:40:00 Sat,Sun 12-05 08:05:40 › Sat,Sun *-12-05 08:05:40 Sat,Sun 08:05:40 › Sat,Sun *-*-* 08:05:40 2003-03-05 05:40 › 2003-03-05 05:40:00 2003-03-05 › 2003-03-05 00:00:00 03-05 › *-03-05 00:00:00 hourly › *-*-* *:00:00 daily › *-*-* 00:00:00 monthly › *-*-01 00:00:00 weekly › Mon *-*-* 00:00:00 yearly › *-01-01 00:00:00 annually › *-01-01 00:00:00 *:2/3 › *-*-* *:02/3:00
Przykład 1: (każdego roku, 5 stycznia o północy)
*-01-05 › *-01-05 00:00:00
Przykład 2: (każdego roku, 5 marca o północy i co dwa miesiące: 5 maja o północy, 5 lipca o północy,...)
*-01/2-05 › *-01/2-05 00:00:00
Czas względny:
# Załóżmy, że aktualny data to: 2012-11-23 18:15:22 +3h30min › Fri 2012-11-23 21:45:22 -5s › Fri 2012-11-23 18:15:17 11min ago › Fri 2012-11-23 18:04:22 11min left › Fri 2012-11-23 18:26:22
UTC:
@1395716396 › Tue 2014-03-25 03:59:56
Podsumowanie
Przedstawiłem podstawowe zagadnienia związane z systemd oraz omówiłem krótko podstawowe typy unitów. W następnych częściach: journald, nspawn, networkd, generatory...