CPU-Pinning mit taskset und cpuset fuer Worker-Isolation
AI generated
$
/etc
Linux · CPU-Pinning · cpuset · Worker-Isolation
CPU-Pinning mit taskset und cpuset
Wie PHP-FPM-Worker und Batch-Jobs sich nicht mehr um Kerne streiten

nice und ionice senken nur die Prioritaet von Hintergrundjobs, verhindern aber nicht, dass sie kurzzeitig ganze Kerne belegen. CPU-Pinning geht einen Schritt weiter: Es reserviert bestimmte Kerne exklusiv fuer PHP-FPM-Worker, waehrend Reindexierung und Cron ausschliesslich auf anderen Kernen laufen duerfen.

17 Min. Lesezeit taskset · cgroups cpuset · systemd AllowedCPUs Linux · Multi-Core-Server · Magento

1. Warum CPU-Pinning ueber nice hinausgeht

nice und ionice senken die Prioritaet von Hintergrundjobs, garantieren aber keine feste Trennung: Sobald ein Kern frei ist, darf auch ein niedrig priorisierter Prozess ihn kurzzeitig vollstaendig belegen. Fuer viele Szenarien reicht das aus, aber bei sehr grossen Reindex-Jobs oder rechenintensiven Batch-Verarbeitungen kann selbst diese kurzzeitige Vollauslastung eines Kerns spuerbare Latenzspitzen fuer gleichzeitig laufende PHP-FPM-Worker verursachen, wenn der Scheduler den Worker-Prozess zwischenzeitlich auf genau diesen Kern verschiebt.

CPU-Pinning loest dieses Problem strukturell: Statt nur die Prioritaet zu senken, wird einem Prozess oder einer Prozessgruppe eine feste Teilmenge der verfuegbaren CPU-Kerne zugewiesen, auf die er beschraenkt bleibt. PHP-FPM-Worker laufen dann ausschliesslich auf einer reservierten Kerngruppe, waehrend Reindexierung, Backups und andere Batch-Jobs eine vollstaendig getrennte Kerngruppe nutzen. Beide Workloads koennen dadurch gleichzeitig auf Volllast laufen, ohne sich gegenseitig zu beeintraechtigen.

Dieser Artikel zeigt, wie CPU-Pinning mit taskset fuer einzelne Prozesse und mit cgroups cpuset fuer dauerhafte Isolation umgesetzt wird, und wie sich diese Isolation sinnvoll mit der NUMA-Topologie eines Servers kombinieren laesst.

2. Wie der Scheduler Prozesse ohne Pinning verteilt

Ohne explizite Konfiguration entscheidet der Completely Fair Scheduler (CFS) fortlaufend, auf welchem Kern ein Prozess als naechstes ausgefuehrt wird, basierend auf aktueller Auslastung und Cache-Lokalitaet. Diese Flexibilitaet ist fuer die meisten Workloads sinnvoll, weil sie automatisch fuer eine ausgeglichene Kernauslastung sorgt, ohne dass ein Administrator manuell eingreifen muss.

Fuer stark unterschiedliche Workload-Typen auf demselben Server ist genau diese Flexibilitaet aber manchmal nachteilig: Ein rechenintensiver Batch-Job und ein latenzkritischer PHP-FPM-Worker koennen wiederholt auf denselben Kern eingeplant werden, was zu Cache-Verdraengung fuehrt, dem sogenannten Cache Thrashing, bei dem staendig wechselnde Prozesse denselben CPU-Cache mit unterschiedlichen Daten ueberschreiben. CPU-Pinning verhindert dieses Wechselspiel, indem es dem Scheduler von vornherein nur eine Teilmenge der Kerne fuer jeden Workload-Typ zur Auswahl gibt.

3. taskset: CPU-Affinitaet fuer einzelne Prozesse setzen

