...

OOM Score og OOM Score Adjust forklaret i forbindelse med hostingdrift

Jeg forklarer den OOM-score og OOM Score Adjust som konkrete styringsværktøjer i hostingdriften: De bestemmer, hvilke processer Linux-OOM-Killer afslutter ved hukommelsesmangel, og hvilke den beskytter. På den måde bevarer jeg kontrollen, når RAM bliver knap, og sørg for, at vigtige tjenester forbliver online.

Centrale punkter

For at give et hurtigt overblik vil jeg kort sammenfatte de vigtigste tanker.

  • Prioritet Ved ressourceknaphed: OOM-score vurderer, hvilken proces der skal afbrydes først.
  • Fin kontrol med oom_score_adj: Fra -1000 (beskytte) til +1000 (ofre).
  • Dynamik i stedet for en fast værdi: Værdien ændrer sig afhængigt af belastning og konfiguration.
  • Værtskabspraksis: Beskyt kritiske tjenester, og afslut hellere ikke-kritiske worker-processer.
  • Årsager Løsning: Kontroller grænser, cgroups og RAM-allokering.

Sådan fungerer Linux OOM-killer

Ved høj hukommelsestryk beslutter Linux-kernen, hvilke processer den skal afslutte for at sikre, at systemet forbliver responsivt. Jeg observerer i den forbindelse, hvordan Kernen tildeler hver proces en slags „badness“, der i høj grad afhænger af det aktuelle hukommelsesforbrug. Hvis der ikke frigøres tilstrækkelig mængde RAM eller swap, griber OOM-Killer ind og afslutter den proces, der har den højeste vurdering. Denne mekanisme forhindrer systemnedbrud, men erstatter ikke en ordentlig kapacitetsplanlægning på værts- og tjenesteniveau. Jeg gennemgår OOM-loggen og kan se, om en tjeneste er blevet markeret på grund af stor hukommelsesforbrug eller forkert konfiguration.

Sådan forstår du OOM-score: Dynamik og skala

Jeg tjekker OOM-score i en proces i /proc/PID/oom_score og aflæser dermed, hvor udsat den er i øjeblikket. Skalaen går praktisk talt fra 0 til 1000: Jo tættere på 1000, jo større er risikoen for, at processen bliver offer for processkiller. Denne værdi er et øjebliksbillede, da belastningsspidser, cgroup-grænser og cache-størrelser ændrer sig løbende. Derfor vurderer jeg aldrig scoren isoleret, men i sammenhæng med arbejdshukommelse, swap, overcommit og parallelle processer. Den, der regelmæssigt holder øje med scoren, genkender typiske mønstre og kan forudse flaskehalse, før de sætter tjenester ud af spillet.

Målrettet brug af OOM Score Adjust

Med oom_score_adj Jeg justerer aktivt en process' vurdering fra -1000 til +1000. Hvis jeg indstiller den til -1000, beskytter jeg processen fuldstændigt, mens høje positive værdier bevidst gør den klar til at blive ofret. Jeg vælger med omtanke, da for mange beskyttede processer begrænser OOM-killerens handlingsrum. Typiske kandidater til lave værdier er SSH, overvågning, reverse-proxy-frontends og følsomme databasecontrollere. Baggrundsjob, rapporteringsprogrammer eller kortvarige arbejdsprocesser får snarere en højere justering, så brugergrænsefladen fortsat reagerer, når det bliver trangt.

At fastlægge prioriteter inden for hosting

I produktive opsætninger definerer jeg klare Prioriteringer mellem frontend, API, database og batch-behandling. Først fastlægger jeg, hvilke tjenester der skal køre set fra brugerens synspunkt, og giver dem en passende OOM-justering. I systemd indstiller jeg til dette formål OOMScoreAdjust= i service-unit-filen og dokumenterer formålet med hver værdi. Hvis man alligevel administrerer tjenester via systemd, kan man strømline processerne; en introduktion hertil findes i systemd i hosting. På den måde forebygger jeg nedbrud i stedet for at overlade dem til tilfældighederne og sikrer, at brugeroplevelsen forbliver stabil online.

Cgroups, containere og begrænsninger

Jeg glemmer aldrig den cgroups, for containere og tjenester eksisterer i deres egne ressourceverdener. En proces med en moderat OOM-score kan alligevel gå ned, hvis dens cgroup har en stram hukommelsesgrænse, og den kortvarigt overskrider den. Derfor kontrollerer jeg grænserne i cgroup v2 og tilpasser hårde og bløde grænser efter belastningsprofilerne. Hvis du driver multi-tenancy eller shared hosting, har du fordel af korrekt indstillede kvoter og accounting; mere baggrundsinformation findes cgroup v2 i hosting. Når samspillet fungerer, virker OOM-justering og grænseværdier som et velafstemt par justeringsskruer.

