- Selbst wenn man
docker run ubuntuausführt, wird der Linux-Kernel des Hosts gemeinsam genutzt, und Ubuntu liefert nur User-Space-Werkzeuge - Das Ergebnis von
uname -rzeigt die Kernel-Version des Hosts, während nur/etc/os-releaseUbuntu-Informationen anzeigt - VMs besitzen jeweils einen eigenen Kernel und benötigen mehrere Minuten zum Booten, während Container innerhalb von Millisekunden starten und sich den Host-Kernel über Isolierung auf OS-Ebene ohne Hardware-Virtualisierung teilen, was den Overhead gering hält
- Dank der Stabilität der Linux-System-Call-ABI können Container verschiedener Distributionen auf demselben Kernel laufen
- In einer Umgebung mit 16 GB RAM liegt die praktische Obergrenze bei etwa 50–100 leichten Containern, 10–30 mittelgroßen und 5–10 großen Containern
- Dieses Architekturverständnis ist wichtig, weil Kernel-Schwachstellen alle Container betreffen und die Wahl des Basis-Images direkte Auswirkungen auf Kompatibilität und Sicherheit hat
Was es bedeutet, „Ubuntu auszuführen“
- Führt man
docker run ubuntu:22.04aus, erhält man einen Bash-Prompt, der wie Ubuntu aussieht, und kannapt updatesowie Paketinstallationen ausführen - Führt man jedoch innerhalb des Containers
uname -raus, wird die Kernel-Version des Hosts angezeigt (z. B. 6.5.0-44-generic) - Die Datei
/etc/os-releasezeigt zwar Ubuntu 22.04 an, aber der Kernel gehört zum Host-System, und der „Ubuntu“-Teil ist lediglich das Dateisystem, das den User Space bildet
Container vs. virtuelle Maschinen: Architekturvergleich
- VMs virtualisieren die Hardware, Container virtualisieren das Betriebssystem
- Wichtige Unterschiede:
- Kernel: VMs besitzen jeweils einen eigenen Kernel, Container teilen sich den Host-Kernel
- Boot-Zeit: VMs mehrere Minuten, Container Millisekunden
- Speicher-Overhead: VMs 512 MB–4 GB, Container 1–10 MB
- Festplattennutzung: VMs 10–100 GB, Container-Images 10–500 MB
- Isolierungsgrad: VMs auf Hardware-Ebene, Container auf Prozess-Ebene
- Leistung: VMs mit etwa 5–10 % Overhead, Container mit nahezu nativer Performance
Woraus ein Basis-Image tatsächlich besteht
- Inhalt des Tarballs, der beim Pullen von
ubuntu:22.04heruntergeladen wird: -
1. Unverzichtbare Binärdateien (
/bin,/usr/bin)/bin/bash(Shell),/bin/ls(Dateiliste),/bin/cat(Dateianzeige)/usr/bin/apt(Paketmanager),/usr/bin/dpkg(Debian-Paketwerkzeug)
-
2. Gemeinsame Bibliotheken (
/lib,/usr/lib)- glibc und weitere Shared Libraries, gegen die Programme gelinkt sind
/lib/x86_64-linux-gnu/libc.so.6(C-Bibliothek – Grundlage aller C-Programme)- Wichtige Bibliotheken wie
libpthread.so.0,libm.so.6usw.
-
3. Konfigurationsdateien (
/etc)/etc/apt/sources.list(Paket-Repositories)/etc/passwd(Benutzerdatenbank)/etc/resolv.conf(DNS-Konfiguration, meist vom Host gemountet)
-
4. Paketdatenbank
/var/lib/dpkg/status(installierte Pakete)/var/lib/apt/lists/(Cache verfügbarer Pakete)
- Kernel, Bootloader und Treiber sind nicht enthalten
Der Kernel bleibt gleich, alles andere ändert sich
- Funktionen, die der Linux-Kernel bereitstellt: Prozess-Scheduling, Speicherverwaltung, Dateisystem-Operationen, Netzwerk-Stack, Gerätetreiber, System Calls
- Wenn ein Container-Prozess
open(),read(),fork()aufruft, wird dies direkt an den Host-Kernel weitergereicht - Dem Kernel ist weder bekannt noch wichtig, ob der betreffende Prozess in einem „Ubuntu-Container“ oder „Alpine-Container“ läuft
-
Stabilität der System-Call-Schnittstelle
- Die Linux-Syscall-ABI ist sehr stabil
- Warum ein mit glibc 2.31 (Ubuntu 20.04) kompiliertes Binary auch auf einem Ubuntu-24.04-Kernel läuft:
- Der Kernel wahrt Abwärtskompatibilität
- Keine Änderung der System-Call-Nummern
- Neue Funktionen kommen hinzu, bestehende werden aber kaum entfernt
- Deshalb kann ein Ubuntu-18.04-Container auf einem Host mit Kernel 6.5 laufen
Praktischer Test: gleicher Kernel, anderer User Space
- Führt man dieselbe Kernel-Abfrage in mehreren Basis-Images aus, sieht man, dass alle Images den Host-Kernel gemeinsam nutzen
ubuntu:22.04,debian:12,alpine:3.19,fedora:39,archlinux:latestzeigen alle dieselbe Kernel-Version an (6.5.0-44-generic)- Unterschiede zwischen den Containern liegen bei Komponenten wie dem
uname-Binary oder libc, also in der Userland-Zusammensetzung
Warum Container so effizient sind
-
1. Keine Kernel-Duplizierung
- VMs laden jeweils einen vollständigen Kernel in den Speicher (ca. 100–500 MB)
- 10 VMs verbrauchen Speicher für 10 Kernel, 10 Container nutzen nur einen Kernel
-
2. Sofortiger Start
- Boot-Reihenfolge einer VM: BIOS → Bootloader → Kernel → Init-System → Services
- Ein Container existiert mit nur
fork()- undexec()-Aufrufen innerhalb von Millisekunden als Prozess - Typischer VM-Start: 30–60 Sekunden / Container-Start: etwa 0,347 Sekunden
-
3. Gemeinsame Image-Layer
- Werden 100 Container aus
ubuntu:22.04gestartet, existieren die Basis-Image-Layer nur einmal auf der Festplatte - Jeder Container erhält lediglich einen dünnen Copy-on-Write-Layer für Änderungen
- Werden 100 Container aus
-
4. Speicherteilung über den Kernel
- Der Page Cache des Kernels wird gemeinsam genutzt
- Wenn 50 Container dieselbe Datei lesen, cached der Kernel sie nur einmal
- Bei identischen Shared Libraries können Speicherseiten per Copy-on-Write gemeinsam genutzt werden
Berechnung der Container-Grenzen
-
Speicheranalyse (auf Basis einer VM mit 16 GB RAM)
- Gesamter RAM: 16.384 MB
- Overhead des Host-OS: -1.024 MB
- Docker-Daemon: -256 MB
- Overhead der Container-Runtime: -512 MB
- Für Container verfügbar: 14.592 MB
-
Speicherverbrauch nach Container-Typ
- Minimal (
sleep): ca. 1 MB - Alpine + kleine App: ca. 25 MB
- Ubuntu + Python-App: ca. 120 MB
- Ubuntu + Java-App: ca. 500 MB
- Node.js-Service: ca. 200 MB
- Minimal (
-
Theoretisches Maximum
- Minimal-Container (1 MB): 14.592
- Alpine + kleine App (25 MB): 583
- Ubuntu + Python (120 MB): 121
- Java-Microservice (500 MB): 29
-
Praktische Grenzen
- Zusätzlich zum Speicher zu beachten:
- CPU-Scheduling: Zu viele konkurrierende Container verursachen Latenzspitzen
- Dateideskriptoren: Standard-
ulimit1024 - Netzwerk-Ports: Für Port-Mapping stehen nur 65.535 zur Verfügung
- PIDs: Begrenzung durch
/proc/sys/kernel/pid_max(Standard: 32.768) - Festplatten-I/O: OverlayFS-Overhead, viele Layer müssen durchsucht werden
- Bei realen Workloads auf einer VM mit 16 GB liegt die praktische Obergrenze bei:
- Leichten Containern (API, Worker): 50–100
- Mittleren Containern (DB, Cache): 10–30
- Großen Containern (ML-Modelle, JVM-Apps): 5–10
- Zusätzlich zum Speicher zu beachten:
Linux-Distributionskompatibilität
-
ABI-Zusicherung des Kernels
- Linux hält eine stabile Syscall-Schnittstelle aufrecht
- Für alte Kernel kompilierte Binaries laufen auf neuen Kerneln
- Ein Ubuntu-18.04-Binary läuft problemlos auf Kernel 6.5
-
Wann die Kompatibilität bricht
- Anforderungen an Kernel-Funktionen: wenn der Container Features braucht, die der Kernel nicht hat (z. B. io_uring erfordert Kernel 5.1+)
- Abhängigkeit von Kernel-Modulen: WireGuard benötigt das WireGuard-Kernelmodul, NVIDIA-Container benötigen den NVIDIA-Kerneltreiber
- Seccomp-/Capability-Beschränkungen: wenn der Host benötigte Syscalls blockiert (z. B. erfordert die Nutzung von
ptrace--cap-add SYS_PTRACE)
Leitfaden zur Wahl des Basis-Images
| Basis-Image | Größe | Paketmanager | Verwendungszweck |
|---|---|---|---|
scratch |
0 MB | keiner | statisch kompilierte Go-/Rust-Binaries |
alpine |
7 MB | apk | minimale Container, musl libc |
distroless |
20 MB | keiner | sicherheitsorientiert, ohne Shell und Paketmanager |
debian-slim |
80 MB | apt | ausgewogen zwischen Größe und Kompatibilität |
ubuntu |
78 MB | apt | entwicklerfreundlich |
fedora |
180 MB | dnf | aktuelle Pakete, SELinux |
-
Wann welches Image sinnvoll ist
- scratch: für statisch kompilierte Binaries, enthält nur das Binary und sonst kein OS
- alpine: minimales Image mit Shell-Zugriff; nutzt musl libc statt glibc, was zu Kompatibilitätsproblemen führen kann
- distroless: sicherheitsorientiertes Produktions-Image; Debugging ist schwieriger, weil Shell und Paketmanager fehlen, dafür ist es sicherer
Grenze zwischen User Space und Kernel
-
Was aus dem Basis-Image kommt (User Space)
- Shell (
/bin/bash,/bin/sh) - C-Bibliotheken (glibc, musl)
- Paketmanager (apt, apk, yum)
- Zentrale Utilities (ls, cat, grep)
- Konfiguration des Init-Systems (meist nicht systemd selbst)
- Standardbenutzer und -gruppen (
/etc/passwd) - Bibliothekspfade und Konfiguration
- Shell (
-
Was vom Host kommt (Kernel)
- Prozess-Scheduling und Speicherverwaltung
- Netzwerk-Stack (TCP/IP, Routing)
- Dateisystem-Operationen (Lesen, Schreiben, Mounten)
- Sicherheitsfunktionen (Namespaces, cgroups, seccomp)
- Gerätetreiber (GPU, Netzwerk, Storage)
- Zeit- und Taktverwaltung
- Kryptografie und Zufallszahlengenerierung
-
Die durch Namespaces erzeugte Illusion
- Der Kernel stellt Namespaces bereit, sodass sich Container isoliert anfühlen
- Ein Prozess, der im Container als PID 1 erscheint, existiert auf dem Host unter einer höheren PID (z. B. 45678)
- Der Kernel hält die Zuordnung aufrecht: Container-PID 1 → Host-PID 45678
- So funktioniert Isolierung ohne Virtualisierung
Bedeutung für Produktionsumgebungen
-
1. Kernel-Schwachstellen betreffen alle Container
- Hat der Host-Kernel eine Schwachstelle, sind alle Container exponiert
- Host-Patches aktuell zu halten ist Pflicht
-
2. Der Host-Kernel begrenzt die Container-Funktionen
- Für die Nutzung von io_uring ist auf dem Host Kernel 5.1+ nötig
- eBPF-Funktionen erfordern Kernel 4.15+ mit bestimmten aktivierten Optionen
-
3. Bedeutung von glibc vs. musl
- Alpine verwendet musl libc
- Manche für glibc kompilierten Binaries funktionieren dort nicht
- Beispiel: Beim Ausführen eines glibc-Binarys auf Alpine kann ein Fehler auftreten, weil
/lib/x86_64-linux-gnu/libc.so.6nicht vorhanden ist
-
4. Das Container-„OS“ ist rein ein Organisationskonzept
- Aus Sicht des Kernels gibt es keinen Unterschied zwischen einem „Ubuntu-Container“ und einem „Debian-Container“
- Beides sind lediglich Prozesse, die Syscalls ausführen
Häufige Missverständnisse
- ❌ „Container sind leichte VMs“: Container sind Prozesse mit fortgeschrittener Isolierung; VMs virtualisieren Hardware und führen einen separaten Kernel aus
- ❌ „Jeder Container hat seinen eigenen Kernel“: Alle Container teilen sich den Host-Kernel; das „OS“ des Containers besteht nur aus User-Space-Dateien
- ❌ „Einen Ubuntu-Container starten = Ubuntu ausführen“: Man nutzt den Host-Kernel und Ubuntu-Werkzeuge; wenn der Host Debian ist, läuft tatsächlich ein Debian-Kernel
- ❌ „Ein Basis-Image enthält ein vollständiges Betriebssystem“: Ein Basis-Image enthält nur minimale User-Space-Werkzeuge, keinen Kernel, Bootloader oder Treiber
- ❌ „Mehr Container = mehr Speicher“: Durch gemeinsame Layer und Kernel-Page-Caching können Container Speicher oft effizient gemeinsam nutzen
Kernaussagen
- Ein Docker-Basis-Image ist ein Dateisystem-Snapshot der User-Space-Komponenten einer Linux-Distribution
- Also der Binärdateien, Bibliotheken und Konfigurationen, durch die sich Ubuntu wie Ubuntu anfühlt
- Das eigentliche Betriebssystem, also der Kernel, wird mit dem Host geteilt
- Diese Architektur ermöglicht:
- Startzeiten im Millisekundenbereich (kein Kernel-Boot)
- Minimalen Speicher-Overhead (ein Kernel, gemeinsam genutzte Seiten)
- Hohe Dichte im großen Maßstab (Hunderte Container pro Host)
- Nahezu native Performance (direkte Syscalls an den Kernel)
- Der Trade-off ist eine schwächere Isolierung als bei VMs – da Container den Kernel teilen, betreffen Kernel-Exploits alle Container
- Für die meisten Workloads ist dieser Trade-off lohnend
9 Kommentare
Kernel + Werkzeuge = Distribution
Dann wäre auch das Ubuntu, oder nicht..
Es gibt also auch Tutorials dazu, Docker direkt unter Linux selbst nachzubauen, dabei Verzeichnisse zu isolieren und sich mit Benutzern, Gruppen und allerlei anderem zu beschäftigen.
Sehr informativ.
https://de.news.hada.io/topic?id=9531
Deshalb kann man, etwas überspitzt gesagt,
chroot + cgroup = Dockerso verstehen.Acktually, das kommt eher
systemd-nspawnein bisschen näher ☝️🤓Eigentlich
Sarkasmus / selbstironisch
Wirklich faszinierend.
Der Haupttext scheint das anhand von Linux zu erklären, aber
wenn man es unter Windows ausführt, würde dann der von WSL2 erzeugte virtuelle Kernel wie im Artikel beschrieben gemeinsam genutzt werden?
Falls Docker eine Schwachstelle hätte, über die man den Kernel angreifen könnte, frage ich mich auch, ob Windows mit dieser zusätzlichen Virtualisierungsschicht womöglich sicherer wäre als Linux.
Die Reaktion im obigen Kommentar hat mich etwas überrascht.
Ich dachte, das wäre selbstverständlich und allen beim Einsatz klar.
Der Linux-Kernel kommt vom Host – alles andere wird in Form der Tools eingebracht, die in der jeweiligen Linux-Distribution verwendet werden.
WSL2 läuft meines Wissens virtualisiert unter Hyper-V.
Windows – Linux in der virtuellen Maschine – und darin dann noch einmal ein Container ...
Grundsätzlich ist
rootinnerhalb eines Containers nicht wirklichrootdes gesamten Systems, deshalb kann man den Kernel normalerweise nicht einfach beliebig manipulieren.Wenn allerdings eine Sicherheitslücke auftaucht, wird es natürlich kritisch.
Aus Performance-Sicht ist Windows deshalb tendenziell langsamer, weil dort noch eine zusätzliche Virtualisierungsschicht dazwischenliegt.