Systemd is een van de belangrijkste vernieuwingen, die Linux in jaren gezien heeft. De meningen zijn echter verdeeld. Veel beheerders en gebruikers vinden dat systemd ingaat tegen de Linux-filosofie in het algemeen. Tegelijkertijd is het een teken dat alle belangrijke Linux-distributies op dit moment over gegaan zijn naar systemd of plannen hebben om op korte termijn over te gaan. In dit artikel maak je nader kennis met dit manusje-van-alles dat in de toekomst onvermijdelijk zal zijn.

Systemd is een vervanging van upstart en init, services die tot voor kort gebruikt werden om services te starten in Linux. Om te begrijpen wat er zo goed is aan systemd, is het goed enig inzicht te hebben in de werking van init en upstart. Voor het gemak gooien we in het vervolg van dit artikel beiden op één hoop en bespreken we de opstartprocedure, zoals die op een systeem met init plaatsvond. Hierbij is het onvermijdelijk dat door de generalisaties een enkele nuance verloren gaat. Dat nemen  we  in dit artikel voor lief, omdat het doel van dit artikel niet is om een diepgaand inzicht te verschaffen in de werking van de vorige opstartprocedure.

 

In het verleden werd init gestart door de kernel. Van hieruit werd vervolgens een reeks scripts aangeroepen, die sequentieel doorlopen werden. Grofweg werden er twee types scripts onderscheiden:

 

       Scripts uit de initiële opstartfase. In deze scripts werd zorg gedragen voor het activeren van essentiële onderdelen van het systeem, zoals het mounten van bestandssystemen, het klaarzetten van devices en meer zaken zonder welke een systeem niet goed kan starten.

       Scripts voor het starten van services, die wel heel nuttig zijn, maar niet noodzakelijk voor een server om goed door te starten.

 

In init waren er een aantal zaken, die niet helemaal in orde waren:

       Er was geen standaardisatie voor de manier waarop de opstartscripts geschreven werden. Dat betekent dat services en andere essentiële zaken vaak op nogal afwijkende wijze gestart werden.

       Services werden achtereenvolgens gestart. Hierdoor kwam het voor dat een systeem niet goed startte als voorgaande services niet succesvol geladen werden.

       Er was geen eenvoudige interface om de initiële fase van de opstartprocedure precies zo vorm te geven als noodzakelijk was. Het gevolg was dat in veel gevallen veel meer zaken geladen werden dan eigenlijk noodzakelijk was.

 

Lennart Ploetering (de hoofdontwikkelaar van systemd) heeft bij het ontwerp van systemd rekening gehouden met al deze tekortkomingen en ernaar gestreefd al deze problemen op te lossen.

 

Basiswerking van systemd

De beste manier om systemd te beschrijven, is door te zeggen dat systemd gebruikt wordt voor het starten van dingen. Het woord “dingen” is hier het meest op zijn plaats, omdat systemd veel verder gaat dan alleen het starten van services. Om er maar een paar te noemen: met systemd kun je naast services starten, zoekpaden beschikbaar maken, bestandssystemen mounten, automounting regelen en nog veel meer. Het mooie is dat dat allemaal event-driven gebeurt. Dit betekent dat deze dingen niet gestart worden als ze niet nodig zijn en wanneer ze nodig zijn, worden ze meteen geladen.

 

Laten we als voorbeeld eens kijken naar de wijze waarop in systemd services gestart worden. Alle voorbeelden in dit artikel zijn ontleend aan Red Hat Enterprise Linux 7. Als je een andere distributie gebruikt, zijn bepaalde details ongetwijfeld anders geregeld.

 

Om de systemd-dingen te starten, wordt gebruik gemaakt van zogenaamde unit files. Wat in dit artikel tot nu toe omschreven is als “dingen” heet in de officiële systemd terminologie een unit. Deze unit files staan op RHEL 7 opgeslagen in de directories /usr/lib/systemd/system en /etc/systemd/system. De gedachte achter deze splitsing van opslaglocaties is simpel: in /usr/lib/systemd/system staan die zaken, die niet aangepast moeten worden en in /etc/systemd/system staan de aanpassingen van de beheerder.

 

