SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor

Waarom start systemd mijn service niet automatisch?

Het Restart-beleid controleert alleen het hoofdproces, waardoor uitgevallen sub-processen onopgemerkt blijven. Leer hoe Type-instellingen en cgroups uw service beïnvloeden.

Het korte antwoord: systemd-herstartbeleid controleert één proces

systemd-herstartbeleid controleert één proces per unit: het hoofdproces. Restart= leest de exit-status van uitsluitend dat ene proces. De control group van een unit kan twintig processen bevatten; als er één sterft, blijft de unit active (running) omdat het hoofdproces nog steeds actief is. Voor systemd is er niets mislukt, dus wordt er niets herstart.

systemd is wel op de hoogte van de andere processen. Het beëindigt deze wanneer de unit stopt, telt hun geheugengebruik mee voor de limieten van de unit, past het CPU-quotum van de unit op hen toe en toont ze in systemctl status. Het leest echter nooit hun exit-status. De herstartlogica en de cgroup zijn twee verschillende zaken, en het grootste deel van deze handleiding gaat over het verschil tussen beide.

Wat de cgroup bevat en wat de herstartlogica leest

Een cgroup (control group) is een kernelobject dat een verzameling processen beheert. Elke service-unit krijgt er een, vernoemd naar de unit zelf. Een proces kan deze groep niet verlaten. Kinderprocessen erven de cgroup van hun ouder, en een proces zonder privileges kan zichzelf niet naar een andere locatie verplaatsen. Daarom kan systemd een daemon die twee keer fork-t wel opschonen, iets wat oude init-scripts nooit betrouwbaar konden doen.

Bekijk beide feiten naast elkaar:

systemd-cgls --unit myapp.service
systemctl show -p MainPID -p NRestarts -p Restart -p RestartUSec myapp.service

systemd-cgls somt elk proces in de unit op. MainPID is het enkele getal dat het herstartbeleid leest. Wanneer die twee niet overeenkomen met uw mentale model, is dat verschil de bug. MainPID=0 is erger dan een onjuiste PID: het betekent dat systemd helemaal niets bijhoudt, waardoor er nooit een Restart=-waarde kan worden geactiveerd.

Er is één echte uitzondering op de regel van het hoofdproces. Als de out-of-memory killer van de kernel een proces binnen de cgroup van de unit beëindigt, ziet systemd dit, omdat het het memory.events-bestand van de cgroup monitort. OOMPolicy= bepaalt wat er daarna gebeurt, en de standaardinstelling is stop: de gehele unit wordt gestopt, het resultaat wordt geregistreerd als oom-kill, en dat telt als een fout, waardoor Restart=on-failure wordt geactiveerd. Het journal vermeldt dit duidelijk.

myapp.service: A process of this unit has been killed by the OOM killer.
myapp.service: Failed with result 'oom-kill'.

Een kindproces dat wordt gedood vanwege geheugengebrek haalt de unit dus wel onderuit, terwijl hetzelfde kindproces dat sterft door een segmentation fault dat niet doet. Als u geheugenlimieten instelt op een unit, lees dan hoe MemoryMax en CPUQuota van toepassing zijn op de cgroup van een unit voordat u het herstartbeleid aanpast, omdat die twee functies hier samenkomen en nergens anders.

Hoe Type= het hoofdproces bepaalt

Type= in de sectie [Service] gaat niet alleen over de opstartvolgorde. Het is de regel die bepaalt welk PID (process ID) het MainPID wordt, wat hetzelfde is als bepalen wat Restart= kan zien.

  • Type=simple is de standaardinstelling. Het proces dat systemd vanuit ExecStart= fork, is het hoofdproces. systemd markeert de unit direct als gestart, nog voordat het weet of de exec überhaupt werkte. Een typefout in het binaire pad resulteert in een starttaak die slaagt, en vervolgens een Main process exited, code=exited, status=203/EXEC een moment later.
  • Type=exec gedraagt zich als simple, behalve dat de starttaak wacht tot de exec is geslaagd. Dat verandert de bovenstaande typefout in een eerlijke startfout. Het vereist systemd 240 of nieuwer, wat elke ondersteunde distributie heeft. Geef de voorkeur aan simple.
  • Type=forking verwacht dat het proces uit ExecStart= een achtergronddaemon fork en vervolgens afsluit. systemd wacht tot de parent afsluit en zoekt dan naar de echte daemon. Geef het PIDFile=. Zonder dit werkt GuessMainPID= (standaard ingeschakeld) alleen als er precies één proces in de cgroup overblijft. Laat er twee achter en MainPID blijft 0.
  • Type=notify betekent dat de service sd_notify(3) aanroept en READY=1 verstuurt wanneer deze verkeer kan afhandelen. Het kan ook MAINPID= sturen om systemd een ander proces te geven om te volgen. NotifyAccess= staat standaard op main, dus een notificatie verstuurd door een child wordt genegeerd en de journal benoemt het PID waar het vandaan kwam.
  • Type=oneshot heeft geen blijvend hoofdproces. De unit wordt inactief zodra ExecStart= is voltooid, tenzij u RemainAfterExit=yes instelt. Restart=always en Restart=on-success worden hier geweigerd, met de melding Service has Restart= set to either always or on-success, which isn't allowed for Type=oneshot services. Refusing. De andere waarden, inclusief on-failure, worden geaccepteerd.

