Docker für Anfänger: Container leicht gemacht
· 12 Min. Lesezeit
Inhaltsverzeichnis
- Was ist Docker?
- Container vs. Virtuelle Maschinen
- Grundlegende Docker-Konzepte
- Wichtige Docker-Befehle
- Dockerfiles schreiben
- Docker Compose für Multi-Container-Apps
- Docker-Netzwerke und Volumes
- Docker Best Practices
- Häufige Fallstricke und wie man sie vermeidet
- Praxisbeispiele
- Häufig gestellte Fragen
- Verwandte Artikel
Was ist Docker?
Docker ist eine Plattform, mit der Sie Anwendungen und ihre Abhängigkeiten in leichtgewichtige, portable Container verpacken können. Stellen Sie sich einen Container als eine winzige, eigenständige Box vor, die alles enthält, was Ihre App zum Laufen braucht: Code, Laufzeitumgebung, Bibliotheken und Systemwerkzeuge. Keine „funktioniert auf meinem Rechner"-Probleme mehr.
Vor Docker bedeutete das Bereitstellen von Software, Abhängigkeiten manuell zu installieren, Server zu konfigurieren und zu hoffen, dass nichts kollidiert. Sie verbrachten Stunden damit, Python-Versionen, Node.js-Pakete, Datenbanktreiber und Systembibliotheken einzurichten. Dann machten Sie alles noch einmal auf Staging- und Produktionsservern und beteten, dass die Konfigurationen übereinstimmten.
Docker beseitigt dieses Chaos, indem es sicherstellt, dass Ihre App überall identisch läuft – auf Ihrem Laptop, dem Rechner eines Teamkollegen, Staging-Servern oder in der Produktion. Der Container wird zur Bereitstellungseinheit, nicht der Anwendungscode allein.
Docker ist zum Standardwerkzeug für moderne Softwareentwicklung geworden. Ob Sie Microservices entwickeln, CI/CD-Pipelines einrichten oder einfach nur eine konsistente Entwicklungsumgebung wollen, Docker macht es mit minimalem Aufwand möglich. Unternehmen wie Netflix, Spotify und PayPal betreiben täglich Millionen von Containern in der Produktion.
Schneller Tipp: Docker ist nicht nur für Produktionsbereitstellungen. Viele Entwickler nutzen es, um zu vermeiden, dass ihr lokaler Rechner mit verschiedenen Sprachversionen, Datenbanken und Tools überladen wird. Brauchen Sie PostgreSQL für ein Projekt und MySQL für ein anderes? Führen Sie beide in Containern ohne Konflikte aus.
Container vs. Virtuelle Maschinen
Container und virtuelle Maschinen bieten beide Isolation, funktionieren aber unter der Haube sehr unterschiedlich. Diesen Unterschied zu verstehen ist entscheidend, um zu schätzen, warum Container so populär geworden sind.
Virtuelle Maschinen führen ein vollständiges Betriebssystem mit eigenem Kernel auf einem Hypervisor aus. Jede VM benötigt ihre eigene OS-Installation, verbraucht Gigabytes an Speicherplatz und erheblichen Arbeitsspeicher. Startzeiten werden in Minuten gemessen. Wenn Sie drei VMs ausführen, führen Sie drei vollständige Betriebssysteme gleichzeitig aus.
Container teilen sich den Host-OS-Kernel und verpacken nur die Anwendungsschicht. Sie sind Megabytes groß (nicht Gigabytes), starten in Sekunden (nicht Minuten), und Sie können Dutzende auf einem einzigen Rechner ausführen, ohne ins Schwitzen zu kommen.
# VM-Ansatz: Jede App bekommt ein vollständiges OS
App A → Gast-OS → Hypervisor → Host-OS → Hardware
App B → Gast-OS → Hypervisor → Host-OS → Hardware
# Container-Ansatz: Apps teilen sich den Kernel
App A → Container-Laufzeit → Host-OS → Hardware
App B → Container-Laufzeit → Host-OS → Hardware
Diese leichtgewichtige Architektur macht Container ideal für Microservices, wo Sie möglicherweise Hunderte kleiner Dienste statt einer monolithischen Anwendung ausführen. Die Ressourceneffizienz ist verblüffend – ein Server, der 10 VMs ausführt, könnte bequem 100 Container ausführen.
| Merkmal | Virtuelle Maschinen | Container |
|---|---|---|
| Startzeit | Minuten | Sekunden |
| Speicherplatz | Gigabytes (vollständiges OS) | Megabytes (nur App-Schicht) |
| Leistung | Nahezu nativ | Nativ (kein Hypervisor-Overhead) |
| Isolation | Vollständig (separater Kernel) | Prozessebene (gemeinsamer Kernel) |
| Portabilität | Begrenzt (hypervisor-abhängig) | Hoch (läuft überall, wo Docker läuft) |
| Ressourcenverbrauch | Schwer | Leichtgewichtig |
Dennoch sind VMs nicht veraltet. Sie bieten stärkere Isolation, da jede VM ihren eigenen Kernel hat. Für sicherheitskritische Workloads oder wenn Sie verschiedene Betriebssysteme ausführen müssen, bleiben VMs die bessere Wahl. Viele Organisationen nutzen beides: VMs für Infrastrukturisolation und Container für Anwendungsbereitstellung.
Grundlegende Docker-Konzepte
Docker hat einige Schlüsselkonzepte, die Sie verstehen müssen, bevor Sie in Befehle und Dockerfiles eintauchen. Diese Bausteine bilden die Grundlage dafür, wie Docker funktioniert.
Images
Ein Docker-Image ist eine schreibgeschützte Vorlage, die Ihren Anwendungscode, die Laufzeitumgebung, Bibliotheken und Abhängigkeiten enthält. Betrachten Sie es als einen Snapshot oder eine Blaupause. Images werden aus Anweisungen in einem Dockerfile erstellt und in Registries wie Docker Hub gespeichert.
Images bestehen aus Schichten. Jede Anweisung in einem Dockerfile erstellt eine neue Schicht. Docker cached diese Schichten, sodass Docker die gecachten Schichten wiederverwendet, wenn Sie ein Image neu erstellen und sich nur die letzte Schicht geändert hat. Das macht Builds unglaublich schnell.
Container
Ein Container ist eine laufende Instanz eines Images. Sie können mehrere Container aus demselben Image erstellen, und jeder läuft isoliert. Wenn Sie einen Container stoppen, gehen alle darin vorgenommenen Änderungen verloren, es sei denn, Sie speichern sie explizit oder verwenden Volumes.
Container sind von Natur aus kurzlebig. Diese Unveränderlichkeit ist ein Feature, kein Bug – sie gewährleistet Konsistenz und macht Skalierung trivial.
Dockerfile
Ein Dockerfile ist eine Textdatei mit Anweisungen zum Erstellen eines Docker-Images. Es spezifiziert das Basis-Image, kopiert Ihren Code, installiert Abhängigkeiten und definiert, wie Ihre Anwendung ausgeführt wird. Wir werden in einem späteren Abschnitt tiefer in Dockerfiles eintauchen.
Docker Registry
Eine Registry ist ein Speicher- und Verteilungssystem für Docker-Images. Docker Hub ist die Standard-Public-Registry und hostet Millionen von Images. Sie können auch private Registries für proprietäre Anwendungen betreiben. Wenn Sie docker pull nginx ausführen, lädt Docker das nginx-Image von Docker Hub herunter.
Volumes
Volumes sind Dockers Mechanismus zur Datenpersistierung. Da Container kurzlebig sind, verschwinden alle in einem Container geschriebenen Daten, wenn er stoppt. Volumes ermöglichen es Ihnen, Daten außerhalb des Container-Dateisystems zu speichern, die Container-Neustarts und -Löschungen überdauern.
Profi-Tipp: Nutzen Sie unseren Docker-Befehlsgenerator, um schnell komplexe Docker-Befehle zu erstellen, ohne sich alle Flags und Optionen merken zu müssen.
Wichtige Docker-Befehle
Lassen Sie uns die Docker-Befehle durchgehen, die Sie täglich verwenden werden. Diese decken den gesamten Container-Lebenszyklus ab, vom Pullen von Images bis zum Aufräumen von Ressourcen.
Arbeiten mit Images
# Ein Image von Docker Hub pullen
docker pull ubuntu:22.04
# Alle lokalen Images auflisten
docker images
# Ein Image entfernen
docker rmi ubuntu:22.04
# Ein Image aus einem Dockerfile erstellen
docker build -t myapp:1.0 .
# Ein Image für das Pushen in eine Registry taggen
docker tag myapp:1.0 username/myapp:1.0
# Ein Image in eine Registry pushen
docker push username/myapp:1.0
Container ausführen
# Einen Container im Vordergrund ausführen
docker run ubuntu:22.04 echo "Hallo Docker"
# Einen Container im Detached-Modus (Hintergrund) ausführen
docker run -d nginx
# Mit einem benutzerdefinierten Namen ausführen
docker run -d --name my-nginx nginx
# Mit Port-Mapping ausführen (Host:Container)
docker run -d -p 8080:80 nginx
# Mit Umgebungsvariablen ausführen
docker run -d -e POSTGRES_PASSWORD=secret postgres
# Mit einem Volume-Mount ausführen
docker run -d -v /host/pfad:/container/pfad nginx
# Interaktiv mit einer Shell ausführen
docker run -it ubuntu:22.04 /bin/bash
Container verwalten
# Laufende Container auflisten
docker ps
# Alle Container auflisten (einschließlich gestoppter)
docker ps -a
# Einen Container stoppen
docker stop my-nginx
# Einen gestoppten Container starten
docker start my-nginx
# Einen Container neu starten
docker restart my-nginx
# Einen Container entfernen
docker rm my-nginx
# Einen laufenden Container entfernen (erzwingen)
docker rm -f my-nginx
# Container-Logs anzeigen
docker logs my-nginx
# Logs in Echtzeit verfolgen
docker logs -f my-nginx
# Einen Befehl in einem laufenden Container ausführen
docker exec my-nginx ls /usr/share/nginx/html
# Eine Shell in einem laufenden Container öffnen
docker exec -it my-nginx /bin/bash
# Container-Ressourcenverbrauch anzeigen
docker stats
# Container-Details inspizieren
docker inspect my-nginx
Aufräumbefehle
# Alle gestoppten Container entfernen
docker container prune
# Ungenutzte Images entfernen
docker image prune
# Ungenutzte Volumes entfernen
docker volume prune
# Alles Ungenutzte entfernen (Container, Images, Netzwerke, Volumes)
docker system prune -a
Die -it-Flags verdienen besondere Erwähnung. -i hält STDIN offen (interaktiv), und -t weist ein Pseudo-TTY (Terminal) zu. Zusammen ermöglichen sie Ihnen, mit dem Container wie mit einer normalen Shell-Sitzung zu interagieren.
Schneller Tipp: Verwenden Sie docker ps -q, um nur Container-IDs zu erhalten, was für Skripting nützlich ist. Zum Beispiel stoppt docker stop $(docker ps -q) alle laufenden Container.
Dockerfiles schreiben
Ein Dockerfile ist der Ort, an dem die Magie passiert. Es ist ein Rezept zum Erstellen Ihres Docker-Images und spezifiziert genau, was in Ihren Container kommt. Lassen Sie uns die wichtigsten Anweisungen und Best Practices aufschlüsseln.
Grundlegende Dockerfile-Struktur
# Von einem Basis-Image starten
FROM node:18-alpine
# Das Arbeitsverzeichnis festlegen
WORKDIR /app
# Paketdateien kopieren
COPY package*.json ./
# Abhängigkeiten installieren
RUN npm install
# Anwendungscode kopieren
COPY . .
# Den Port freigeben, auf dem Ihre App läuft
EXPOSE 3000
# Den Befehl zum Ausführen Ihrer App definieren
CMD ["node", "server.js"]
Wichtige Dockerfile-Anweisungen
FROM spezifiziert das Basis-Image. Verwenden Sie immer spezifische Versions-Tags (wie node:18-alpine) anstelle von latest, um reproduzierbare Builds zu gewährleisten. Alpine-Varianten sind kleiner und sicherer.
WORKDIR setzt das Arbeitsverzeichnis für nachfolgende Anweisungen. Es erstellt das Verzeichnis, falls es nicht existiert. Das ist sauberer als RUN cd /app zu verwenden.
COPY kopiert Dateien von Ihrem Host-Rechner in das Image. Die Syntax ist COPY Quelle Ziel. Verwenden Sie COPY . ., um alles zu kopieren, aber achten Sie darauf, was Sie einschließen (verwenden Sie .dockerignore).
RUN führt Befehle während des Build-Prozesses aus. Jede RUN-Anweisung erstellt eine neue Schicht. Verketten Sie Befehle mit &&, um Schichten zu reduzieren:
# Schlecht: Erstellt 3 Schichten
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# Gut: Erstellt 1 Schicht
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
EXPOSE dokumentiert, auf welchem Port Ihre Anwendung lauscht. Es veröffentlicht den Port nicht tatsächlich – das geschieht mit docker run -p.
CMD spezifiziert den Standardbefehl, der beim Start des Containers ausgeführt wird. Verwenden Sie das JSON-Array-Format (["executable", "param1"]), um Shell-Verarbeitungsprobleme zu vermeiden.
ENTRYPOINT ist ähnlich wie CMD, aber schwerer zu überschreiben. Verwenden Sie es, wenn Sie möchten, dass sich Ihr Container wie eine ausführbare Datei verhält. Sie können ENTRYPOINT und CMD für flexible Standardwerte kombinieren.
Multi-Stage-Builds
Multi-Stage-Builds ermöglichen es Ihnen, mehrere FROM-Anweisungen in einem Dockerfile zu verwenden. Das ist unglaublich leistungsstark, um kleine Produktions-Images zu erstellen und gleichzeitig Build-Tools getrennt zu halten.
# Build-Stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Produktions-Stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
Das Produktions-Image enthält nur den kompilierten Code und Laufzeit-Abhängigkeiten, nicht die Build-Tools. Das kann die Image-Größe um 70% oder mehr reduzieren.
.dockerignore-Datei
Erstellen Sie eine .dockerignore-Datei, um Dateien vom Build-Kontext auszuschließen. Das beschleunigt Builds und reduziert die Image-Größe.
node_modules
npm-debug.log
.git
.env
*.md
.DS_Store
coverage
.vscode