...

Systemd Resource Control: Linux-services gericht beperken

Met systemd resource regel ik gericht de CPU, het RAM, de I/O en de PID’s voor Linux-services, waardoor productieve services voorspelbaar blijven. De volgende stappen laten in de praktijk zien hoe ik limieten instel in units en slices, voortbouw op cgroups v2 en conflicten tussen resources met duidelijke regels oplos; zo blijft elke Instantie voorspelbaar.

Centrale punten

Het volgende overzicht bevat de belangrijkste punten die ik in het artikel uitgebreid toelicht; het dient als een snel Gids.

  • cgroepen v2 als een uniforme hiërarchie met systemd als centrale beheerder
  • Soorten eenheden Service, Scope en Slice doelgericht combineren
  • CPUQuota en CPWicht voor een eerlijke verdeling van de CPU-capaciteit
  • MemoryMax en GeheugenHoog tegen OOM en snelheidsbeperking
  • Plakjes voor groepslimieten en prioriteiten bij de serverwerking

Waarom systemd en cgroups v2 samenwerken

Ik organiseer alle processen in cgroups v2 en gebruik systemd als Controlecentrum. De uniforme hiërarchie onder /sys/fs/cgroup groepeert controllers zoals cpu, memory, io en pids overzichtelijk. Elke unit krijgt zijn eigen cgroup, waardoor ik limieten consistent kan toepassen op een hele familie van services. Deze structuur voorkomt dat individuele PID’s limieten omzeilen, omdat de groep als geheel telt. Sinds systemd-versie 232 beheert systemd de hiërarchie exclusief en schrijft het limieten naar de kernel-interfaces; delegatie sta ik alleen bewust toe, zodat niets aan de controles voorbijgaat. Zo houd ik mijn Bronnen te allen tijde beheersbaar.

Unit-types begrijpen: Service, Scope, Slice

Ik verpak klassieke Daemons in Service-Units en groepeer extern gestarte processen in scopes. Voor de hiërarchie maak ik slices aan, die als interne knooppunten resources voor hele groepen definiëren. Services en scopes vormen de bladeren die de limieten van de betreffende slice overnemen. Zo verdeel ik CPU-, geheugen- en I/O-budgetten over de hele boomstructuur, in plaats van elke service afzonderlijk te bekijken. Voor beginners is het aan te raden om eens te kijken naar Hostingdiensten efficiënt beheren, om de rol van units in het serverbeheer te begrijpen en zelf Plakjes te plannen.

Controleer de vereisten: uniforme hiërarchie en controller

Ik zorg ervoor dat het systeem in de unified mode draait en dat alle benodigde controllers actief zijn. Dat kan ik zien aan /sys/fs/cgroup (een koppelpunt) en aan het feit dat systemd de boomstructuur beheert. Als er controllers ontbreken (bijv. io), controleer ik de kernelconfiguratie en, indien nodig, de opstartparameters. Vooral in oudere omgevingen migreer ik bewust van v1 naar v2, zodat de beschreven richtlijnen zoals IOWeight, MemoryHigh of AllowedCPUs effect hebben. Pas wanneer accounting en controllers werken, is het de moeite waard om de gewichten en quota nauwkeurig af te stemmen.

CPU-beheer: CPUWeight en CPUQuota op de juiste manier gebruiken

Ik beheer CPU-aandelen via CPUQuota en relatieve prioriteiten via CPUWeight. Een quotum van 50% beperkt de dienst tot de helft van de kerntijd, terwijl een gewicht van 200 voorrang geeft ten opzichte van diensten met lagere gewichten. Zo regel ik taken die continu draaien, zonder interactieve diensten te vertragen. In de praktijk begin ik met gematigde quota, houd ik de latenties in de gaten en verhoog ik het gewicht van belangrijke diensten. Zo verdeel ik de rekenkracht op basis van belangrijkheid in plaats van willekeurige verdeling.

CPU-affiniteit, AllowedCPUs en quotumperiodes

Als ik cores vast wil toewijzen, gebruik ik CPUAffinity of de fijnere cpuset-regeling via AllowedCPUs. Zo scheid ik bijvoorbeeld batch-workloads van latentiegevoelige diensten op aparte cores. Voor pieken pas ik CPUQuotaPeriodSec aan: een langere periode maakt grotere, kortstondige uitschieters binnen hetzelfde gemiddelde quotum mogelijk, wat bij diensten met pieken de P99-latentie verbetert.

[Service]
# Kernselectie (sched_affinity) versus cpuset (cgroup v2)
CPUAffinity=0 1 2 3
AllowedCPUs=0-3

