Zusammenfassung

In diesem Artikel wird erklärt, wie man mit der cpuset-Option in Docker Containern CPU-Ressourcen gezielt zuweisen kann. Wir beleuchten die Vorteile von CPU-Pinning, insbesondere bei CPU-intensiven Anwendungen wie LLMs, und zeigen, wie man mit docker run und docker-compose.yml CPU-Set-Constraints definiert. Zudem wird erläutert, wie man auf physikalische und logische CPU-Kerne zugreift, um gezielte Zuweisungen zu ermöglichen.

Einleitung

Bei modernen Servern mit heterogenen CPU-Architekturen, wie z. B. Intel Xeon oder AMD EPYC Prozessoren, ist es oft sinnvoll, Container gezielt auf bestimmte CPU-Kerne zu pinnen. Dies geschieht nicht nur aus Performance-Gründen, sondern auch, um Ressourcenkonflikte zu vermeiden, die bei parallelen Ausführungen auftreten können.

Die cpuset-Option in Docker ermöglicht genau das: Sie erlaubt es, festzulegen, welche physikalischen CPU-Kerne ein Container verwenden darf. Das ist besonders nützlich bei:

  • CPU-intensiven Anwendungen wie LLMs (z. B. Ollama)
  • Heterogenen Architekturen, bei denen man z. B. eine CPU-Gruppe für Systemprozesse und eine andere für Container reservieren möchte
  • Hyperthreading-Optimierung, um z. B. nur physikalische Kerne zu nutzen

Hintergrund

Was ist ein cpuset?

Ein cpuset ist ein Linux-Konzept zur Ressourcenverwaltung, das es ermöglicht, einem Prozess oder einem Prozessgruppe eine Menge von CPU-Kernen zuzuweisen. In Docker wird dies über die --cpuset-cpus-Option realisiert.

Warum ist CPU-Pinning sinnvoll?

  1. Performance: Durch die Reduktion von Context Switches und die Minimierung von Cache-Misses kann man die Performance verbessern.
  2. Isolation: Bei gleichzeitig laufenden CPU-intensiven Prozessen (z. B. LLM-Server, Datenbanken) vermeidet man Ressourcenkonflikte.
  3. Hyperthreading-Optimierung: Bei CPUs mit Hyperthreading (z. B. 2 Kerne pro physikalischem Kern) kann man gezielt nur die physischen Kerne verwenden, um die tatsächlich vorhandenen Recheneinheiten effizienter zu nutzen.

Praktische Umsetzung

Wie finde ich heraus, welche CPUs verfügbar sind?

Zunächst prüfen wir, wie viele logische CPU-Kerne unser System besitzt. Um die physikalischen Kerne zu identifizieren, verwenden wir:

$ lscpu
Architecture:                x86_64
  CPU op-mode(s):            32-bit, 64-bit
  Address sizes:             48 bits physical, 48 bits virtual
  Byte Order:                Little Endian
CPU(s):                      32
  On-line CPU(s) list:       0-31
Vendor ID:                   AuthenticAMD
  Model name:                AMD RYZEN AI MAX+ 395 w/ Radeon 8060S
    CPU family:              26
    Model:                   112
    Thread(s) per core:      2
    Core(s) per socket:      16
    Socket(s):               1

Das bedeutet:

  • Es gibt einen Socket (physikalische CPUs)
  • Jeder Socket hat 16 physikalische Kerne
  • Jeder physikalische Kern unterstützt zwei Threads / zwei logische Kerne
  • Insgesamt 32 logische Kerne (inkl. Hyperthreading)

Wie erkenne ich physikalische vs. logische Kerne?

Die Ausgabe von lscpu zeigt auch an, wie viele Kerne pro Socket es gibt. Um die physikalischen Kerne zu identifizieren, können wir folgende Befehle verwenden:

$ lscpu --parse=CPU,CORE,SOCKET
# The following is the parsable format, which can be fed to other
# programs. Each different item in every column has an unique ID
# starting usually from zero.
# CPU,Core,Socket
0,0,0
1,1,0
2,2,0
...
15,15,0
16,0,0
...
30,14,0
31,15,0

Daraus ergibt sich:

  • Logische CPUs 0–15: Socket 0, separate physikalische Kerne 0-15
  • Logische CPUs 16–31: Socket 0, hyperthreading Kerne auf den physikalischen Kernen 0-15

Wenn man also z. B. nur die physikalischen Kerne von Socket 0 verwenden möchte, wählt man Kerne 0–15

Wie pinne ich einen Container mit docker run?

Angenommen, wir wollen einen Container nur auf physikalische Kerne 0–7 (Socket 0) laufen lassen:

$ docker run --cpuset-cpus="0-7" your-image

Alternativ kann man auch einzelne Kerne angeben:

$ docker run --cpuset-cpus="0,2,4,6" your-image

Wie setze ich cpuset in docker-compose.yml?

In einer docker-compose.yml-Datei kann man das folgendermaßen angeben:

services:
  ollama:
    image: ollama/ollama
    cpuset_cpus: "0-7"
    ports:
      - "11434:11434"

Fazit

Die Verwendung von cpuset in Docker ist ein mächtiges Werkzeug, um die Performance und Isolation von Containern zu verbessern, besonders bei CPU-intensiven Anwendungen wie LLMs. Durch gezielte Zuweisung auf physikalische Kerne kann man:

  • Ressourcenkonflikte reduzieren
  • Cache-Misses minimieren
  • Hyperthreading gezielt steuern

Referenzen

Kaya Kupferschmidt

Author Kaya Kupferschmidt

Kaya Kupferschmidt ist ein erfahrener freiberuflicher Data Architect, Data Engineer und Data Scientist. Seit 2005 beschäftigt er sich mit Daten und hat einen Doktortitel in Mathematik. Seine Expertise liegt in der Entwicklung und Implementierung robuster Datenlösungen, wobei er sich besonders für Big Data, Machine Learning und KI begeistert. Kaya verfügt über breites technologisches Wissen und setzt dabei bevorzugt auf Open-Source-Technologien.

Mehr Artikel von Kaya Kupferschmidt

Hinterlasse einen Kommentar