Twee Type=forking-fouten zijn het onthouden waard, omdat ze elk resulteren in een unit die zonder zichtbare reden defect lijkt:

myapp.service: Can't open PID file /run/myapp.pid (yet?) after start: No such file or directory
myapp.service: New main PID 4711 does not belong to service, and PID file is not owned by root. Refusing.

De eerste betekent dat de daemon zijn PID-bestand ergens anders schrijft, of het later schrijft dan systemd zoekt. De tweede betekent dat het PID-bestand een proces buiten de cgroup van de unit benoemt, wat systemd weigert te adopteren, omdat een schrijfbaar PID-bestand anders een manier zou worden om systemd signalen te laten sturen naar elk willekeurig proces op de machine.

Waarom een wrapper-script het overlijden van zijn kinderen verbergt

Dit is de structuur die de vraag in de titel oproept.

#!/bin/bash
/usr/local/bin/myapp-web &
/usr/local/bin/myapp-worker &
wait

De unit is Type=simple, dus het hoofdproces is de shell. wait zonder argumenten keert pas terug nadat elk kindproces is afgesloten. Beëindig de worker en de shell blijft wachten op het webproces, waardoor de shell niet afsluit, waardoor MainPID niet afsluit en Restart= nooit wordt geraadpleegd. De cgroup bevat nu één proces minder, systemctl status toont de kortere boomstructuur en de unit is nog steeds active (running). Niets in systemd houdt die boomstructuur in de gaten op wijzigingen.

Een tweede versie van dezelfde fout is minder opvallend:

ExecStart=/bin/sh -c 'export APP_ENV=production; /usr/local/bin/myapp'

Het hoofdproces is de shell, niet myapp. Bij systemctl stop stuurt systemd SIGTERM naar het hoofdproces, en een shell die wacht op een kindproces in de voorgrond geeft het signaal niet door. Het stoppen duurt dan de volledige TimeoutStopSec, standaard 90 seconden, en eindigt als volgt:

myapp.service: State 'stop-sigterm' timed out. Killing.
myapp.service: Killing process 4711 (myapp) with signal SIGKILL.

De oplossing is exec. Schrijf exec /usr/local/bin/myapp en de shell wordt vervangen door het programma, waardoor MainPID het programma is en signalen het bereiken. Beter nog, verwijder de shell en gebruik Environment= of EnvironmentFile= in de unit. Merk op dat deze bug zichzelf verbergt wanneer de -c-string één enkel commando bevat, omdat zowel bash als dash dat geval optimaliseren naar een directe exec. Voeg een tweede commando toe aan de string en de shell blijft actief voor uw programma.

Reproduceer dit op een test-VPS in twee minuten

Sla de bovenstaande wrapper op als /usr/local/bin/two-children.sh, maak deze uitvoerbaar met chmod +x en vervang de twee programmapaden door sleep 3600. Wijs een unit aan naar het bestand met Type=simple en Restart=on-failure, en voer daarna systemctl daemon-reload uit en start de unit. Voer systemd-cgls --unit two-children.service uit en noteer de drie PID's: de shell en zijn twee kinderen. Beëindig één kindproces met sudo kill <pid>. Controleer de unit opnieuw. De boomstructuur is één proces korter, de status is nog steeds active (running) en het logboek bevat geen nieuwe informatie. Voer nu in plaats daarvan sudo kill -9 <shell pid> uit. De unit faalt, het overlevende kindproces wordt opgeruimd omdat KillMode=control-group de standaardinstelling is, en het logboek toont Scheduled restart job, restart counter is at 1.