# 150% Totale tijd bij een periode van 200 ms (meer ruimte voor pieken)
CPUQuota=150%
CPUQuotaPeriodSec=200 ms

# Relatieve weging binnen dezelfde slice
CPUWeight=200

Geheugenlimieten met MemoryMax, MemoryHigh, MemoryLow

Ik stel een strikte limiet in met MemoryMax, om OOM-situaties door uitschieters te voorkomen. Met MemoryHigh beperk ik de geheugentoegang al vóór het bereiken van de maximale grens, wat de algehele stabiliteit verhoogt. MemoryLow en MemoryMin bieden diensten een veilige ruimte, zodat de kernel eerst andere groepen terugwint. Deze gelaagde aanpak voorkomt cascade-effecten wanneer meerdere diensten tegelijkertijd groeien. Wie op zoek is naar achtergrondinformatie over de controller, vindt die bij Uitleg over de geheugencontroller een heldere inleiding tot de bijbehorende Mechanismen.

De swap-strategie en het OOM-gedrag bewust instellen

Ik leg duidelijk vast of en in welke mate een unit gebruik mag maken van swap. Met MemorySwapMax stel ik een bovengrens in voor het gecombineerde gebruik van RAM en swap. Voor latentiegevoelige services beperk ik swap vaak sterk of schakel ik het uit om page-outs te voorkomen. Daarnaast beïnvloed ik met OOMScoreAdjust de kans dat de kernel afzonderlijke processen beëindigt – en met OOMPolicy bepaal ik hoe systemd reageert op een OOM in de unit (bijvoorbeeld de hele unit stoppen of laten doorlopen).

[Service]
# Maximaal 2G incl. swap; harde RAM-limiet blijft MemoryMax
MemoryMax=1.5G
MemorySwapMax=2G

# Prioriteit van de OOM-beslissing (lager = beter beschermd)
OOMScoreAdjust=-500

# Reactie wanneer de OOM-killer binnen de unit in actie komt
OOMPolicy=stop

Met deze combinatie voorkom ik ongecontroleerde swaps, zorg ik voor vastgelegde failover-scenario’s en houd ik databases of in-memory-caches op betrouwbare wijze onder één planbaar dak.

I/O- en proceslimieten: IOWeight, bandbreedtes en TasksMax

Ik beperk de lees- en schrijfsnelheden via IOReadBandwidthMax en IOWriteBandwidthMax, wanneer schijven worden gedeeld. Voor relatieve prioritering gebruik ik IOWeight, zodat centrale workloads voorrang krijgen boven batch-streams. Met TasksMax stel ik een duidelijke bovengrens in voor processen en threads, wat fork-bommen effectief tegengaat. Deze controles stabiliseren omgevingen met meerdere servers, waarin afzonderlijke taken anders de volledige I/O zouden domineren. Vooral bij build-servers zorg ik zo voor reproduceerbare Doorvoer van.

I/O per apparaat gericht regelen

In heterogene opstellingen met NVMe en HDD’s pas ik de instellingen per apparaat aan. Dit voorkomt dat snelle SSD’s worden afgeremd door een luidruchtige buur op de HDD. De combinatie van relatieve gewichten en absolute limieten per apparaat dekt de meeste praktijkgevallen.

[Service]
# Relatief gewicht voor alle apparaten
IOWeight=300

# Gewicht per apparaat (bijv. prioriteit geven aan NVMe)
IODeviceWeight=/dev/nvme0n1 500
IODeviceWeight=/dev/sda 100

# Absolute limiet per apparaat (lees-/schrijfsnelheid)
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M

Belangrijk: IOWeight werkt alleen relatief tussen actieve cgroups; de Max-richtlijnen leggen harde limieten op. Ik begin vaak met gewichten en voeg alleen harde limieten toe wanneer ik „Noisy Neighbors“ zeker moet afschermen.

Configureren: unit-bestanden, drop-ins en set-property

Ik voer limieten rechtstreeks in het Eenheid-bestand of gebruik drop-ins die de originele bestanden ongewijzigd laten. Met `systemctl edit NAME.service` maak ik een fragment aan dat `CPUQuota`, `CPUWeight`, `MemoryMax` en andere richtlijnen toevoegt. Voor snelle tests gebruik ik `systemctl set-property`; systemd schrijft de wijziging netjes naar een drop-in. Na aanpassingen laad ik de daemons opnieuw en controleer ik de status om het effect te verifiëren. Deze werkwijze zorgt ervoor dat updates conflictvrij verlopen en voorziet elke Amendement met een duidelijke geschiedenis.

Drop-in-prioriteiten, voorinstellingen en standaardinstellingen

