print("Python:", sys.version.split()[0])Python: 3.12.14
print("System:", platform.system(), platform.release())System: Linux 6.17.0-1022-azure
print("Zeitzone:", os.environ.get("TZ", "nicht gesetzt"))Zeitzone: UTC
Vorwissen: Pakete und Umgebungen, denn ein Container ist die nächste Stufe derselben Idee, und Linux und Shell für die Befehle. Anders als auf den übrigen Seiten läuft hier kein Docker mit: Der Build hat keinen Docker-Dienst, die Befehle sind deshalb Listings. Alles, was sich ohne Docker zeigen lässt, ist ausgeführt.
Eine virtuelle Umgebung friert die Python-Pakete ein. Sie friert nicht ein, welche Python-Version läuft, welche Systembibliotheken vorhanden sind und welches Betriebssystem darunter liegt. Genau daran scheitern Auswertungen beim Umzug: Das Paket ist installiert, aber gegen eine andere C-Bibliothek gebaut, oder die Zeitzone des Servers verschiebt Datumsangaben.
Was eine Umgebung nicht festlegt, lässt sich zeigen:
Python: 3.12.14
System: Linux 6.17.0-1022-azure
Zeitzone: UTC
Diese drei Angaben stammen vom Rechner, der diese Website baut. Auf einem anderen Rechner stünde dort etwas anderes, und die Abhängigkeitsliste würde das nicht bemerken.
| virtuelle Maschine | Container | |
|---|---|---|
| enthält | vollständiges Gastbetriebssystem samt Kern | nur Dateisystem und Prozesse |
| Grösse | Gigabyte | Megabyte |
| Startzeit | Minute | Sekunden |
| Isolation | sehr weitgehend | gut, aber Kern geteilt |
| Grenze | schwergewichtig | an die Kernfamilie des Hosts gebunden |
Der entscheidende Unterschied liegt im Kern. Eine VM bringt ihren eigenen mit und emuliert damit einen ganzen Rechner. Ein Container nutzt den des Hosts und kapselt nur, was darüber liegt. Deshalb startet er in Sekunden, und deshalb laufen Linux-Container nicht nativ auf einem Windows-Kern, sondern in einer darunterliegenden Linux-VM.
Diese beiden Begriffe werden ständig verwechselt, und die Verwechslung führt zu falschen Erwartungen.
Ein Image ist unveränderlich und beschreibt, was drin ist. Ein Container ist ein laufendes Exemplar davon. Aus demselben Image lassen sich beliebig viele Container starten, und jeder beginnt im selben definierten Zustand.
Die Beziehung ist dieselbe wie zwischen Klasse und Instanz aus Objektorientierung in Python: der Bauplan und das Exemplar.
Es beschreibt, wie ein Image gebaut wird. Jeder Schritt erzeugt eine Schicht, die zwischengespeichert wird, und daraus folgt die wichtigste Regel für die Reihenfolge.
Würden die beiden COPY-Zeilen vertauscht, müsste bei jeder Codeänderung die gesamte Paketinstallation neu laufen, weil alle darauffolgenden Schichten verworfen werden. So gebaut, wird sie aus dem Zwischenspeicher genommen, und der Bau dauert Sekunden statt Minuten.
Der Bildname trägt eine Version nach dem Doppelpunkt. latest ist verlockend und der häufigste Fehler: Es ist kein fester Zeiger, sondern verweist auf das, was zuletzt so benannt wurde. Wer Reproduzierbarkeit will, vergibt und verwendet feste Versionsnummern.
Ein Container verliert beim Beenden alles, was in ihm geschrieben wurde. Das ist gewollt, denn genau daraus folgt der definierte Ausgangszustand. Für Daten, die bleiben sollen, wird ein Verzeichnis des Hosts eingehängt:
Die Portangabe liest sich von aussen nach innen: Der Port 8888 des Hosts wird auf den Port 8888 im Container geleitet. Ohne diese Zeile läuft der Dienst zwar, ist aber von aussen nicht erreichbar.
Der Nutzen liegt darin, dass ein Analyseprojekt samt Systembibliotheken, Interpreter und Paketversionen als ein einziges Artefakt weitergegeben werden kann. Das ist die Stufe über der virtuellen Umgebung.
R version 4.6.1 (2026-06-24)
Plattform: x86_64-pc-linux-gnu
Beide Ausgaben zusammen beschreiben, was ein Image festschreiben würde. Solange sie nur im Bericht stehen, sind sie Dokumentation; im Dockerfile werden sie zur ausführbaren Zusage.
Die Abstufung als Faustregel: virtuelle Umgebung für die eigene Arbeit am eigenen Rechner, Container sobald etwas weitergegeben wird, auf einem Server läuft oder länger reproduzierbar bleiben soll. Virtuelle Maschinen nur, wenn ein anderes Betriebssystem gebraucht wird.
docker build -t projekt:1.0 . # Image aus dem Dockerfile bauen
docker images # vorhandene Images auflisten
docker run --rm -it projekt:1.0 bash # Container starten, danach verwerfen
docker run -p 8888:8888 projekt:1.0 # Port nach aussen durchreichen
docker ps -a # laufende und beendete Container
docker logs <container> # Ausgabe eines Containers ansehen
docker exec -it <container> bash # in einen laufenden Container gehen
docker system prune # nicht mehr genutzte Objekte entfernenEinen Ordner des Rechners im Container verfügbar machen
Ein Dockerfile für eine Auswertung
Ergebnisse aus dem Container herausbekommen
Nur was in einem eingehängten Ordner oder einem Volume liegt, überlebt das Ende des Containers. Ergebnisse deshalb immer in den eingehängten Pfad schreiben, nicht in das Arbeitsverzeichnis des Images.
Im Build dieser Website läuft kein Docker, deshalb sind die Dockerfiles und die docker-Befehle hier Listings. Was wirklich läuft, ist die Ausgabe zur Umgebung weiter oben: Sie zeigt, was eine virtuelle Umgebung eben nicht festhält. Die Stufe darunter steht unter Pakete und Umgebungen.
Warum kann derselbe Container auf jedem Rechner gleich laufen, dieselbe virtuelle Umgebung aber nicht?
Warum gehören die Zeilen für die Paketinstallation im Dockerfile vor die für den eigenen Code?