Het volledige Restart= vocabulaire, en wanneer on-failure de voorkeur verdient boven always

Restart= accepteert zeven verschillende waarden, waarbij het onderscheid wordt gemaakt op basis van wat als een correcte afsluiting wordt beschouwd. systemd beschouwt exitcode 0, elke code vermeld in SuccessExitStatus=, en de signalen SIGHUP, SIGINT, SIGTERM en SIGPIPE als een correcte afsluiting. Al het overige, inclusief SIGKILL en SIGSEGV, wordt als incorrect beschouwd.

  • no is de standaardinstelling. De unit herstart zichzelf nooit; daarom stopt een unit zonder Restart=-regel direct bij de eerste crash en blijft deze gestopt.
  • on-success herstart alleen na een correcte afsluiting.
  • on-failure herstart bij een exitcode die ongelijk is aan nul, een incorrect signaal, een start- of stop-timeout, of het verlopen van een watchdog.
  • on-abnormal herstart bij een incorrect signaal, een timeout of het verlopen van een watchdog, maar nooit bij een reguliere exitcode die ongelijk is aan nul.
  • on-abort herstart alleen bij een incorrect signaal, wat duidt op een crash.
  • on-watchdog herstart alleen wanneer WatchdogSec= verloopt.
  • always herstart in alle bovenstaande gevallen, inclusief een correcte afsluiting met status 0.

on-failure is de juiste standaardinstelling voor een daemon die continu moet draaien. Het herstelt een crash, maar laat een bewuste exit 0 ongemoeid. always hoort bij een programma dat correct afsluit om redenen buiten de eigen controle, zoals een tunnel-client die 0 teruggeeft wanneer de verbinding aan de andere kant wordt verbroken. Het nadeel van always is dat het bugs maskeert: een service die opstart, een defect configuratiebestand leest, de fout logt en afsluit met 0, zal in een oneindige lus terechtkomen, waarbij het enige teken een oplopende herstartteller is.

SuccessExitStatus= verlegt de grens tussen correct en incorrect. Borg geeft 1 terug voor waarschuwingen en 2 voor fouten; een backup-unit zonder SuccessExitStatus=1 wordt dus elke keer als mislukt gemarkeerd wanneer er één onleesbaar bestand wordt overgeslagen. RestartPreventExitStatus= bevat codes die een herstart blokkeren, zelfs onder always; dit is de correcte manier voor een programma om aan te geven dat het niet opnieuw moet opstarten. RestartForceExitStatus= doet het tegenovergestelde. Een backup-taak hoort thuis in een Type=oneshot-unit die wordt aangestuurd door een timer in plaats van in een herstart-lus, en het service- en timer-paar dat een taak op schema uitvoert is de structuur die u daarvoor moet kopiëren.

Eén waarschuwing met betrekking tot testen. Het beëindigen van uw service met een reguliere kill <pid> verstuurt SIGTERM, wat op de lijst met correcte afsluitingen staat. Hierdoor doet Restart=on-failure terecht niets en concludeert u ten onrechte dat uw configuratie defect is. Gebruik in plaats daarvan kill -9 <pid> of systemctl kill -s SIGKILL myapp.service. Onthoud ook dat geen enkele waarde van Restart= wordt geactiveerd na systemctl stop, of wanneer de unit is gestopt omdat een BindsTo=- of PartOf=-afhankelijkheid is weggevallen. Een stop-taak is geen fout.

RestartSec, en de standaardwaarde van 100 milliseconden

RestartSec= is de pauze tussen het stoppen van de unit en het moment dat systemd deze opnieuw start; de standaardwaarde is 100 milliseconden. Controleer wat uw unit daadwerkelijk heeft geladen:

systemctl show -p RestartUSec -p StartLimitIntervalUSec -p StartLimitBurst myapp.service

Een unit waarbij dit niet is ingesteld, toont RestartUSec=100ms. Die standaardwaarde is prima voor een service die eenmalig crasht en weer terugkomt. Het is echter onjuist voor een service die helemaal niet kan starten, omdat er dan vijf herstarts plaatsvinden binnen een halve seconde. Dit is precies wat de hierna beschreven snelheidslimiet (rate limit) activeert. Voor elke service die afhankelijk is van een database, een mount of een netwerkroute, stelt u RestartSec=5s of meer in.

