Stell dir vor, es ist Freitagnachmittag um 16:00 Uhr. Dein Team hat gerade die neue Microservice-Architektur fertiggestellt. Du setzt den Befehl ab, um Run Docker Container From Image zu starten, und alles sieht im ersten Moment gut aus. Die Container laufen, die Logs bleiben ruhig. Du gehst ins Wochenende. Am Montagmorgen erwartet dich keine Erfolgsmeldung, sondern ein wütender Anruf vom Cloud-Provider oder deinem Chef. Die Instanzen sind wegen Speicherüberlastung abgestürzt, die Festplatten sind mit Logfiles zugemüllt, die niemand begrenzt hat, und eine Sicherheitslücke im Basis-Image hat dazu geführt, dass ein Bot-Netzwerk deine Rechenleistung für Krypto-Mining missbraucht hat. Die Kosten? Fünfstelliger Bereich, nur weil man dachte, ein einfacher Befehl reicht aus. Ich habe dieses Szenario in den letzten zehn Jahren bei Firmen jeder Größe miterlebt, vom kleinen Startup bis zum DAX-Konzern. Der Glaube, dass Docker alles "magisch" löst, ist der teuerste Irrtum der modernen IT.
Der Mythos vom fertigen Image aus dem Internet
Einer der häufigsten Fehler, die ich sehe, ist das blinde Vertrauen in öffentliche Repositories. Jemand braucht eine Datenbank oder einen Webserver und zieht sich das erstbeste Image, das bei einer Suche ganz oben steht. Das Problem ist nicht nur die Sicherheit. Es ist die schiere Ineffizienz. Diese Images sind oft vollgestopft mit Werkzeugen, die du in der Produktion niemals brauchst. Wir reden hier von Paketen wie curl, git oder sogar kompletten Build-Umgebungen, die nur darauf warten, von einem Angreifer übernommen zu werden.
Ich habe ein Projekt begleitet, bei dem ein Team ein Standard-Node.js-Image verwendete, das fast 1 GB groß war. Der Prozess dauerte ewig, die Deployment-Pipelines waren langsam und die Speicherkosten auf den Knoten explodierten. Erst als wir auf "Alpine"-Varianten oder "Distroless"-Images umstiegen, sank die Größe auf unter 100 MB. Das spart nicht nur Platz. Es reduziert die Angriffsfläche massiv. Wer keine Shell im Container hat, macht es einem Hacker verdammt schwer, sich im System umzusehen.
Warum Run Docker Container From Image ohne Limits dein Todesurteil ist
Es gibt diesen einen Befehl, den fast jeder Anfänger nutzt, ohne über die Konsequenzen nachzudenken. Man startet den Container und gibt ihm keine Grenzen. In der Theorie nimmt sich Docker nur das, was es braucht. In der Praxis reicht ein einziger Speicherleck in deiner Applikation aus, um den gesamten Host-Server in die Knie zu zwingen. Wenn ein Container anfängt, den gesamten RAM des Hosts zu fressen, fängt der Linux-Kernel an, Prozesse zu töten. Und meistens trifft es nicht den Amok laufenden Container, sondern wichtige Systemdienste oder die Datenbank daneben.
Wenn du Run Docker Container From Image ausführst, musst du zwingend Flags für Speicher und CPU setzen. Ein Limit von 512 MB RAM klingt wenig, aber es zwingt dich dazu, deine Software sauber zu schreiben. Ich habe erlebt, wie ein unbegrenzter Container innerhalb von 20 Minuten 64 GB RAM verschlungen hat, nur weil eine Schleife im Code nicht sauber terminiert wurde. Das hat eine ganze Abteilung lahmgelegt. Setze Limits. Immer. Es gibt keine Entschuldigung dafür, es nicht zu tun.
Das Problem mit dem Root-User
Standardmäßig laufen Prozesse in einem Container als Root. Das ist Wahnsinn. Wenn es jemandem gelingt, aus dem Container auszubrechen – und solche Sicherheitslücken gibt es immer wieder –, hat er Root-Zugriff auf deinen Host-Server. Trotzdem sehe ich in acht von zehn Dockerfiles keinen USER-Befehl. Die Leute sagen mir dann: "Aber meine App braucht Root-Rechte, um Port 80 zu binden." Nein, braucht sie nicht. Man kann Ports umleiten oder dem Prozess spezifische Rechte geben. Wer als Root deployt, handelt grob fahrlässig. Es dauert fünf Minuten, einen nicht-privilegierten Nutzer anzulegen, aber es rettet dir den Job, wenn der Ernstfall eintritt.
Daten gehören niemals in den Container
Das klingt wie eine Grundregel aus dem Lehrbuch, aber die Realität in deutschen Serverräumen sieht anders aus. Ich wurde einmal zu einem Kunden gerufen, der panisch war, weil nach einem Neustart des Hosts alle Kundendaten der letzten zwei Tage weg waren. Was war passiert? Sie hatten ihre Datenbank in einem Container laufen lassen, aber die Daten innerhalb des beschreibbaren Layers des Containers gespeichert.
Ein Container ist flüchtig. Er ist dafür gedacht, jederzeit weggeworfen und neu gestartet zu werden. Wenn du ihn stoppst und löschst, ist alles weg, was nicht explizit auf einem Volume oder einem externen Speicher liegt. Die Lösung ist einfach, wird aber oft aus Bequemlichkeit ignoriert. Nutze benannte Volumes oder Mounts vom Host-System. Und nein, ein lokaler Ordner auf deinem Laptop ist kein Backup-Konzept für die Produktion. Wer Daten im Container lässt, spielt russisches Roulette mit seinen Geschäftsdaten.
Die Lüge der "Latest" Tags
Wir müssen über Tags reden. Fast jeder nutzt image:latest. Es klingt logisch – man will ja immer die neueste Version haben. Aber in einer professionellen Umgebung ist latest dein schlimmster Feind. Warum? Weil du die Kontrolle abgibst. Wenn der Maintainer des Images morgen eine Version veröffentlicht, die nicht abwärtskompatibel ist, und dein System nachts neu startet, zieht es sich die kaputte Version. Dein System bricht zusammen, und du hast keine Ahnung, warum, weil du "nichts geändert hast".
Ich erinnere mich an einen Fall, bei dem ein automatisches Update eines Basis-Images die gesamte Python-Laufzeitumgebung von 3.8 auf 3.10 gehoben hat. Die halbe Softwarebibliothek des Kunden war damit nicht kompatibel. Das Ergebnis war ein ganzer Tag Stillstand für das gesamte Entwicklerteam. Nutze spezifische Versionstags. Wenn du Version 1.4.2 brauchst, dann schreibe 1.4.2. Updates sollten ein bewusster Schritt sein, den du testest, und kein Zufallsprodukt deiner Infrastruktur.
Vernachlässigte Logs und die schleichende Festplattenfüllung
Ein oft übersehener Aspekt beim Prozess, einen Container stabil zu betreiben, ist das Logging. Docker fängt alles ab, was deine App nach stdout und stderr schreibt. Das ist super für das Debugging. Aber Docker begrenzt diese Logdateien standardmäßig nicht. Ich habe Systeme gesehen, auf denen die /var/lib/docker Partition zu 100 % gefüllt war, nur weil ein Container im Sekundentakt Fehlermeldungen produziert hat.
Wenn die Platte voll ist, geht gar nichts mehr. Keine neuen Container, keine Datenbank-Schreibvorgänge, oft nicht mal mehr ein Login per SSH. Du musst die Log-Rotation in der Docker-Daemon-Konfiguration oder pro Container festlegen. Wer das ignoriert, baut sich eine Zeitbombe. Ein Limit von 10 MB pro Datei und maximal drei Rotationen reicht meistens völlig aus. Alles, was darüber hinausgeht, gehört sowieso in ein zentrales Logging-System wie Graylog oder ELK, nicht auf die lokale Platte des Host-Servers.
Ein Vorher/Nachher-Vergleich aus der Praxis
Schauen wir uns an, wie der Unterschied zwischen einem amateurhaften und einem professionellen Ansatz in der Realität aussieht.
Vorher: Der naive Weg
Ein Entwickler möchte eine kleine Web-Applikation hosten. Er schreibt ein Dockerfile, das auf ubuntu:latest basiert. Er installiert Python, kopiert seinen Code hinein und startet den Container mit einem einfachen Befehl ohne weitere Parameter. Der Container läuft als Root-Nutzer. Die Logs fließen unbegrenzt in das Standardverzeichnis. Die Datenbank-Zugangsdaten stehen im Klartext im Dockerfile oder werden als Umgebungsvariablen direkt im Startbefehl übergeben, der nun für jeden in der Prozessliste (ps aux) sichtbar ist. Nach drei Wochen ist die Festplatte voll, ein Update bricht die App und bei einem Audit stellt sich heraus, dass das System seit Tagen Teil eines Botnetzes ist, weil eine alte Bibliothek im Ubuntu-Image nie aktualisiert wurde.
Nachher: Der professionelle Weg
Derselbe Entwickler nutzt jetzt ein minimales python:3.11-slim Image. Er legt im Dockerfile einen Nutzer namens appuser an und wechselt zu diesem, bevor die App startet. Er nutzt eine .dockerignore Datei, um keine unnötigen Geheimnisse oder Git-Verzeichnisse in das Image zu kopieren. Beim Starten setzt er Limits: --memory="256m" und --cpus=".5". Er nutzt Docker Secrets oder eine verschlüsselte Umgebungsvariable für das Datenbank-Passwort. Die Logs werden über den json-file Driver mit einer max-size von 10 MB begrenzt. Das Image ist exakt getaggt und wird in einer privaten Registry gescannt, bevor es produktiv geht. Das System läuft monatelang stabil. Wenn eine Komponente mehr Ressourcen braucht, meldet das Monitoring dies, bevor das System abstürzt. Die Kosten sind planbar und die Sicherheit ist auf einem Niveau, das auch einer professionellen Prüfung standhält.
Die Falle der Netzwerkkonfiguration
Viele Anfänger nutzen den "Host-Modus" für das Netzwerk, weil es einfacher ist. Man muss sich keine Gedanken über Port-Mappings machen. Aber damit reißt man eine riesige Sicherheitslücke auf. Der Container hat direkten Zugriff auf die Netzwerkschnittstellen des Hosts. Ein Fehler in der App, und schon kann ein Angreifer Dienste erreichen, die eigentlich nur lokal auf dem Server lauschen sollten.
Nutze stattdessen Bridge-Netzwerke. Isoliere deine Container. Eine Web-App muss nur mit der Datenbank reden können, nicht mit dem Internet und schon gar nicht mit dem SSH-Dienst deines Servers. Docker erlaubt es, eigene Netzwerke zu erstellen, in denen sich Container nur untereinander über ihre Namen finden. Das ist sauber, sicher und verhindert, dass ein kompromittierter Webserver sofort dein gesamtes internes Netz scannen kann.
Realitätscheck: Was es wirklich braucht
Vergiss die Idee, dass du Docker "nebenbei" lernst, indem du ein paar Tutorials liest. Wer professionell mit Containern arbeitet, muss Linux-Grundlagen beherrschen. Du musst wissen, wie Namespaces und Cgroups funktionieren, sonst verstehst du nie, warum dein Container sich seltsam verhält. Es gibt keine Abkürzung zur Stabilität.
In der echten Welt verbringst du 10 % der Zeit damit, den Container zum Laufen zu bringen, und 90 % der Zeit damit, ihn sicher, schlank und überwachbar zu machen. Wenn du nicht bereit bist, dich mit Ressourcenlimits, Nutzerrechten und Image-Größen auseinanderzusetzen, wirst du früher oder später für diesen Fehler bezahlen. Entweder mit deiner Zeit bei der nächtlichen Fehlersuche oder mit echtem Geld für unnötige Cloud-Ressourcen. Docker ist ein mächtiges Werkzeug, aber in den falschen Händen ist es einfach nur eine komplizierte Art, seinen Server kaputt zu machen. Sei derjenige, der die Details ernst nimmt. Es ist am Anfang mühsamer, aber es ist der einzige Weg, der auf Dauer funktioniert.