Als je bijvoorbeeld aanpassingen aan wilt brengen aan het bestand /usr/lib/systemd/system/httpd.service, dan is het de bedoeling dat je dit bestand kopieert naar de directory /etc/systemd/system en op die locatie wijzigingen aanbrengt. Op deze manier wordt gegarandeerd dat wijzigingen van de beheerder nooit de algemene werking in de weg zitten.

 

In listing 1 zie je hoe de inhoud van het unit-bestand eruit ziet, dat gebruikt wordt om de Apache webserver te starten op Red Hat.

 

**** LISTING MET ONDERSCHRIFT -> Listing 1: Het httpd.service unit-bestand ***

 

[root@server system]# cat httpd.service

[Unit]

Description=The Apache HTTP Server

After=network.target remote-fs.target nss-lookup.target

 

[Service]

Type=notify

EnvironmentFile=/etc/sysconfig/httpd

ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND

ExecReload=/usr/sbin/httpd $OPTIONS -k graceful

ExecStop=/bin/kill -WINCH ${MAINPID}

# We want systemd to give httpd some time to finish gracefully, but still want

# it to kill httpd after TimeoutStopSec if something went wrong during the

# graceful stop. Normally, Systemd sends SIGTERM signal right after the

# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give

# httpd time to finish.

KillSignal=SIGCONT

PrivateTmp=true

 

[Install]

WantedBy=multi-user.target

 

*** EINDE LISTING ***

 

Zoals je ziet is het bestand aanmerkelijk overzichtelijker dan het gemiddelde init-script. Elk systemd unit file heeft een vaststaand aantal secties, waarin geregeld wordt hoe de unit in kwestie gestart wordt. In de unit-file in listing 1, zie je dat deze bestaat uit drie secties:

 

       [Unit]

       [Service}

       [Install]

 

Om te beginnen is er de [Unit] sectie. Hierin worden algemene zaken geregeld, waaronder de afhankelijkheidsrelaties. Door middel van een Before statement wordt opgegeven vóór welke andere units deze unit gestart moet worden. Door middel van het After statement geef je op welke units in elk geval gestart moeten worden, voordat deze unit gestart kan worden. Hierbij valt op dat verwezen wordt naar “targets”. Voorlopig omschrijven we deze targets als “een groepje unit-files”, maar later meer hierover.

 

Het tweede deel bestaat uit de [Service] sectie. Omdat listing 1 een service unit-file laat zien, is er een [Service] sectie. Als je bijvoorbeeld een mount unit-file bekijkt, is dat een [Mount] sectie. In deze sectie staat de configuratie, die voor deze unit belangrijk is. Bij een servicebestand gaat het er vooral om dat duidelijk is hoe een service gestart, gestopt en herstart wordt en waar eventuele omgevingsvariabelen gelezen worden, maar bij een mount unit-file (zie listing 2) zie je hier wat er gemount moet worden, waar het gemount moet worden en met welke opties het gemount moet worden. Om te kijken wat er in welke unit qua opties mogelijk is, type je:

 

 

systemctl show <unitnaam>

 

 Listing 2: Voorbeeld van een mount unit-file ***

 [root@server system]# cat tmp.mount

#  This file is part of systemd.

#

#  systemd is free software; you can redistribute it and/or modify it

#  under the terms of the GNU Lesser General Public License as published by

#  the Free Software Foundation; either version 2.1 of the License, or

#  (at your option) any later version.

 

[Unit]

Description=Temporary Directory

Documentation=man:hier(7)

Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems

DefaultDependencies=no

Conflicts=umount.target

Before=local-fs.target umount.target

 

[Mount]

What=tmpfs

Where=/tmp

Type=tmpfs

Options=mode=1777,strictatime

 

# Make ‘systemctl enable tmp.mount’ work:

[Install]

WantedBy=local-fs.target

 

**** EINDE LISTING ****

 