Ik let op de volgorde van de drop-ins: systemd laadt ze in numerieke volgorde; een 90-override.conf overschrijft bijvoorbeeld eerdere 10-*.conf-bestanden. Aan de voorinstellingen van leveranciers kom ik niet; ik overschrijf ze in /etc, zodat pakketupdates geen problemen opleveren. Systeembrede standaardinstellingen zoals DefaultTasksMax, DefaultCPUAccounting of DefaultMemoryAccounting stel ik bewust in systemd.conf in om uniforme statistieken en veiligheidsmaatregelen te garanderen, ook voor nieuwe units.

# De actieve waarden controleren
systemctl show NAME.service -p CPUQuota -p CPUWeight -p MemoryMax
systemd-analyze dump | grep -E "Default(TasksMax|CPUAccounting|MemoryAccounting)"

# Een permanent override-bestand openen/aanmaken
systemctl edit NAME.service

Slices in de praktijk: groepen op een zinvolle manier beperken

Ik bundel verwante diensten in aparte Plakjes, zoals web.slice, db.slice en batch.slice. In batch.slice sta ik bijvoorbeeld 200% CPU en 4G RAM toe, zodat achtergrondtaken voldoende ruimte krijgen zonder de frontends te verdringen. Services wijs ik via Slice= toe aan hun doelslice; limieten gelden dan voor alle leden gezamenlijk. Deze groepering vereenvoudigt het beleid enorm: een nieuw teamproject neemt automatisch het beleid van zijn slice over. Voor geïsoleerde klant- of app-groepen is het bovendien handig om te kijken naar cgroups-isolatie, om de scheiding netjes te Plan.

Standaardsegmenten: system.slice, user.slice, machine.slice

Ik laat systeemservices in de system.slice en stel daar slechts voorzichtig globale limieten in, zodat essentiële diensten niet zonder middelen komen te zitten. Gebruikersprocessen komen terecht in user.slice, waar ik interactieve sessies beperk zonder shells volledig te blokkeren. Virtualisaties en containers breng ik samen in de machine.slice en stel ik per VM of container duidelijke budgetten vast. Deze standaardstructuur zorgt voor orde en biedt zinvolle aanknopingspunten voor eigen slices. Wie netjes overneemt, bespaart zich veel afzonderlijke regels en houdt de Transparantie hoog.

Delegatie voor containers en dynamische workloads

Wanneer ik subbomen doorgeef aan container-runtimes of door gebruikers aangestuurde tools, stel ik ‘Delegate=yes’ bewust in, en alleen op die plaatsen waar controle nodig is. Zo blijft de zeggenschap bij systemd, terwijl de ontvanger van de delegatie binnen zijn subboom eigen cgroups mag aanmaken. In combinatie met scopes kan ik kortstondige processen (bijv. CI-taken) netjes verzamelen, beperken en weer vrijgeven, zonder de slices te verwateren.

[Service]
# Staat onderbeheer van de cgroup-subtree toe (bijv. door de container-runtime)
Delegate=yes
Slice=machine.slice
MemoryMax=4G
CPUWeight=300

Monitoring en foutopsporing: Status, cgtop, cgls

Ik controleer eerst met systemctl status NAME.service, welke limieten actief zijn en hoe de dienst presteert. Met systemd-cgtop zie ik het CPU- en geheugengebruik per cgroup in realtime. systemd-cgls toont me de boomstructuur en maakt overerving zichtbaar. Bij afwijkingen bekijk ik de bestanden in /sys/fs/cgroup om de ingestelde waarden van de controllers te controleren. Vervolgens pas ik de quota's stapsgewijs aan, houd ik de statistieken in de gaten en documenteer ik elke Amendement.

Monitoring verdiepen: boekhouding, PSI en sneltests

Voor betrouwbare statistieken schakel ik CPUAccounting, MemoryAccounting en IOAccounting in op afzonderlijke eenheden of standaard. Daarnaast houd ik piekbelastingen in de gaten via Pressure-informatie (PSI) in de kernel, om te zien of er meer beperkingen (memory.high) worden toegepast of dat er permanent een tekort aan I/O is. Voor reproduceerbare tests start ik workloads met systemd-run als scope en wijs ik tijdelijk limieten toe, voordat ik ze in een permanente drop-in integreer.

# Tijdelijke scope met I/O- en CPU-gewicht
systemd-run --scope -p IOWeight=400 -p CPUWeight=300 --unit test-batch -- dd if=/dev/zero of=/tmp/out bs=1M count=1024

# Accounting activeren op een bestaande unit
systemctl set-property NAME.service CPUAccounting=yes MemoryAccounting=yes IOAccounting=yes

