Hoppa till innehållet

systemd

det som startar allt annat i Linux

Vad det är

Den process som får process-id 1 på nästan varje stor Linux-distribution heter systemd. Det är en system- och tjänstehanterare: den tar upp maskinen vid uppstart, startar och vaktar tjänsterna medan maskinen är igång och stänger ner allt i rätt ordning när den ska av. Projektet skriver namnet med gemener, aldrig SystemD eller System D, och det avslutande d:et är den gamla Unix-markeringen för en daemon. Det hör till familjen init-system. Där finns också SysV-initskripten och Upstart, som det har ersatt på de flesta distributioner, OpenRC och runit, som en del mindre distributioner fortfarande väljer, och launchd, som gör samma jobb på macOS.

Allt som hanteraren tar hand om är en enhet, unit i den engelska dokumentationen, och varje enhet beskrivs i en kort textfil med sektioner och par av nyckel och värde. Manualen räknar upp 11 typer: service, socket, target, device, mount, automount, timer, swap, path, slice och scope. En service kör ett program, och en timer startar en annan enhet enligt en tidsplan. En socket håller en lyssnande port öppen och startar sin service först när den första anslutningen kommer in. Ett target samlar enheter till ett tillstånd som maskinen kan nå, t.ex. ett fleranvändarsystem med nätverk. Beroenden och ordning är skilda inställningar, så ”det här behöver det där” och ”det här startar efter det där” blir två olika rader. Av dem bygger hanteraren en enda transaktion av start- och stoppjobb som den kan kontrollera innan något körs.

Två designval skiljer systemd från de skriptbaserade init-systemen som kom före. Varje tjänst körs i en egen kontrollgrupp (cgroup) i Linux, så hanteraren känner till varje process som tjänsten någonsin har startat och kan stoppa alla, begränsa deras minne och redovisa deras processortid. Dessutom är projektet en hel svit och inte bara en hanterare: den egna webbplatsen räknar upp en loggdaemon, journalen, och komponenter för inloggning, nätverkskonfiguration, tidssynkronisering och namnuppslagning. Distributionerna väljer själva vilka av tilläggen de slår på. Hanteraren på PID 1 är den del som finns nästan överallt.

Varför det spelar roll

På en Linux-server är frågan sällan om du ska köra systemd, för distributionen har redan valt det. Det verkliga beslutet är om du låter systemd vara både vaktare och schemaläggare, eller om du staplar en annan processhanterare, en containerdaemons omstartspolicy eller cron ovanpå.

Låter du systemd sköta det hamnar startordning, omstartspolicy, loggar, resursgränser och sandlådeisolering (sandboxing) i en fil per tjänst, läst av en process som redan kör och som inte kostar något extra minne. En enhet kan sätta ett tak för minnet med MemoryMax=, göra operativsystemets egna kataloger skrivskyddade för sin tjänst med ProtectSystem= och köra under en tillfällig användare med DynamicUser=. Kommandot systemd-analyze security ger dessutom varje tjänst ett exponeringsvärde på en skala från 0,0 till 10,0. Ett jobb som en timer startar skiljer sig från en rad i crontab på tre sätt: det loggar till journalen, det får samma sandlådeisolering som vilken tjänst som helst, och det visar sin senaste och nästa körning i systemctl list-timers. Kunskapen går också att ta med sig: den som kan läsa en enhetsfil kan sköta de flesta Linux-servrar, oavsett distribution.

Kostnaderna finns också. Enheter fungerar bara på Linux, så ett team som även levererar till macOS skriver property lists för launchd, och ett som levererar till BSD-systemen skriver rc-skript. I de flesta containrar finns ingen systemd alls: applikationen är själv PID 1, och omstartspolicyn hör till det som kör containern. Där containrar och vanliga tjänster delar värd minskar Quadlet i Podman avståndet genom att göra en containerbeskrivning till en vanlig tjänsteenhet, så att båda sorternas arbetslast står under samma vaktare.

Så ser det ut i praktiken

Företagets egen plattform lutar sig mot systemd på tre ställen.