Diagnose og overvågning ved OOM-hændelser

Når det går løs, har jeg brug for klare Signaler og gentagelighed i analysen. Jeg gennemgår dmesg, journald og /var/log/kern.log, gemmer OOM-linjerne og henter offerets PID sammen med oom_score og oom_score_adj. Til rutinemæssige kontroller bruger jeg scripts, der viser de største hukommelsesforbrugere og udløser advarselsgrænser. Hvis man ønsker at gå mere i dybden, finder man en struktureret tilgang i Analyse af OOM-Killer. I faste opsætninger integrerer jeg nøgletal som RSS, cache, swap-in/out og containergrænser i overvågningen, så jeg kan opdage tendenser i god tid.

Oversigt i tabelform til administratorer

Jeg bruger følgende oversigt som en kortfattet Guide, når jeg prioriterer roller og dokumenterer justeringer. Kolonnen „Begrundelse“ viser, hvorfor en rolle tildeles beskyttelse eller offervilje. Jeg tilpasser tallene til det enkelte projekt, men retningslinjerne hjælper med at træffe hurtige beslutninger. Den, der bruger tabellen som udgangspunkt, får større klarhed i efteranalyser og ændringsforslag. Det vigtige er, at jeg altid holder en buffer i det samlede system, så drastiske nedskæringer sjældent bliver nødvendige.

Komponent Typisk mål Eksempel: oom_score_adj Årsag
SSH-Daemon Skytter -500 til -900 Sikre adgang til indgreb, selv i tilfælde af pladsmangel.
Reverse proxy (nginx/HAProxy) Skytter -300 til -700 Håndtere indgående trafik, vise fejlsider.
DB-Controller/primærinstans Skytter -200 til -600 Oprethold forbindelser, sikr adgang til data.
PHP-FPM/applikations-workere Neutral til villig til at ofre sig 0 til +300 Der kan være mange parallelle arbejdsprocesser.
Batch/Sikkerhedskopiering/Rapporter Ofretsvillig +300 til +800 Kan udskydes uden at påvirke brugerne.
Indekserer/kø-forbruger Ofretsvillig +200 til +600 Kan afbrydes kortvarigt og indhentes senere.

Sådan begrænser du WordPress- og PHP-workere korrekt

Når jeg bruger WordPress, lægger jeg mærke til Arbejder-antal, memory_limit og store operationer som billedbehandling eller import. Jeg konfigurerer PHP-FPM, så antallet af aktive processer passer til RAM-kapaciteten og ikke skaber overbelastning. I databasen lægger jeg buffer- og cache-størrelser sammen og sørger for at have lidt spillerum, så spidsbelastninger ikke blokerer det hele. Jeg overvåger OpCache, objektcachen og billedoptimeringsværktøjet, da de hurtigt kan få hukommelsesforbruget til at stige. På den måde sikrer jeg, at korte belastningsspidser ikke straks går ud over de vigtige frontend-processer.

Praksis: Retningslinjer og vejledninger

Jeg beholder min Politikker Kortfattet og praktisk, så teamet ikke tøver i en nødsituation. Det omfatter: at definere beskyttelseskandidater, udpege offerroller, tilføje OOMScoreAdjust= til systemd-enheder og dokumentere værdierne i repoen. Jeg tester effekten med værktøjer og testbelastning, indtil rækkefølgen af de systemer, der skal ofres, passer til målene. Derefter skriver jeg en playbook, der beskriver logfiler, alarmering og førstehjælpsforanstaltninger. På den måde forbliver reaktionen konsistent, selv når nye kolleger overtager opgaven.

# Eksempel på et fragment til en systemd-unit
[Service]
OOMScoreAdjust=-400
# Genindlæsning og genstart:
# systemctl daemon-reload && systemctl restart nginx

# Løbende kontrol:
cat /proc/$(pidof nginx)/oom_score
cat /proc/$(pidof nginx)/oom_score_adj

# Midlertidig forhøjelse/sænkning (root):
echo 300 | sudo tee /proc//oom_score_adj

Hyppige fejl og modforanstaltninger

Mange problemer opstår, fordi Grænser passer ikke sammen: for mange PHP-workere, for store DB-cacher og ingen plads til spidsbelastning. Så griber OOM-killeren regelmæssigt ind, selvom det ville være nok med nogle få justeringer. Jeg justerer først antallet af arbejdsprocesser, måler effekten og øger kun RAM-kapaciteten, hvis behovet tydeligt viser sig. Det skader også at indstille mange processer til -1000, da kernen har brug for handlingsfrihed. Jeg prioriterer med sund fornuft, så systemet kan reagere ordentligt i en nødsituation.

