17. Juli 20265 min

Einen kleinen Linux-Container ohne Docker bauen 2026

Ein praktischer 2026 Walkthrough zum Bau eines kleinen isolierten Linux-Containers mit overlayFS, cgroups, namespaces, pivot_root und Kernel-Primitiven statt Docker.

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_root verschiebt 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_root verschob 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.