Het laatste onderdeel van de unit-file beschrijft hoe het geïnstalleerd wordt.

 

Het alternatief voor runlevels

In het verleden waren er runlevels, maar in systemd zitten targets. Een target is een groep units, die bij elkaar horen. Targets kunnen genest worden, wat betekent dat een target onderdeel kan zijn van een andere target. Om te bepalen hoe een systeem gestart wordt, zijn er een aantal targets die de hoofdrol spelen:

 

       graphical.target: hierin worden alle units gestart, die nodig zijn voor een volledige grafische omgeving. Als je een Linux desktop gebruikt, is dit de target die je gebruikt.

       multi-user.target: dit is de target, waarin servers normaliter gestart worden. Alle noodzakelijke services zijn aanwezig, maar er is geen GUI.

       rescue.target: een minimale target waarin alleen services gestart worden, die nodig zijn een systeem dat niet langer werkt, te starten. Om in deze modus op te starten, type je op de GRUB prompt de opdracht systemd.unit=rescue.target aan het eind van de regel waar de kernel geladen wordt.

       emergency.target: dit is een nog minimalere target, die gebruikt kan worden tijdens het opstarten. Type systemd.unit=emergency.target op de GRUB prompt om in deze modus te starten.

 

Het is wat lastig te achterhalen wat er nu precies in een target gedefinieerd is. Dat komt met name, omdat een target niet een lijst met units is, die altijd gestart wordt. Je kunt het beter omgekeerd zien. Om een bepaalde unit te starten, is een aantal andere units nodig. Je ziet hiervan een overzicht als je een opdracht typt als systemctl list-dependencies sshd.service. Je ziet dan een overzicht van alles wat nodig is om die specifieke service te starten en systemd zorgt ervoor dat dat ook allemaal gestart wordt, indien nodig.

 

Een tweede ingangspunt is de [Install]  sectie, die je aan het einde van vrijwel elk unit-bestand vindt. Hierin vind je een regel als WantedBy=local-fs.target. Dit betekent dat als de betreffende unit aangezet wordt door middel van de systemctl enable opdracht, er een link gemaakt wordt naar de betreffende target.

 

Deze link wordt ingericht als een zogenaamde want. En dat is dan wel weer handig, om te achterhalen wat in een specifieke target allemaal nodig is, kun je de wants (letterlijk: de gewilden) achterhalen. Omdat wants specifiek zijn voor een bepaalde systeemconfiguratie, vindt je de wants in de /etc/systemd/system directory. Hier tref je bijvoorbeeld de directory multi-user.target.wants, waarin door middel van symbolic links naar het betreffende unit-bestand aangegeven wordt welke specifieke units nodig zijn in deze target. In afbeelding 1 zie je een voorbeeld.

 

***Afbeelding 1: inhoud van een wants directory ****

 

Zoals je ziet, staan er tussen de wants voor het multi-user.target ook verwijzingen naar andere targets, zoals de basic.target waarin alle essentiële unit files voorkomen, en de localfs.target, waarin alles voorkomt dat nodig is voor de lokale bestandssystemen op een server. Een andere manier overigens om de inhoud van een target te bekijken, is met de systemctl list-dependencies opdracht. Type bijvoorbeeld systemctl list-dependencies multi-user.target voor een boomvormig overzicht van alle units, die in deze target voorkomen.

 

Beheren van systemd

Als beheerder maak je gebruik van de opdracht systemctl om units te beheren. Laten we eens beginnen met het commando waarmee je units start. Dat kan met de opdracht systemctl start <unitnaam>, zoals in systemctl start httpd.service. De tegenhanger van deze opdracht is systemctl stop <unitnaam> waarmee je de betreffende unit weer uit kunt zetten.

 

Als een unit eenmaal gestart is, kun je de status opvragen met het systemctl status commando. In afbeelding 2 zie je wat het resultaat van dit commando is op de httpd.service unit.

 

*** Afbeelding 2: Status van een unit-file bekijken *******

 