Das Kommando taskset setzt oder liest die CPU-Affinitaetsmaske eines Prozesses, also die Menge der Kerne, auf denen der Prozess ausgefuehrt werden darf. Ein neuer Prozess kann direkt mit einer Kernmaske gestartet werden, ein bereits laufender Prozess laesst sich per PID nachtraeglich anpassen, ohne ihn neu starten zu muessen. Diese Form von CPU-Pinning ist ideal fuer schnelle, gezielte Tests, etwa um zu pruefen, ob ein bestimmter Batch-Job tatsaechlich von einer Kernbeschraenkung profitiert, bevor eine dauerhafte Loesung eingerichtet wird.

Fuer produktive Konfigurationen ist taskset allerdings nur ein erster Schritt, weil die Bindung nicht automatisch fuer neu gestartete Kindprozesse gilt und bei jedem Neustart des Zielprozesses manuell wiederholt werden muss. Fuer dauerhafte, vererbbare Isolation eignen sich cgroups cpuset deutlich besser.


# Start a new process pinned to cores 4-7 only
taskset -c 4-7 php bin/magento indexer:reindex

# Pin an already-running process by PID to cores 0-3
taskset -cp 0-3 24817

# Check the current CPU affinity of a running process
taskset -cp 24817

4. cgroups cpuset: Kerngruppen dauerhaft reservieren

Die cgroups-cpuset-Controller erlauben, eine ganze Prozessgruppe dauerhaft auf eine feste Kernmenge zu beschraenken, inklusive aller Kindprozesse, die spaeter innerhalb dieser Gruppe gestartet werden. Das macht cpuset zur robusteren Wahl fuer CPU-Pinning auf produktiven Servern: Eine cgroup fuer PHP-FPM-Worker mit den Kernen 0 bis 7, eine getrennte cgroup fuer Batch-Jobs mit den Kernen 8 bis 15, und beide Gruppen bleiben unabhaengig von Neustarts einzelner Prozesse bestehen.

Auf modernen Distributionen mit cgroups v2 lassen sich diese Kerngruppen direkt ueber das Dateisystem unter /sys/fs/cgroup/ verwalten, ueber die Datei cpuset.cpus pro Gruppe. Zusaetzlich steuert cpuset.mems analog, welche NUMA-Speicher-Nodes einer Gruppe zur Verfuegung stehen, was CPU-Pinning und NUMA-Bindung in einer einzigen Konfiguration zusammenfuehrt.


#!/usr/bin/env bash
# setup-cpusets.sh — create dedicated cgroups for web vs. batch workloads
set -euo pipefail

mkdir -p /sys/fs/cgroup/web-workers
mkdir -p /sys/fs/cgroup/batch-jobs

# Web workers get cores 0-7, memory from NUMA node 0
echo "0-7"   > /sys/fs/cgroup/web-workers/cpuset.cpus
echo "0"     > /sys/fs/cgroup/web-workers/cpuset.mems

# Batch jobs get cores 8-15, memory from NUMA node 1
echo "8-15"  > /sys/fs/cgroup/batch-jobs/cpuset.cpus
echo "1"     > /sys/fs/cgroup/batch-jobs/cpuset.mems

5. PHP-FPM-Pools auf dedizierte Kerne beschraenken

Fuer PHP-FPM selbst laesst sich CPU-Pinning ueber den Systemd-Service-Wrapper oder direkt beim Start des Master-Prozesses konfigurieren, sodass alle von ihm erzeugten Worker-Kindprozesse die Bindung automatisch erben. Wichtig dabei: Die Bindung sollte auf den Master-Prozess angewendet werden, nicht individuell auf jeden einzelnen Worker, weil PHP-FPM Worker dynamisch startet und beendet, abhaengig vom konfigurierten Process Manager Modus.

Bei mehreren PHP-FPM-Pools fuer unterschiedliche Magento-Websites auf demselben Server kann CPU-Pinning zusaetzlich genutzt werden, um kritische Pools von weniger kritischen zu trennen, etwa den Pool fuer den Haupt-Shop auf staerker priorisierten Kernen, waehrend ein selten genutzter B2B-Pool auf einer kleineren Kernmenge laeuft.


# /etc/systemd/system/php8.4-fpm.service.d/cpuset.conf
[Service]
# Restrict the PHP-FPM master and all its worker children to cores 0-7
AllowedCPUs=0-7