Sinds augustus 2026 bieden systemd 254 en nieuwer ook RestartSteps= en RestartMaxDelaySec=. Hiermee wordt de vertraging vanaf RestartSec= stapsgewijs vergroot tot een maximum over dat aantal pogingen. Ubuntu 24.04 wordt geleverd met systemd 255 en beschikt hierover. Debian 12 wordt geleverd met systemd 252 en beschikt hier niet over. Groeiende vertragingen zijn de juiste oplossing wanneer de afhankelijkheid voor langere tijd onbereikbaar kan zijn.

Wat "start request repeated too quickly" werkelijk betekent

Dit is de status die gebruikers aanzien voor het willekeurig opgeven door systemd. Het is in feite een teller. De regel is: als een unit meer dan StartLimitBurst= keer wordt gestart binnen StartLimitIntervalSec=, weigert systemd deze opnieuw te starten en plaatst de unit in de status failed. De standaardinstellingen zijn 5 starts in 10 seconden.

Het journal toont de volgende reeks:

myapp.service: Scheduled restart job, restart counter is at 5.
myapp.service: Start request repeated too quickly.
myapp.service: Failed with result 'start-limit-hit'.
Failed to start myapp.service - My application.

en systemctl start geeft het antwoord met de oplossing al uitgeschreven:

Job for myapp.service failed because start of the service was attempted too often. See "systemctl status myapp.service" and "journalctl -xeu myapp.service" for details. To force a start use "systemctl reset-failed myapp.service" followed by "systemctl start myapp.service" again.

systemctl reset-failed myapp.service wist de teller en de status failed. Niets anders doet dit, dus een gewone systemctl start blijft geweigerd worden totdat u dit commando uitvoert. Handmatige starts tellen ook mee voor de limiet; een paar ongeduldige systemctl restart-pogingen terwijl u een configuratiebestand bewerkt, kunnen de foutmelding al triggeren zonder dat er sprake is van een crash.

Het onderdeel dat mensen op het verkeerde been zet: start-limit-hit vermeldt nooit waarom de service faalde. Het geeft alleen aan dat de service herhaaldelijk en snel achter elkaar faalde. De werkelijke reden staat in de journal-regels daarboven.

Beide instellingen horen in de sectie [Unit] thuis. U zult voorbeelden vinden die ze in [Service] plaatsen, wat oudere versies van systemd accepteerden, en daar begint de verwarring. Schrijf ze in [Unit] en vraag daarna aan systemd wat er is geladen met systemctl show, aangezien de geladen waarde de enige is die telt.

[Unit]
Description=My application
StartLimitIntervalSec=300
StartLimitBurst=5

[Service]
Type=exec
ExecStart=/usr/local/bin/myapp
Restart=on-failure
RestartSec=10s

Dit geeft de unit vijf pogingen binnen een venster van vijf minuten voordat het systeem opgeeft. StartLimitIntervalSec=0 schakelt de limiet volledig uit, en u dient te weten waar u voor kiest: een service die niet kan starten zal nu oneindig blijven proberen en elke keer naar het journal schrijven. De standaardinstellingen voor de gehele machine staan in /etc/systemd/system.conf als DefaultStartLimitIntervalSec= en DefaultStartLimitBurst=.

Een naburige instelling verdient een waarschuwing. StartLimitAction= bepaalt wat er gebeurt wanneer de limiet wordt bereikt, en accepteert waarden zoals reboot, reboot-force en poweroff. De standaardwaarde is none, wat de unit op failed zet en de machine verder met rust laat. Op een externe VPS betekent poweroff een server die uitgeschakeld blijft totdat u de console van de provider opent.

Oplossing één: één proces per unit

Dit is in bijna elk geval het juiste antwoord. Als twee programma's moeten draaien, schrijf dan twee units. Elke unit heeft dan een echt hoofdproces, een echte exit-status en een eigen herstartbeleid. U krijgt bovendien gescheiden logs, gescheiden resourcelimieten en gescheiden herstarttellers; precies wat u nodig heeft om drie uur 's nachts.