Problemen oplossen en typische struikelblokken

  • Harsh Caps vs. Burst: een te krappe CPUQuota zonder aangepaste periode leidt tot haperingen. Ik verhoog CPUQuotaPeriodSec of verlaag de quota slechts in beperkte mate en maak meer gebruik van CPUWeight.
  • De geheugensnelkoppeling treedt te vroeg in werking: is MemoryHigh te laag ingesteld? Ik verhoog de waarde of definieer MemoryLow, zodat kritieke paden niet te agressief worden vrijgemaakt.
  • I/O-apparaten zijn verkeerd geadresseerd: IO*-richtlijnen verwachten blokapparaten. Ik controleer het apparaatpad met lsblk en stel regels in per apparaat, niet per koppelpunt.
  • Threads bereiken de limiet: een te lage TasksMax-waarde remt de worker-pools af. Ik bereken de benodigde capaciteit op basis van het piek aantal threads plus een buffer en houd de kolom ‘Tasks’ in de gaten met systemd-cgtop.
  • Drop-ins zonder effect: Na het aanbrengen van wijzigingen voer ik `systemctl daemon-reload` uit en controleer ik met `systemctl show` of de eigenschappen daadwerkelijk zijn ingesteld.

Beste praktijken voor prioriteiten en grenzen

Ik groepeer services op rol, wijs CPUWeight en IOWeight toe op basis van belangrijkheid en stel strikte opslaglimieten in via MemoryMax. Kritieke databases krijgen een hoog gewicht en minder strikte quota, terwijl rapporten en batch-taken strenger worden beperkt. Ik stel TasksMax in wanneer applicaties veel workers gebruiken of wanneer er een risico op thread-explosies bestaat. Elke aanpassing wordt met een versienummer in de repository opgeslagen, zodat ik deze kan traceren en indien nodig kan terugdraaien. In de staging-omgeving stem ik de waarden af op de belastingprofielen en neem ik ze vervolgens op een conservatieve manier over naar de Productie.

Tabel met een overzicht van belangrijke richtlijnen

Deze beknopte tabel geeft een overzicht van de gebruikelijke instellingen en helpt me bij het vinden van de juiste Waarden om te kiezen.

Doel richtlijn Voorbeeldwaarde Effect
CPU-aandeel CPWicht 200 Heeft voorrang boven eenheden met een lager gewicht; verdeeld CPU eerlijk.
CPU-quotum CPUQuota 50% Beperkt de bruikbare kernwerktijd; ideaal voor langdurige belasting Jobs.
Harde schijf MemoryMax 1G Absolute limiet; voorkomt OOM door uitschieters in hetzelfde Slice.
Zachte opslag GeheugenHoog 800 m Remt af voor Max; vermindert de druk op de Systeem.
I/O-prioriteit IOWeight 500 Geeft de voorkeur aan centrale diensten op gedeelde Schijven.
PID's/threads TakenMax 512 Beperkt het aantal processen/threads; biedt bescherming tegen Forks-Lawines.

Toepassingen op het gebied van hosting en serverbeheer

Ik stel voor cliënten eigen Plakjes Ik stel per klant CPU- en RAM-budgetten vast. In microservice-opstellingen krijgen API- en authenticatieservices een hoger gewicht, terwijl rapportage asynchroon werkt. Voor CI/CD-runners maak ik een batch-slice aan, zodat builds nooit frontends verdringen. In container- en VM-omgevingen kapsel ik workloads in de machine.slice in en houd ik de budgetten per klant duidelijk gescheiden. Deze indeling vermindert ‘noisy neighbor’-effecten en zorgt voor reproduceerbare Latencies tijdens piekuren.

Samenvatting

Ik configureer Linux-services met systemd en cgroups v2 Eenheid in plaats van op afzonderlijke processen. CPUQuota, CPUWeight, MemoryMax, MemoryHigh, IOWeight en TasksMax vormen mijn kernset voor een eerlijke verdeling en duidelijke bovengrenzen. Slices zorgen voor orde, bundelen richtlijnen en vergemakkelijken het beheer en de integratie van nieuwe diensten. Monitoring met systemctl status, cgtop en cgls laat al in een vroeg stadium zien waar ik aanpassingen moet doen. Zo blijven prestaties en beschikbaarheid voorspelbaar en houd ik conflicten om resources onder Controle.

Huidige artikelen

Linux-server met gevisualiseerde kengetallen voor drukstagnatie in het datacenter
Administratie

Linux PSI voor nauwkeurige prestatieanalyse en monitoring

Linux PSI (Pressure Stall Information) laat zien in hoeverre de CPU, het geheugen en de I/O je systeem vertragen. Ontdek hoe je PSI kunt activeren en kunt gebruiken voor nauwkeurige prestatiebewaking.