6. AllowedCPUs in systemd-Units konfigurieren

Seit systemd Version 244 steht mit AllowedCPUs= eine native Direktive zur Verfuegung, die direkt eine cgroups-cpuset-Beschraenkung fuer eine Unit setzt, ohne die manuelle Verwaltung von Dateien unter /sys/fs/cgroup/ zu erfordern. Diese Direktive laesst sich auf jede beliebige Service-, Slice- oder Scope-Unit anwenden und vererbt sich automatisch an alle Prozesse, die diese Unit spaeter erzeugt.

Fuer eine saubere, systemweite Trennung empfiehlt sich, eine dedizierte systemd-Slice fuer Web-Workloads und eine weitere fuer Batch-Jobs anzulegen, jeweils mit eigenem AllowedCPUs=-Wert, und alle relevanten Dienste dieser Slice zuzuordnen. Das zentralisiert die CPU-Pinning-Konfiguration an einer Stelle, statt sie ueber viele einzelne Unit-Dateien zu verstreuen.


# /etc/systemd/system/web-workers.slice
[Slice]
AllowedCPUs=0-7

# /etc/systemd/system/batch-jobs.slice
[Slice]
AllowedCPUs=8-15

# Assign a service to a slice:
# systemctl set-property php8.4-fpm.service Slice=web-workers.slice

7. CPU-Pinning und NUMA-Topologie gemeinsam planen

CPU-Pinning ohne Ruecksicht auf die NUMA-Topologie eines Servers kann selbst neue Probleme schaffen: Werden PHP-FPM-Worker auf Kerne von Node 0 gepinnt, waehrend der von ihnen genutzte Speicher, etwa OPcache-Shared-Memory, auf Node 1 alloziert ist, entstehen zusaetzliche entfernte Speicherzugriffe, die die Isolation teilweise wieder zunichtemachen. Die Kerngruppen fuer CPU-Pinning sollten deshalb immer entlang der tatsaechlichen NUMA-Node-Grenzen gezogen werden, ermittelt ueber numactl --hardware.

Fuer einen Zwei-Node-Server bedeutet das in der Praxis: PHP-FPM-Worker auf die Kerne und den Speicher von Node 0 pinnen, Batch-Jobs auf Kerne und Speicher von Node 1. Diese Kombination aus CPU-Pinning und NUMA-Bindung liefert die staerkste Isolation, weil weder CPU-Zeit noch Speicherbandbreite zwischen den beiden Workload-Typen geteilt werden muss.

8. Typische Fehler beim CPU-Pinning

Der haeufigste Fehler ist, zu wenige Kerne fuer die Web-Worker-Gruppe zu reservieren, in der Annahme, dass mehr Isolation immer besser ist. Wird die reservierte Kernmenge zu knapp bemessen, kann PHP-FPM bei Lastspitzen nicht mehr auf ungenutzte Kerne der Batch-Gruppe ausweichen, obwohl diese gerade frei waeren, was die Gesamt-Kapazitaet des Servers kuenstlich einschraenkt. Die Kernaufteilung sollte deshalb immer anhand realistischer Lastmessungen erfolgen, nicht nach einer pauschalen Faustregel.

Ein zweiter Fehler ist, CPU-Pinning einzurichten, ohne zu pruefen, ob der Hypervisor auf virtualisierten Servern die Konfiguration ueberhaupt respektiert. Auf stark ueberbuchten Cloud-Instanzen mit geteilten vCPUs kann eine im Gastsystem gesetzte cpuset-Beschraenkung wirkungslos bleiben, weil der Host die tatsaechliche Kernzuteilung ohnehin unabhaengig steuert.

9. Isolationsstrategien im Vergleich

Die folgende Tabelle vergleicht die verschiedenen Ansaetze fuer CPU-Pinning und Isolation auf einem Magento-Server.

Ansatz Persistenz Isolationsgrad
nice / ionice Pro Prozessstart Niedrig, nur relative Prioritaet
taskset Manuell nach jedem Neustart Mittel, harte Kernbeschraenkung
cgroups cpuset (manuell) Dauerhaft, vererbbar Hoch, inklusive Kindprozesse
systemd AllowedCPUs / Slice Dauerhaft, zentral verwaltet Hoch, systemweit konsistent