Druk de relatie tussen de units uit in de unit-bestanden, niet in een shell-script.

  • After= regelt alleen de opstartvolgorde. Het zegt niets over fouten.
  • Requires= start de andere unit tegelijk met deze unit, en stopt deze unit als de andere expliciet wordt gestopt.
  • BindsTo= is Requires= plus het geval waar het u om gaat: deze unit stopt wanneer de andere unit om welke reden dan ook stopt, inclusief een crash. Combineer dit met After=, anders is de volgorde ongedefinieerd.
  • PartOf= propageert stoppen en herstarten naar beneden, zodat systemctl restart myapp.target elke unit bereikt die PartOf= is.
  • Upholds= (systemd 249 en nieuwer, dus Ubuntu 22.04 en later) houdt de genoemde unit draaiende: als deze stopt, start systemd hem opnieuw. Dit is onderhevig aan dezelfde limiet voor de opstartsnelheid als alle andere processen.

Een worker die nooit mag draaien zonder zijn API-server, en die systemd actief houdt zolang de API draait:

# /etc/systemd/system/myapp-api.service
[Unit]
Description=myapp API server
Wants=network-online.target
After=network-online.target
Upholds=myapp-worker.service

[Service]
Type=exec
User=myapp
ExecStart=/usr/local/bin/myapp serve
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/myapp-worker.service
[Unit]
Description=myapp background worker
BindsTo=myapp-api.service
After=myapp-api.service
StartLimitIntervalSec=120
StartLimitBurst=5

[Service]
Type=exec
User=myapp
ExecStart=/usr/local/bin/myapp worker
Restart=on-failure
RestartSec=5s

De worker heeft geen [Install]-sectie en wordt nooit handmatig ingeschakeld. De API-unit trekt de worker aan met Upholds=, dus systemctl enable --now myapp-api.service is het enige commando dat u uitvoert. Herlaad de configuratie en controleer wat systemd van het paar heeft gemaakt:

sudo systemctl daemon-reload
systemd-analyze verify /etc/systemd/system/myapp-worker.service
systemctl list-dependencies myapp-api.service

systemd-analyze verify geeft helemaal niets weer als het bestand in orde is. Elke uitvoer duidt op een probleem, meestal een sleutel die systemd niet herkent in de sectie waar u deze heeft geschreven, of een afhankelijkheid van een unit die niet bestaat.

Oplossing twee: Type=notify, zodat systemd meer weet dan alleen een PID

Als het programma het systemd-notificatieprotocol ondersteunt, gebruik dit dan. Met Type=notify laat de service aan systemd weten wanneer deze gereed is. Hierdoor wordt de opstartvolgorde feitelijk in plaats van gebaseerd op aannames. Bovendien kan het MAINPID= verzenden om systemd te wijzen op het relevante proces in plaats van op een launcher.

WatchdogSec= is de moeite waard. Wanneer dit is ingesteld, moet de service minimaal zo vaak WATCHDOG=1 via sd_notify(3) verzenden. Zodra de berichten stoppen, beëindigt systemd de service met SIGABRT en markeert deze als mislukt, waarna Restart=on-failure of Restart=on-watchdog de service herstelt. Dit is de enige ingebouwde methode om een proces te herstarten dat wel draait maar is vastgelopen; een exit-statusbeleid kan dit nooit detecteren.

[Service]
Type=notify
NotifyAccess=main
ExecStart=/usr/local/bin/myapp serve
WatchdogSec=30s
Restart=on-failure
RestartSec=5s

Een watchdog-activering verschijnt in de journal als myapp.service: Watchdog timeout (limit 30s)!, gevolgd door de beëindiging. Als de unit in plaats daarvan in activating (start) blijft hangen totdat TimeoutStartSec is verstreken, is READY=1 nooit aangekomen. Dit betekent dat het programma het protocol niet ondersteunt, of dat NotifyAccess=main een notificatie afwijst die afkomstig is van een onderliggend proces, wat in de journal wordt gerapporteerd met beide PIDs.

Voor software die een HTTP-health-endpoint aanbiedt maar geen sd_notify-ondersteuning heeft, zijn de eerlijke opties: een kleine timer-unit die het endpoint controleert en systemctl restart aanroept, of het laten uitvoeren van de controle door een container-runtime. Hiervoor bestaat Compose healthchecks en hun herstartgedrag.

Oplossing drie: een supervisor binnen de unit, alleen als er geen andere keuze is

Sommige software wordt daadwerkelijk geleverd als een bundel processen achter een launcher die u niet kunt opsplitsen. In dat geval draait u een supervisor binnen de unit en accepteert u de gevolgen: systemd houdt de supervisor in de gaten, de supervisor houdt de rest in de gaten, en uw herstartbeleid staat nu in twee verschillende bestanden.

