Wykorzystanie systemd zamiast crona, initd i automatu do kawy

Kilka lat temu w większości Linuksów zagościł na dobre daemon systemd odpowiedzialny za obsługę startu, całego cyklu życia i wyłączenia systemu operacyjnego. Do czego ten kombajn może się przydać?

Sysvinit

Systemd zastąpił dość stary mechanizm zarządzania skryptami startowymi sysvinit, który po załadowaniu jądra był inicjowany przez program /sbin/init. Brał on kolejne serwisy z katalogów /etc/rc[runlevel].d/ i uruchamiał z odpowiednim parametrem. Nowsze lub bardziej rozbudowane dystrybucje potrafiły dodatkowo wprowadzić wymagane serwisy lub podsystemy, które były potrzebne do uruchomienia danego serwisu.

Systemd wprowadził natomiast format opisowych plików, które pełnią podobną funkcję, natomiast nie są skryptami samymi w sobie, tak jak sysvinit.

Prosy serwis

Popatrzmy na przykładowy serwis z CoreCluster, który startuje podsystem agentów:

[Unit]
Description=CoreCluster cloud
After=network.target
Requires=network.target

[Service]
Type=simple
ExecStartPre=-/usr/sbin/cc-manage configure
ExecStart=/usr/sbin/cc-manage agent start
ExecStop=/usr/sbin/cc-manage agent stop

[Install]
WantedBy=multi-user.target

Zawiera on sekcję Unit, która opisuje serwis i mówi daemonowi systemd, co jest wymagane do uruchomienia go. Na pierwszy rzut oka wpisy Requires i After mogą sprawiać wrażenie zduplikowanych, jednak oba mają pewne znaczenie. Ponieważ systemd działa przez cały czas, od startu systemu i monitoruje stan systemu, możemy określić, że do działania naszego serwisu wymagana jest sieć (parametr Requires). Oba parametry gwarantują, że po nasz serwis corecluster.service zostanie po niej uruchomiony. After=network.target określa kolejność i spowoduje, że serwis będzie mógł być uruchomiony dopiero po uruchomieniu sieci. Requires spowoduje, że serwis będzie musiał zostać wyłączony jak tylko wyłączy się jakikolwiek z wymaganych serwisów, czyli tutaj sieć.

Idźmy dalej. Sekcja Service opisuje co i jak uruchomić. Powyższy przykład pokazuje jak włączyć proces, który zatrzyma się dopiero podczas zatrzymania samego serwisu. Czyli polecenie zawarte w ExecStart zakończy się. To, jak ma się zachować uruchamiany proces określamy parametrem Type=simple z tej sekcji. Po uruchomieniu danego polecenia serwis będzie widoczny jako uruchomiony tak długo, jak proces będzie działał i ni zwróci żadnego kodu wyjścia.

Innym rozwiązaniem są procesy, które odrazu przechodzą do działania w tle lub kończą się (np działają trybie daemon). Zobaczmy na przykład serwisu zarządzającego nodami CoreCluster:

[Unit]
Description=CoreCluster cloud
After=network.target
Requires=network.target

[Service]
Type=oneshot
ExecStartPre=-/usr/sbin/cc-node configure
ExecStart=/usr/sbin/cc-node start
ExecStop=/usr/sbin/cc-node stop
RemainAfterExit=1

[Install]
WantedBy=multi-user.target

Zamiast Type=simple możemy wpisać Type=oneshot. Oznacza to, ze proces powinien zakończyć się, a systemd nie przyjmie tego jako błędu.

W każdym przypadku możemy uruchomić dodatkowe polecenia przed i po uruchomieniu właściwego serwisu. Można dzięki temu skonfigurować środowisko lub posprzątać.

Okresowe uruchamianie procesów

Od lat robione cronem. Nikt go nie wyrzuca, ale systemd daje większe możliwości 🙂 Po pierwsze systemd wie więcej o stanie systemu (czy mamy sieć, środowisko graficzne itd). Po drugie systemd pozwala łatwiej kontrolować to, co się dzieje, jeśli jesteśmy rootem.

[Unit]
Description=Run foo weekly and on boot

[Timer]
OnBootSec=15min
OnUnitActiveSec=1w
Unit=serwis_do_uruchomienia.service 

[Install]
WantedBy=timers.target

Powyższy przykład uruchomi serwis serwis_do_uruchomienia (parametr Unit) 15 minut po starcie systemu i następnie co tydzień. Zamiast OnUnitActiveSec możemy też określić w sekcji Timer konkretne daty, podobnie jak w Cronie: OnCalendar=*-*-* 00:00:00. Taki wpis spowoduje wykonanie timera dokładnie o północy każdego dnia.

Instalacja serwisu i timera

Jeśli Twój serwis jest dostarczany przez pakiet instalacyjny deb lub rpm, to możesz zawrzeć opis Twojego serwisu w odpowiednim pliku podczas budowania paczki (patrz przykłady z CoreCluster). W przeciwnym wypadku możesz dodać serwisy jako odpowiednie pliki w katalogu /usr/lib/systemd/system/. Odpowiednio, pliki serwisów będą miały nazwy kończące się na .service, czyli np. serwis_do_uruchomienia.service, a pliki timerów .timer.

Aby nasz serwis lub timer zadziałał, musimy go włączyć poleceniem systemctl enable serwis_do_uruchomienia.service. Spowoduje to, że systemd załaduje ten plik. Jeżeli z jakiegoś powodu będziemy chcieli zmienić jego zawartość (np. zmienić czasy wykonywania timera), to należy później poinformować o tym fakcie demona systemd i przeładować zawartość pliku poleceniem systemctl daemon-reload. Nie musimy się przy tym martwić linkami symbolicznymi oraz kolejnością uruchamiania serwisów przez systemd, jak to miało miejsce w przypadku sysvinit. Po nieco dłuższym zaznajomieniu się z systemd i jego sposobem zarządzania procesami będziesz mógł bardzo dokładnie dopasować uruchamianie procesów w Twoim systemie. Np. uruchamiając je po podłączeniu konkretnego urządzenia: After=dev-sdb.device (ich listę znajdziesz po wpisaniu systemctl –all –full -t device).

Leave a Reply