Fuer produktive Magento-Server ist die Kombination aus systemd-Slices mit AllowedCPUs= und NUMA-bewusster Kernaufteilung die robusteste Loesung. taskset bleibt sinnvoll fuer schnelle Tests, sollte aber nicht die einzige Massnahme fuer eine dauerhafte Produktionskonfiguration sein.

Mironsoft

Worker-Isolation und CPU-Pinning fuer Magento-Server

Konkurrieren Batch-Jobs und PHP-FPM um dieselben Kerne?

Wir richten NUMA-bewusstes CPU-Pinning ueber systemd-Slices ein, trennen Web-Worker von Batch-Jobs und validieren die Isolation unter realer Last.

Kernaufteilung

Web- und Batch-Kerngruppen anhand realer Lastmessungen planen

systemd-Slices

AllowedCPUs zentral und dauerhaft konfigurieren

NUMA-Abstimmung

CPU- und Speicherbindung entlang der Node-Grenzen ausrichten

10. Zusammenfassung

CPU-Pinning geht ueber die relative Priorisierung von nice und ionice hinaus und schafft eine harte, strukturelle Trennung zwischen Workload-Typen. taskset eignet sich fuer schnelle, gezielte Tests einzelner Prozesse, waehrt aber keine Persistenz ueber Neustarts hinweg. cgroups cpuset und die systemd-Direktive AllowedCPUs= bieten dauerhafte, vererbbare Isolation, die automatisch fuer alle Kindprozesse einer Gruppe gilt.

Die groesste Wirkung entfaltet CPU-Pinning, wenn es gemeinsam mit der NUMA-Topologie eines Servers geplant wird: Kerngruppen sollten entlang der NUMA-Node-Grenzen gezogen werden, damit weder CPU-Zeit noch Speicherbandbreite zwischen isolierten Workloads geteilt werden muss. Fuer produktive Magento-Server ist die Kombination aus systemd-Slices und NUMA-bewusster Kernaufteilung die robusteste und wartungsaermste Loesung.

CPU-Pinning mit taskset und cpuset — Das Wichtigste auf einen Blick

Schnelltest

taskset -c fuer manuelle, temporaere Kernbeschraenkung einzelner Prozesse.

Dauerhafte Isolation

cgroups cpuset.cpus oder systemd AllowedCPUs= pro Slice.

NUMA beachten

Kerngruppen entlang der Node-Grenzen aus numactl --hardware ziehen.

Kapazitaet pruefen

Kernaufteilung anhand realer Lastmessungen, nicht nach Faustregel.

11. FAQ: CPU-Pinning mit taskset und cpuset

1Unterschied zwischen CPU-Pinning und nice?
nice ist relative Prioritaet, CPU-Pinning ist harte Kernbeschraenkung.
2taskset oder cpuset verwenden?
taskset fuer Tests, cpuset oder AllowedCPUs fuer dauerhafte Produktion.
3Erben Kindprozesse die Bindung?
Bei cgroups und systemd ja, bei taskset nur unter bestimmten Bedingungen.
4CPU-Pinning fuer PHP-FPM einrichten?
Ueber AllowedCPUs= in einer systemd-Override-Datei fuer den Service.
5Was macht cpuset.mems?
Legt erlaubte NUMA-Speicher-Nodes fuer eine cgroup fest.
6Warum NUMA beachten?
Sonst entstehen zusaetzliche entfernte Speicherzugriffe trotz Kernisolation.
7Wie viele Kerne fuer Web-Worker?
Anhand realer Lastmessungen, nicht nach Faustregel bestimmen.
8Funktioniert es auf jeder Cloud-Instanz?
Nicht immer, ueberbuchte geteilte vCPUs koennen die Wirkung aufheben.
9Was ist eine systemd-Slice?
Eine cgroups-Gruppierung mehrerer Units mit gemeinsamen Ressourcenlimits.
10Ersetzt es nice und ionice?
Nein, beide Mechanismen ergaenzen sich gegenseitig.