De meest voorkomende vorm hiervan is een container-runtime. Een docker compose of podman unit volgt exact dit patroon, waarbij het herstartbeleid per container in het Compose-bestand staat en de systemd unit alleen de runtime draaiende houdt. Als dit uw situatie is, laat de unit die een Compose-stack opstart bij het booten de werkende versie zien, inclusief waarom Type=oneshot met RemainAfterExit=yes daar meestal de juiste keuze is.

De cgroup werkt nog steeds in uw voordeel. Alles wat de supervisor start, blijft binnen de cgroup van de unit, dus MemoryMax=, CPUQuota= en de opschoning bij het stoppen dekken nog steeds de gehele boomstructuur. Alleen de beslissing om te herstarten is gedelegeerd.

Welke supervisor u ook kiest, stel niet zonder na te denken Restart=always in op de buitenste unit in combinatie met een agressief herstartbeleid aan de binnenkant. Twee lagen van herstartlogica, elk met een eigen backoff-mechanisme, zorgen voor een service die minutenlang blijft flapperen en een journal dat niet verklaart waarom.

ExitType=cgroup betekent niet "herstart wanneer een willekeurig proces stopt"

ExitType= (systemd 250 en nieuwer, dus zowel Ubuntu 24.04 als Debian 12 beschikken hierover) is de instelling die mensen vinden wanneer zij naar dit probleem zoeken, en deze doet precies het tegenovergestelde van wat de naam suggereert. De standaardwaarde, ExitType=main, betekent dat de service als gestopt wordt beschouwd zodra het hoofdproces eindigt. ExitType=cgroup betekent dat de service als actief wordt beschouwd totdat het laatste proces in de cgroup eindigt.

ExitType=cgroup maakt een unit dus minder gevoelig voor het stoppen van een enkel proces, niet gevoeliger. Dit is de juiste instelling voor een programma dat zijn eigenlijke worker fork't en het ouderproces afsluit zonder een PID-bestand te schrijven, waarbij Type=forking de daemon niet kan vinden. Het is de verkeerde instelling voor de hier beschreven fout.

Er bestaat geen Restart=-waarde die betekent "herstart de unit wanneer een willekeurig proces in de cgroup stopt". Als u dat gedrag nodig heeft, heeft u één proces per unit nodig. Als u het programma niet kunt opsplitsen en u de wrapper-script beheert, is het dichtstbijzijnde alternatief wait -n, dat direct terugkeert zodra het eerste child-proces stopt:

#!/bin/bash
/usr/local/bin/myapp-web &
/usr/local/bin/myapp-worker &
wait -n
exit 1

Wanneer nu een child-proces stopt, wordt de wrapper afgesloten met een status die ongelijk is aan nul, waardoor Restart=on-failure in werking treedt. Dit is een compromis, geen definitieve oplossing. U behoudt één herstartteller voor twee programma's, één logstroom en er is geen mogelijkheid om het falende deel afzonderlijk te herstarten.

Hoe u inspecteert wat er daadwerkelijk is gebeurd

Vier commando's, in deze volgorde.

systemctl status myapp.service
systemd-cgls --unit myapp.service
systemctl show -p MainPID -p NRestarts -p Result -p ExecMainStatus myapp.service
journalctl -u myapp.service -b -o short-precise

systemctl status toont de status, het hoofd-PID en de cgroup-boom op één scherm. Een gezonde unit leest Active: active (running) met een Main PID:-regel die het proces benoemt dat u verwacht. Als de boom onderaan processen vermeldt die u niet herkent, of als er een ontbreekt die u wel verwacht, dan heeft u het antwoord al.

systemd-cgls --unit drukt dezelfde boom af zonder afkapping, wat van belang wordt zodra een unit meer dan een handvol processen bevat.

systemctl show geeft machineleesbare feiten. NRestarts= is de herstartteller, en dit is de snelste manier om een service die veertig keer is herstart te onderscheiden van een service die sinds het opstarten draait. Result= bevat de laatste foutreden: exit-code, signal, timeout, oom-kill, watchdog of start-limit-hit. ExecMainStatus= is de ruwe exit-status van het laatste hoofdproces.

De journal bevat de reeks. Dit zijn de drie regels om op te zoeken:

myapp.service: Main process exited, code=exited, status=1/FAILURE
myapp.service: Failed with result 'exit-code'.
myapp.service: Scheduled restart job, restart counter is at 1.