In het resultaat van een opdracht als systemctl status begin je pas te zien hoe leuk systemd eigenlijk is. Waar het equivalente commando op een init-systeem (service httpd status) alleen maar laat zien of een service actief is of niet, laat systemd ook uitgebreide informatie over de huidige status van de unit zien. Dat is echt handig, omdat je bij problemen ook gelijk de relevante meldingen uit de logbestanden te zien krijgt. Deze logmeldingen komen overigens van journald, een service die aan systemd gerelateerd is en er voor zorgt dat het afhandelen van logberichten op een veel efficiënter wijze gebeurt dan in het verleden het geval was.

 

Om ervoor te zorgen dat een unit ook geactiveerd wordt als je server herstart wordt, gebruik je het systemctl enable <unitnaam> commando, wat als tegenhanger de opdracht systemctl disable <unitnaam> heeft. In listing 3 zie je wat deze twee commando’s doen:

 

**** LISTING MET ONDERSCHRIFT -> Listing 3: Enablen en disablen van een unit ****

[root@server ~]# systemctl disable httpd

rm ‘/etc/systemd/system/multi-user.target.wants/httpd.service’

[root@server ~]# systemctl enable httpd

ln -s ‘/usr/lib/systemd/system/httpd.service’ \  ‘/etc/systemd/system/multi-user.target.wants/httpd.service’

**** EINDE LISTING ***

 

Wanneer je systemctl enable of disable gebruikt, wordt de [Install] sectie aan het eind van de unit-file gebruikt om te bepalen in welke target de betreffende unit gestart dan wel gestopt zou moeten worden. Als dat bekend is, kan in de wants directory in /etc/systemd/system een symbolic link gemaakt worden bij het enablen van de unit, of verwijderd worden bij het disablen van de unit. Als de betreffende target gestart wordt, hoeft alleen maar deze lijst doorgelopen te worden om te bepalen wat er in een unit nodig is.

 

Mounten en meer

Zoals eerder gezegd, systemd gaat veel verder dan alleen maar het werken met services. Heel veel dingen zijn onder systemd een unit geworden. Om te bepalen welke typen units er allemaal zijn, gebruik je de opdracht  systemctl -t help (zie afbeelding 3).

 

*** Afbeelding 3: Overzicht opvragen van de unit-file types ****

 

Een van de types unit-files is de mount. Dit type zorgt ervoor dat je in plaats van door /etc/fstab mounts ook kunt laten plaatsvinden via systemd. Neem als voorbeeld de inhoud van listing 4:

[Unit]

Description=Mounting the data directory

DefaultDependencies=no

Conflicts=umount.target

Before=local-fs.target umount.target

 

[Mount]

What=/dev/sdb1

Where=/data

Type=ext4

Options=

 

[Install]

WantedBy=local-fs.target

 

Zoals je ziet, deze mount bevat alle informatie, die nodig is om het bestandssysteem op /dev/sdb1 te mounten via systemd. En dat werkt ook gewoon (aannemende dat je een device /dev/sdb1 hebt en dat aan de andere voorwaarden ook voldaan is). Om de mount uit te voeren, plaats je de inhoud van listing 4 in een bestand met de naam /etc/systemd/system/data.mount. Vervolgens type je systemctl start data.mount om het bestandssysteem te activeren. Wil je dat dit voortaan automatisch gebeurt? Type dan systemctl enable data.mount om de mount toe te voegen aan local-fs.target (die in multi-user.target wordt uitgevoerd). Het resultaat is dat de mount na een herstart automatisch geactiveerd wordt. Overigens: systemd maakt het mogelijk om /etc/fstab te vervangen, maar de huidige distributies maken veelal nog gebruik van /etc/fstab. Aan jou de keuze om dit anders te doen!

 

Conclusie

 

In dit artikel heb je geleerd welke mogelijkheden systemd biedt. Onder de indruk? Dit is alleen nog maar het begin! Systemd biedt nog tal van andere mogelijkheden. Hopelijk heeft dit artikel je gemotiveerd om eens verder te kijken.