Begriff
Dockerfile
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Das Dockerfile (ohne Dateiendung!) ist das Kochrezept für dein Image. Docker liest es von oben nach unten. Jede Zeile im Rezept fügt eine Zutat hinzu. Typische Befehle:
FROM: Nimm einen Basis-Kuchenboden (Ubuntu).COPY: Wirf meine Erdbeeren (Code) drauf.RUN: Backe es bei 200 Grad (Kompilieren / Installieren).CMD: Serviere es mit Sahne (Startbefehl).
Merksatz: Eine Textdatei mit Instruktionen zur automatisierten Erstellung eines Docker-Images. Jeder Befehl erzeugt einen neuen Layer im Image.
:level0
Lernbruecke: Ein Dockerfile ist eine Bauanleitung. FROM sagt, womit du startest. COPY legt Dateien hinein. RUN bereitet etwas vor. CMD sagt, was beim Start laufen soll.
Ein klassisches Node.js Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Bauen mit docker build -t my-app ..
:level1
Praxisbeispiel: Du baust ein kleines Node.js-Image. Erst Basisimage, dann package-Dateien kopieren, Abhaengigkeiten installieren, App kopieren, Startbefehl setzen. Danach testest du das Image lokal.
Praxisroutine
In der Praxis lernst du Dockerfile, indem du mit einem kleinen, kontrollierten Beispiel beginnst. Baue zuerst einen Minimalfall, prüfe das Ergebnis, veraendere genau eine Sache und beobachte, was sich ändert. Notiere dir Eingabe, Aktion, Ausgabe und typischen Fehler.
Übung: Erstelle ein Beispiel aus deinem Alltag, fuehre den Ablauf gedanklich Schritt für Schritt durch und markiere die Stelle, an der du Feedback oder ein Log brauchst. Wenn du diese Stelle benennen kannst, verstehst du den Begriff praktisch.
1. Instruktions-Details
COPYvsADD:ADDkann URLs herunterladen und.tar.gzautomatisch entpacken. Das ist oft "magisch" und ungewollt. Best Practice: Nutze immerCOPY, außer du brauchst das Entpacken zwingend.ENVvsARG:ARG: Variable existiert nur beim Build. (docker build --build-arg VERSION=1.0).ENV: Variable existiert im laufenden Container.SHELL: Du kannst die Shell ändern (SHELL ["/bin/bash", "-c"]), praktisch für Windows Container (PowerShell) oder wenn du "Pipefail" aktivieren willst (set -o pipefail).
2. Layer-Optimierung
Schlecht:
RUN apt-get update
RUN apt-get install -y vim
RUN rm -rf /var/lib/apt/lists/*
Das sind 3 Layer. Im 2. Layer sind die apt-Listen da. Im 3. gelöscht. Das Image bleibt groß, weil Layer 2 noch existiert! Gut (Chaining):
RUN apt-get update && \
apt-get install -y vim && \
rm -rf /var/lib/apt/lists/*
Das ist ein Layer. Müll wird im selben Schritt gelöscht -> Image bleibt klein.
3. .dockerignore
Unterschätzt!
Alles, was nicht in .dockerignore steht, wird an den Daemon übertragen (Build Context).
Wenn du .git (500MB History) nicht ignorierst, dauert jeder Build 10 Sekunden länger ("Sending build context...").
Und: .env Dateien mit Passwörtern landen aus Versehen im Image!
Technisch entscheidet die Reihenfolge der Befehle über Cache und Build-Zeit. Haefig geänderte Dateien sollten später kopiert werden, damit Dependency-Layer wiederverwendet werden können.
1. Build-Zeit vs. Laufzeit Executables
Oft findet man im Dockerfile Befehle, die konzeptionell falsch platziert sind.
Ein typischer Fehler: RUN npm start anstelle von CMD ["npm", "start"].
RUNwird exakt einmal zum Zeitpunkt des Image-Builds (z.B. in der GitHub Action) im isolierten temporären Container ausgeführt. Wenn die App 30 Sekunden läuft und crasht, crasht der Build.CMDundENTRYPOINTsind Befehle, die erst feuern, wenn ein User später auf dem Produktivserverdocker runtippt. Sie werden einfach als Metadaten-String in die JSON-Struktur des Images gehängt.
2. Exec-Form vs. Shell-Form
Befehle existieren historisch in zwei Syntax-Arten.
- Shell-Form:
CMD node app.js. Docker schaltet heimlich/bin/sh -cdazwischen. Problem: Die Shell krallt sich PID 1. Dasnode-Programm startet als Kind-Prozess. Drückst dudocker stop, kriegt die Shell das SIGTERM-Signal, gibt es aber nicht an Node weiter. Node stirbt grausam nach 10s Timeout-Kill. - Exec-Form:
CMD ["node", "app.js"]. Hier wird Node direkt als PID 1 im Container gespawned (ohne/bin/sh). Signale kommen sauber durch, Graceful Shutdown funktioniert, Datenbank-Connections werden sanft beendet.
3. Geheimnisse im Build (Secret Mounts)
Braucht dein Build-Prozess ein OAuth-Token (um ein privates NPM Package von GitHub Packages zu laden), tippen Anfänger oft: ENV TOKEN=xyz.
Das ist extrem falsch, da das API-Token dann für immer als Klartext im Environment des finalen Images brennt (jeder kann es mit docker inspect stehlen).
BuildKit hat dafür Secret Mounts eingeführt:
RUN --mount=type=secret,id=npm_token npm ci
Der Mount hängt eine kleine Read-Only Datei temporär an den RUN Befehl und blendet sie nach Ausführung mikroskopisch schnell wieder aus. Nichts landet in den Persistenzlayern des Abbildes.
Quick-Check
Was macht
onbuild?Ein Trigger für Kind-Images. Wenn ichFROM my-onbuild-imagemache, werden Befehle ausgeführt, die im Eltern-Image definiert wurden. Nützlich für Boilerplates, aber intransparent ("Magic code execution"). Wird heute selten genutzt.Warum
EXPOSE?Es ist nur Dokumentation für den Menschen. Es öffnet keinen Port! (Das machtdocker run -p). Aber Tools (wie Traefik) lesen es, um zu wissen, wo der Service läuft.Heredocs?
Seit BuildKit kannst du mehrzeilige Skripte sauber schreiben:RUN <<EOF ... EOF. Das machtRUN-Layer viel lesbarer als&& \-Ketten.