Overcommit, swap og hukommelsesniveauer

Jeg præsenterer min Overcommit-strategi indstilles bevidst, da den afgør, hvor hurtigt et system havner i OOM-zonen. Med vm.overcommit_memory=0 (heuristik) kører jeg ofte stabilt, fordi kernen vurderer commit-grænsen ud fra udnyttelse og historik. Det bliver strengere med vm.overcommit_memory=2 plus vm.overcommit_ratio, som definerer den maksimalt tilladte virtuelle allokering. Hvis man generelt indstiller vm.overcommit_memory=1, risikerer man, at hukommelsesreservationer lykkes, men senere mislykkes fuldstændigt ved allokering – en hyppig årsag til OOM-hændelser under belastning.

Jeg kalibrerer Bytte således at den fungerer som buffer, men ikke bliver en »latency-killer«. En moderat vm.swappiness holder ren RAM fri til de mest benyttede stier, mens sjældent anvendte sider flyttes ud. Zswap eller zram kan jeg bruge som en elastisk buffer, når I/O er langsom – det mindsker risikoen for OOM, men koster CPU-kraft. Vandstandene er også vigtige: vm.min_free_kbytes skal være tilstrækkeligt høj, så kernen kan genvinde plads i tide. Hvis man sætter værdierne for lavt, tvinger man systemet til hektisk genvinding og fremprovokerer sti-logikker, der munder ud i OOM.

# Eksempel: konservativ overcommit og moderat swapping
sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=90
sysctl -w vm.swappiness=30
# Indtast permanent i /etc/sysctl.d/ til testformål

Systemd-indstillinger ud over OOMScoreAdjust

Ud over OOMScoreAdjust bruger jeg systemd til at Lagringsskinner at indstille direkte på tjenesten. Med MemoryMax= sætter jeg en hård grænse (cgroup memory.max), MemoryHigh= bremser blidt under belastning, og MemorySwapMax= begrænser udlagring. MemoryLow= og MemoryMin= prioriterer en tjenestes cache-andele under pres, så vigtige processer ikke køler ned så hurtigt. Sammen med OOMPolicy= styrer jeg, hvad systemd foretager sig på unit-niveau ved OOM (f.eks. kun stoppe tjenesten eller afbryde hele afhængigheder). I Slices aggregerer jeg roller – web-frontend, batch, DB – og udleder ensartede regler, så enkelte afvigelser ikke destabiliserer helheden.

Jeg er klar over, at beskyttelse aldrig er absolut: Selv processer med -1000 kan være nødt til at vige i håbløse situationer. Derfor sætter jeg generøse, men realistiske Minima (MemoryLow/Min) kun for ganske få kernetjenester og kontrollerer, om summen af alle tildelinger forbliver under den fysisk tilgængelige hukommelse. På den måde forhindrer jeg, at velmenende beskyttelsesmekanismer forblinder OOM-killeren.

Kubernetes og containerorkestrering

I orkestreringssystemer som Kubernetes spiller OOM-logikken en rolle på flere niveauer. Jeg sætter Forespørgsler og Grænser således at pods falder ind under den ønskede QoS-klasse: »Guaranteed« giver den bedste beskyttelse, »Burstable« udjævner belastningen, mens »BestEffort« er mest udsat. Kubelet tildeler automatisk de deraf følgende OOMScoreAdjust-værdier – jeg planlægger altså ud fra ressourceangivelser i stedet for manuelle tuningværdier i containerne. Hvis en container når sin memory.limit, lukkes den ned inden for sin cgroup, selvom værten stadig har ledig kapacitet; dette er ikke en klassisk host-OOM, men en målrettet selvbeskyttelse af grænsen.

Jeg tager hensyn til indbyggede lagerandele uden for heap-konfigurationerne (f.eks. ved JVM/node), så containere ikke uventet går ned, når de når grænsen. Derudover beregner jeg pod-buffere til at imødegå spidsbelastninger og planlægger kun node-overcommit i moderat omfang, så evictions forekommer sjældent. Når cgroup v2 er aktiv, bruger jeg memory.oom.group målrettet, så en hel procesgruppe i nødstilfælde lukkes ned på en ordnet måde, i stedet for at efterlade enkelte arbejdsprocesser i en zombie-pod. Det holder systemet rent og gør genoprettelsen forudsigelig.

