Docker für Entwickler: Images, Container und Compose
Du hast eine App gebaut, die auf deinem Rechner funktioniert, aber beim Deployment bricht alles zusammen. Abhängigkeiten fehlen, Versionen unterscheiden sich, und die Konfiguration ist ein Chaos. Docker löst dieses Problem, indem es deine Anwendung und ihre Umgebung in eine einzige, portable Einheit packt. Dieser Artikel erklärt Docker-Images, Container und Docker Compose mit praktischen Beispielen, damit du deinen Entwicklungs-Workflow optimieren kannst.
Was ist ein Docker-Image?
Ein Docker-Image ist eine schreibgeschützte Vorlage, die deinen Anwendungscode, die Laufzeitumgebung, Bibliotheken, Umgebungsvariablen und Konfigurationsdateien enthält. Betrachte es als einen Schnappschuss der Umgebung deiner App. Images werden aus einem Dockerfile gebaut, einer Textdatei mit Anweisungen.
Hier ist ein einfaches Dockerfile für eine Node.js-App:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Jede Anweisung erzeugt eine Ebene (Layer). Layer werden gecacht, sodass ein Neubau nach einer Codeänderung nur die Schritte ab der geänderten Ebene erneut ausführt. Das macht Builds schnell.
Was ist ein Docker-Container?
Ein Container ist eine laufende Instanz eines Images. Er ist ein isolierter Prozess mit eigenem Dateisystem, Netzwerk und Prozessraum, teilt aber den Kernel des Host-Betriebssystems. Dadurch sind Container im Vergleich zu virtuellen Maschinen leichtgewichtig und schnell zu starten.
Um einen Container aus einem Image zu starten:
docker run -d -p 3000:3000 --name myapp my-node-app
Dies startet einen Container im Detached-Modus (-d), mappt Port 3000 auf dem Host auf Port 3000 im Container (-p 3000:3000) und benennt ihn myapp.
Container sind standardmäßig flüchtig. Alle darin geschriebenen Daten gehen verloren, wenn der Container stoppt. Um Daten zu persistieren, verwende Volumes:
docker run -v /host/data:/app/data my-node-app
Warum Docker Compose verwenden?
Echte Anwendungen benötigen oft mehrere Dienste: einen Webserver, eine Datenbank, einen Cache usw. Jeden einzelnen mit docker run zu starten und manuell zu verknüpfen, ist mühsam. Docker Compose ermöglicht es dir, Multi-Container-Apps mit einer einzigen YAML-Datei zu definieren und auszuführen.
Hier ist eine docker-compose.yml für eine Node.js-App mit PostgreSQL und Redis:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
cache:
image: redis:7
volumes:
pgdata:
Starte alles mit:
docker compose up -d
Compose erstellt ein Netzwerk, damit Dienste sich gegenseitig über ihren Namen erreichen können (z. B. db, cache). Es übernimmt auch die Volume-Erstellung und die Abhängigkeitsreihenfolge.
Wichtige Docker-Befehle für Entwickler
docker build -t myapp .– Ein Image aus einem Dockerfile im aktuellen Verzeichnis bauen.docker images– Lokale Images auflisten.docker ps– Laufende Container auflisten (mit-afür alle).docker exec -it myapp sh– Eine Shell in einem laufenden Container öffnen.docker logs myapp– Container-Logs anzeigen.docker compose down– Container, Netzwerke und Volumes, die in Compose definiert sind, stoppen und entfernen.
Best Practices für Docker in der Entwicklung
- Verwende .dockerignore, um node_modules, .git und andere unnötige Dateien aus dem Build-Kontext auszuschließen.
- Nutze Multi-Stage-Builds, um Produktions-Images klein zu halten. Baue deine App beispielsweise in einer Stage und kopiere nur die Artefakte in ein minimales Runtime-Image.
- Pinn Versionen in deinem Dockerfile (z. B.
node:18-alpinestattnode:latest) für reproduzierbare Builds. - Verwende Volumes für Code während der Entwicklung für Hot Reloading:
volumes: - .:/appin Compose. - Nicht als Root ausführen in der Produktion. Erstelle einen Nicht-Root-Benutzer in deinem Dockerfile.
Häufige Fallstricke und wie man sie vermeidet
Fallstrick 1: Aufgeblähte Images. Die Verwendung eines vollständigen OS-Basis-Images wie ubuntu kann zu Images von 1 GB+ führen. Verwende Slim- oder Alpine-Varianten.
Fallstrick 2: Langsame Builds. Ordne die Dockerfile-Anweisungen von am wenigsten zu am häufigsten geänderten. Kopiere Paketdateien und installiere Abhängigkeiten, bevor du den Quellcode kopierst.
Fallstrick 3: Datenverlust. Verwende immer Volumes für Datenbanken. Ohne sie verschwinden die Daten, wenn der Container entfernt wird.
Fallstrick 4: Netzwerk-Verwirrung. In Compose kommunizieren Dienste über ein Standardnetzwerk, wobei Dienstnamen als Hostnamen verwendet werden. Verwende nicht localhost, um einen anderen Dienst zu erreichen.
FAQ
Was ist der Unterschied zwischen einem Image und einem Container?
Ein Image ist eine schreibgeschützte Vorlage; ein Container ist eine laufende Instanz dieses Images. Du kannst mehrere Container aus demselben Image ausführen.
Kann ich Docker Compose in der Produktion verwenden?
Compose ist in erster Linie für Entwicklung und Tests gedacht. Für die Produktion solltest du Orchestrierungstools wie Kubernetes oder Docker Swarm in Betracht ziehen, obwohl Compose für einfache Deployments funktionieren kann.
Wie aktualisiere ich einen Container mit neuem Code?
Baue das Image mit docker build neu und erstelle dann den Container neu: docker compose up -d --build.
Bereit, deinen Entwicklungs-Workflow zu optimieren? Probiere unseren Nginx Log Analyzer, um die Logs deines containerisierten Webservers schnell zu debuggen.