Container wirken mysterioes, bis man einen selbst baut.
In diesem Walkthrough bauen wir eine kleine isolierte Linux-Umgebung mit Primitiven, die bereits im Kernel existieren: overlayFS, cgroups, namespaces und pivot_root. Kein Docker, kein Podman, keine Container Runtime.
Nutze fuer dieses Experiment eine wegwerfbare virtuelle Maschine. Wir fuehren privilegierte Befehle aus, erzeugen Device Nodes, mounten Filesystems und wechseln Root-Verzeichnisse. Ein Host-System aus Versehen zu beschaedigen ist sehr einfach.
Der Punkt ist nicht, Docker in Produktion zu ersetzen. Der Punkt ist zu verstehen, welche Bausteine Docker und aehnliche Tools fuer dich verpacken.
Die Kernel-Bausteine hinter einem Container
Ein Container ist kein einzelnes magisches Feature. Er ist eine Kombination aus Linux-Isolation und Resource-Control-Mechanismen:
- overlayFS gibt dem Container eine beschreibbare Filesystem-Schicht,
- cgroups erzwingen CPU- und Memory-Limits,
- namespaces isolieren Process IDs, Hostnames, Mounts, Networking, IPC und cgroup-Views,
pivot_rootverschiebt den Prozess in ein neues Root Filesystem.
Der Begriff control group, oder cgroup, erschien 2007 im Linux-Kernel. Urspruenglich hiess das Feature process containers. Das ist eine gute Erinnerung daran, dass Container-Tooling nach den Kernel-Ideen kam.
Ein privates Root Filesystem vorbereiten
Erzeuge eine Verzeichnisstruktur fuer das Experiment:
mkdir -p /tmp/mybox/{lower,upper,work,merged}
Lade ein kleines Root-Filesystem-Archiv herunter, zum Beispiel Alpine, BusyBox oder ein aehnliches minimales rootfs, und entpacke es nach lower:
tar -xzf mini-rootfs.tar.gz -C /tmp/mybox/lower
Jetzt legen wir overlayFS darueber, damit Aenderungen in upper bleiben, waehrend die Originaldateien unveraendert bleiben:
mount -t overlay overlay \
-o lowerdir=/tmp/mybox/lower,upperdir=/tmp/mybox/upper,workdir=/tmp/mybox/work \
/tmp/mybox/merged
/tmp/mybox/merged verhaelt sich jetzt wie das Root-Verzeichnis des Containers.
Weil overlayFS nur geaenderte Dateien speichert, kann ein Container eine read-only Base teilen und nur eine kleine beschreibbare Schicht halten. Das ist ein Grund, warum Container Images effizient sein koennen, selbst wenn viele Container dieselbe Base nutzen.
Resource Limits mit cgroups setzen
Dieses Beispiel geht von cgroup v2 aus.
Erzeuge eine neue cgroup und erlaube CPU- und Memory-Controls darin:
mkdir -p /sys/fs/cgroup/mybox.slice/one
echo "+memory +cpu" > /sys/fs/cgroup/mybox.slice/cgroup.subtree_control
Begrenze den Container auf 15 Prozent eines CPU-Cores und 512 MiB RAM. Swap deaktivieren wir fuer das Experiment:
echo "15000 100000" > /sys/fs/cgroup/mybox.slice/one/cpu.max
echo "512M" > /sys/fs/cgroup/mybox.slice/one/memory.max
echo "0" > /sys/fs/cgroup/mybox.slice/one/memory.swap.max
Warum 15000 100000?
Die erste Zahl ist Laufzeit in Mikrosekunden. Die zweite Zahl ist die Gesamtperiode. Der Prozess darf 15.000 Mikrosekunden von jeweils 100.000 Mikrosekunden laufen, also exakt 15 Prozent.
Neue Namespaces betreten
Wechsle zu root, trete der cgroup bei und starte dann eine Shell in frischen namespaces:
sudo -i
echo $$ > /sys/fs/cgroup/mybox.slice/one/cgroup.procs
unshare \
--uts --pid --mount --mount-proc \
--net --ipc --cgroup \
--fork /bin/bash
Jetzt hast du mehrere isolierte Views:
- UTS namespace: ein Hostname, der nur im Container sichtbar ist.
- PID namespace: der erste Prozess innen bekommt PID 1.
- Mount namespace: Mount Points sind privat fuer diese Umgebung.
- Network namespace: Networking kann vom Host getrennt werden.
- IPC namespace: Inter-Process Communication ist isoliert.
Probiere, den Hostname zu aendern:
hostname mycontainer2026
Dieser Hostname ist nur in diesem namespace sichtbar.
Root mit pivot_root wechseln
Wechsle in das merged-Verzeichnis, mache den Mount Tree privat, pivotiere in das neue Root und entferne das alte:
cd /tmp/mybox/merged
mount --make-rprivate /
mkdir old_root
pivot_root . old_root
umount -l /old_root
rmdir old_root
pivot_root ist staerker als eine einfache chroot-Demo, weil das alte Root danach getrennt werden kann. Das verhindert mehrere klassische Escape-Muster, bei denen das vorherige Root erreichbar bleibt.
Wichtige virtuelle Filesystems mounten
Ohne /proc, /sys und /dev scheitern viele Tools.
Erzeuge ein minimales Set:
mknod -m 666 dev/null c 1 3
mknod -m 666 dev/zero c 1 5
mknod -m 666 dev/tty c 5 0
mkdir -p dev/{pts,shm}
mount -t devpts devpts dev/pts
mount -t tmpfs tmpfs dev/shm
mount -t sysfs sysfs sys
mount -t tmpfs tmpfs run
mount -t proc proc proc
Das ist weiterhin keine production-grade Container-Umgebung. Es ist eine Lernumgebung. Echte Runtimes ergaenzen Mount Policies, seccomp, capabilities, AppArmor oder SELinux, user namespaces, Image Management, Networking und Cleanup-Logik.
Die Shell durch eine Anwendung ersetzen
Fuer die Demo starten wir eine minimale Shell aus dem Root Filesystem:
exec /bin/busybox sh
Ab jetzt befindest du dich in einer selbstgebauten container-aehnlichen Umgebung.
Der Prozess hat eine isolierte PID-View, einen privaten Hostname, einen eigenen Mount namespace, ein separates Root Filesystem und cgroup-Limits.
Limits pruefen
CPU-Test
Im Container einen engen Loop starten:
while :; do :; done
Auf dem Host den busybox-Prozess pruefen:
top -Hp $(pgrep -f busybox)
Die CPU-Nutzung sollte um das konfigurierte Limit begrenzt sein.
Memory-Test
Weiterhin im Container:
python - <<'PY'
b = bytearray(700 * 1024 * 1024)
PY
Sobald die Allocation das 512-MiB-Memory-Limit ueberschreitet, sollte der Kernel Memory Controller den Prozess beenden und ausgeben:
Killed
Mit aktiviertem Swap kann der Kernel Seiten zuerst auf Disk verschieben. Swap zu deaktivieren macht das Out-of-Memory-Verhalten leichter sichtbar.
Aufraeumen
Verlasse die Container-Shell und unmount das Overlay:
exit
umount /tmp/mybox/merged
Danach die temporaeren Verzeichnisse entfernen:
rm -rf /tmp/mybox
Wenn ein Mount sich nicht entfernen laesst, pruefe ihn mit:
findmnt | grep mybox
Dann zuerst verschachtelte Filesystems unmounten, bevor das Verzeichnis entfernt wird.
Was man daraus lernt
Wir haben eine kleine container-aehnliche Umgebung nur mit Linux-Kernel-Primitiven gebaut:
- overlayFS stellte die beschreibbare Schicht bereit,
- cgroups erzwangen Resource Quotas,
- namespaces isolierten die Prozessumgebung,
pivot_rootverschob den Prozess in ein neues Root Filesystem.
Docker, Podman, containerd und Kubernetes machen diese Mechanismen im grossen Massstab nutzbar. Sie ergaenzen Image Distribution, Networking, Metadaten, Security Defaults, Lifecycle Management und operative Ergonomie.
Darunter bleiben die Kernideen aber dieselben.
Wenn man einmal einen kleinen Container von Hand gebaut hat, wirken Container nicht mehr wie Magie. Sie werden zu einer Reihe verstaendlicher Kernel-Features, die in der richtigen Reihenfolge zusammengesetzt werden.
Dieses Verstaendnis hilft beim Debugging von Produktionscontainern, beim Haerten von Workloads und beim Erklaeren, warum Isolation stark, aber nicht absolut ist.