Produktionsvärden i hela företaget på en enda värd med 512 MB har en kärna och ungefär 464 MB användbart minne. Den bär webbservern för sju domäner, git-forgen, en onion-tjänst för Tor, servrar för Gemini och Gopher, krypterade säkerhetskopior och spärrning av intrångsförsök. I den storleken måste varje process som ligger kvar i minnet förtjäna sin plats. Därför är systemd vaktaren, utan någon andra processhanterare ovanpå, och containrarna körs som Quadlet-enheter under samma hanterare i stället för under en egen containerdaemon. När frågan var om värden också klarade en onion-tjänst visade ett dygns mätningar att tillgängligt minne aldrig föll under ungefär 310 MB av 464.

De krypterade säkerhetskopiorna med en månatlig återläsningsövning körs av en systemd-timer: en databasdump där det finns en databas, en krypterad och deduplicerad ögonblicksbild som skickas via SFTP till lagring hos en annan leverantör, en gallring enligt bevarandepolicyn och en integritetskontroll. Ett dödmansgrepp pingas bara när allt har lyckats. Arbetet stötte också på en av de vassare kanterna i systemd, som beskrivs nedan: lösenfrasen ligger i en fil som enheterna läser, eftersom ett värde som skickas via enhetens miljö får sina escape-tecken tolkade, och ett omvänt snedstreck i den skulle i tysthet ha ändrat lösenfrasen.

Teknikstacken drar den allmänna gränsen: vanliga systemd-enheter som standard, och Docker och Kubernetes för de arbetslaster som förtjänar dem.

Var det går fel

En redigerad enhet kör vidare med sina gamla inställningar. Hanteraren läser in enhetsfilerna i minnet, så en ändring på disken gör ingenting förrän systemctl daemon-reload bygger om hanterarens bild av dem, och systemctl cat kan visa en fil som skiljer sig från den som den körande hanteraren använder. Gör ändringar med systemctl edit, som laddar om åt dig, eller ladda om och starta sedan om tjänsten.

En hemlighet når tjänsten med ett tecken för lite. Värden i Environment= går igenom de citatregler som systemd själv använder, där escape-sekvenser i C-stil tolkas och ett bokstavligt omvänt snedstreck måste skrivas dubbelt. Dokumentationen varnar också för att miljövariabler syns för oprivilegierade klienter över D-Bus. Lägg hemligheter i en fil och lämna över dem med LoadCredential=, som ger tjänsten en skrivskyddad kopia som bara dess användare och root kan läsa.

En paketuppgradering tar tyst bort en lokal rättelse. Ändringar i leverantörens fil under /usr/lib/systemd/system/ skrivs över vid nästa uppdatering av paketet. Lägg ändringarna i en drop-in-fil under /etc/systemd/system/<unit>.d/ i stället. Den läggs ovanpå originalet och överlever uppgraderingar. Kommandolistor läggs ihop i stället för att ersättas, så en drop-in som byter startkommando tömmer det först med en tom tilldelning:

# /etc/systemd/system/app.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/local/bin/app --config /etc/app/app.toml
Restart=on-failure

Ett nattligt jobb hoppar över natten då servern var avstängd. En kalendertimer som skulle ha körts medan maskinen var nere väntar bara på sin nästa tid, eftersom Persistent= som standard är false. Sätt Persistent=true på säkerhetskopiering och andra jobb som måste köras, så går den missade körningen så snart timern är aktiv igen.

En tjänst som har kraschat förblir nere. Restart= är som standard no, så en tjänst som dör ligger kvar död tills någon märker det. Sätt Restart=on-failure på tjänster som ska köra hela tiden, och räkna med att hanteraren ger upp en enhet som når sin startgräns. En manuell start får då vänta tills intervallet har gått eller tills någon kör systemctl reset-failed.

Läs mer

  • systemd(1) — själva hanteraren: enhetstyperna, uppstartsförloppet och de signaler den svarar på.
  • systemd.unit(5) — enhetsfiler, beroenden, startgränser och de drop-in-kataloger som åsidosätter dem.
  • systemd.service(5) — tjänstetyper, omstartspolicy och hur kommandolistor nollställs.
  • systemd.exec(5) — körmiljön, alternativen för sandlådeisolering och credentials till tjänster.
  • systemd.timer(5) — kalendertimrar och monotona timrar, bl.a. Persistent=.
  • systemctl(1) — kommandoraden för att starta, redigera, ladda om och inspektera enheter.

Ordlista