code=exited, status=N betekent dat het programma ervoor koos om N terug te geven, dus de fout ligt bij het programma of de configuratie ervan. code=killed, signal=SEGV betekent dat het is gecrasht. code=killed, signal=TERM betekent meestal dat iets anders vroeg om te stoppen, wat geen fout is en geen Restart=on-failure zal triggeren. code=dumped betekent dat het een core-bestand heeft achtergelaten, dat coredumpctl list u zal tonen wanneer systemd-coredump is geïnstalleerd.

Over meerdere machines heen is NRestarts het getal dat de moeite waard is om volgens een schema te verzamelen. Een unit waarvan de teller elke dag oploopt, faalt elke dag, ongeacht of iemand het heeft opgemerkt. Zodra u meer dan twee of drie servers beheert, verandert een consistente manier om één commando op elke server uit te voeren dit van een vermoeden in een rapport.

FAQ

Waarom meldt systemctl dat mijn service actief is terwijl het proces is gestopt?

systemd houdt één proces per service-unit bij, het hoofdproces, en Restart= leest alleen de exit-status van dat proces. Alle andere processen die de unit start, bevinden zich in dezelfde cgroup; systemd beëindigt deze processen wel wanneer de unit stopt, maar controleert ze nooit op hun exit-status. Voer systemctl show -p MainPID myapp.service uit en vergelijk het aantal met systemd-cgls --unit myapp.service. Als het proces dat is gestopt wel in de boomstructuur verschijnt maar niet MainPID is, dan heeft systemd zich precies volgens het ontwerp gedragen. De oplossing is één proces per unit, waarbij de relatie tussen de units wordt gedefinieerd als BindsTo= en Upholds=.

Wat betekent "start request repeated too quickly"?

Dit betekent dat de unit meer dan StartLimitBurst= keer is gestart binnen StartLimitIntervalSec=. De standaardinstelling is 5 starts in 10 seconden, waarna systemd stopt met proberen. Dit is een rate limit en het geeft niet aan waarom de service faalde; lees daarom de journal-regels die daaraan voorafgaan. Wis de status met systemctl reset-failed myapp.service en los daarna de onderliggende fout op. Als de service wacht op een traag opstartend onderdeel, verhoog dan RestartSec=, aangezien de standaardpauze van 100 milliseconden alle vijf pogingen in minder dan een seconde verbruikt.

Moet ik Restart=always of Restart=on-failure gebruiken?

Gebruik on-failure voor vrijwel alles. Dit herstart de service bij een crash, een non-zero exit, een timeout of een watchdog-fout, en laat een bewuste exit 0 met rust. Gebruik always alleen wanneer het programma correct afsluit om redenen buiten de eigen controle, zoals een client die 0 retourneert wanneer de tegenpartij de verbinding verbreekt. Het nadeel van always is dat een service die een defecte configuratie leest, één fout logt en afsluit met 0, oneindig blijft loopen. Het enige zichtbare symptoom is dan dat NRestarts oploopt in systemctl show.

Waarom activeert het handmatig beëindigen van mijn proces geen herstart?

Omdat systemd SIGHUP, SIGINT, SIGTERM en SIGPIPE beschouwt als correcte afsluitingen, en een standaard kill <pid> verstuurt een SIGTERM. Onder Restart=on-failure is een correcte afsluiting geen fout, dus vindt er geen herstart plaats en lijkt de configuratie defect terwijl dat niet zo is. Test dit met kill -9 <pid> of systemctl kill -s SIGKILL myapp.service; dit is een onjuiste beëindiging die het beleid wel activeert. Dezelfde regel verklaart waarom systemctl stop nooit uw herstartbeleid tegenwerkt.

Waar horen StartLimitIntervalSec en StartLimitBurst te staan?

In de sectie [Unit]. Oudere documentatie en oudere systemd-versies plaatsen deze in [Service], waardoor gekopieerde voorbeelden onderling verschillen. Gok niet welke versie uw systeem ondersteunt. Vraag na systemctl daemon-reload aan systemd wat er is geladen met systemctl show -p StartLimitBurst -p StartLimitIntervalUSec myapp.service en beschouw die getallen als de waarheid. systemd-analyze verify /etc/systemd/system/myapp.service detecteert sleutels die systemd helemaal niet herkent en geeft niets weer als het bestand correct is.

#systemd#restart#service-unit#cgroups#reliability