Diagnosedybde: SMaps, PSI og reproducerbare tests

Når jeg skal lave dybdegående analyser, bruger jeg /proc-Indblik og belastningsmålinger. /proc/PID/status viser VmRSS, VmSwap og tråde; /proc/PID/smaps_rollup opsummerer andele som Anon, File og Shmem, uden at jeg går i detaljer. På den måde kan jeg se, om sidecachen er vildledende, eller om de anonyme sider (det reelle arbejdsdatasæt) vokser. Med /proc/pressure/memory måler jeg PSI-signaler, altså hvor meget tid systemet bruger på aktiv reclaim eller stalls. Jeg udløser alarmer for disse værdier, længe før OOM slår til – ideelt til automatisk at iværksætte modforanstaltninger (throttling, skalering, reduktion af workers).

# Relevante øjebliksbilleder
journalctl -k -g "Out of memory|oom-killer"
cat /proc/pressure/memory
grep -E "VmRSS|VmSwap|Threads" /proc//status
cat /proc//smaps_rollup

# Gengivelse af OOM (testmiljø!)
stress-ng --vm 2 --vm-bytes 80% --timeout 30s

Særlige tilfælde: JVM, Node.js og PHP i containere

JVM-Tjenester kræver opmærksomhed, fordi der ud over heap’en også skal tages højde for metaspace, trådstakke, direkte buffere og den native allokatoradfærd. Jeg styrer systemet på en containervenlig måde med MaxRAMPercentage og indstiller en heap, der giver plads til disse komponenter. Ved høj parallelitet begrænser jeg trådpuljer, da mange små stakke tilsammen udgør en betydelig belastning. For Node.js tilpasser jeg –max-old-space-size til containergrænsen for at undgå, at processer lukkes ned tvungent. Og ved PHP-FPM Jeg beregner pm.max_children ud fra RAM, det gennemsnitlige forbrug pr. anmodning og memory_limit – plus en reserve til cacher og webserveren. På den måde forhindrer jeg snigende overbelastninger, der først bliver synlige ved spidsbelastninger.

Jeg beholder Allokeringsstrategi I fokus: glibc med mange arener kan fragmentere hukommelsen og øge forbruget i arbejdsbelastninger med mange tråde. For visse tjenester leverer jemalloc eller tcmalloc mere konsistente spidsbelastninger; jeg tester dette målrettet, dokumenterer effekten og implementerer det på en kontrolleret måde. Derudover begrænser jeg tmpfs-mapper i containeren, så uploads eller midlertidige filer ikke ubemærket sluger RAM-hukommelsen.

Tmpfs, Huge Pages og sidecache

tmpfs Det overses ofte: Uden en størrelsesbegrænsning vokser det, indtil det udgør en del af RAM’en, og pludselig mangler der plads andre steder. Jeg monterer tmpfs med en bevidst angivelse af »size=«, især når det gælder build- eller upload-stier. Gennemsigtige store sider (THP) Fragmentering og ventetid har indflydelse; til tjenester, hvor ventetiden er afgørende, bruger jeg ofte „madvise“, så kun passende allokeringer drager fordel heraf. KSM kan deduplicere og spare plads, men belaster CPU’en – det er nyttigt på udviklingsservere, men i ydeevne-scenarier tjekker jeg effekten og overheadet.

Der Sidecache er ikke „spildt“ hukommelse; den fremskynder I/O. Hvis jeg for aggressivt fortrænger den eller bruger drop-caches som en permanent foranstaltning, flytter jeg omkostningerne over til latenstops. Det er bedre at definere hukommelsesmål pr. rolle og via cgroup-mekanismer (memory.high / memory.max) sikre en retfærdig fordeling. På den måde forbliver hotsets fra de vigtige tjenester i RAM'en, og OOM-situationer forekommer sjældnere.

Opsummering til hverdagen

Jeg bruger OOM-score som et barometer for risiko og justerer med oom_score_adj den rigtige rækkefølge for, hvilke processer der skal ofres. Jeg beskytter tjenester, der har betydning for brugerne, gør flytbare opgaver klar til at blive ofret, og jeg dokumenterer hver værdi på en gennemsigtig måde. Jeg planlægger cgroup-grænser, antal arbejdsprocesser og cache-størrelser som en helhed, så spidsbelastninger ikke udvikler sig til en omfattende krise. Logs, overvågning og en kort playbook sikrer, at jeg hurtigt opdager OOM-hændelser og løser dem målrettet. Med denne disciplin forbliver værten pålidelig, og jeg undgår ubehagelige overraskelser i produktive nætter.

Aktuelle artikler