Docker Tutorial #5: Dockerfiles erstellen – Eigene Images bauen
Was ist ein Dockerfile?
Ein Dockerfile ist eine Textdatei mit Anweisungen, wie Docker ein Image erstellen soll. Es ist das „Rezept“ für Ihr Container-Image.
Analogie: Ein Dockerfile ist wie ein Kochrezept:
- Zutaten (Base Image, Dependencies)
- Schritte (RUN, COPY Befehle)
- Endprodukt (Fertiges Image)
Ihr erstes Dockerfile
Erstellen Sie eine Datei namens Dockerfile (ohne Endung):
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80
Erstellen Sie index.html:
<h1>Mein erstes Docker-Image!</h1>
Image bauen:
docker build -t meine-webseite:v1 .
Container starten:
docker run -d -p 8080:80 meine-webseite:v1
Besuchen Sie http://localhost:8080
Die wichtigsten Dockerfile-Befehle
FROM – Base Image wählen
# Offizielles Nginx-Image
FROM nginx:alpine
# Node.js 18
FROM node:18-alpine
# Python 3.11
FROM python:3.11-slim
# Ubuntu 22.04
FROM ubuntu:22.04
Best Practice: Verwenden Sie Alpine-Versionen für kleinere Images.
WORKDIR – Arbeitsverzeichnis setzen
FROM node:18-alpine
WORKDIR /app
# Alle folgenden Befehle laufen in /app
COPY – Dateien kopieren
# Einzelne Datei
COPY package.json /app/
# Mehrere Dateien
COPY package.json package-lock.json /app/
# Verzeichnis kopieren
COPY ./src /app/src
# Alles kopieren
COPY . /app/
RUN – Befehle ausführen
# Pakete installieren
RUN apt-get update && apt-get install -y curl
# Node Packages installieren
RUN npm install
# Mehrere Befehle kombinieren
RUN apt-get update && \
apt-get install -y curl wget && \
rm -rf /var/lib/apt/lists/*
Wichtig: Kombinieren Sie RUN-Befehle mit && um Layer zu reduzieren.
ENV – Umgebungsvariablen
ENV NODE_ENV=production
ENV PORT=3000
ENV DATABASE_URL=mysql://db:3306/mydb
EXPOSE – Ports dokumentieren
EXPOSE 80
EXPOSE 3000
EXPOSE 8080 8443
Hinweis: EXPOSE öffnet Ports nicht wirklich, es ist nur Dokumentation.
CMD – Standard-Befehl
# Shell-Form
CMD npm start
# Exec-Form (bevorzugt)
CMD ["npm", "start"]
# Nur ein CMD pro Dockerfile!
ENTRYPOINT – Haupt-Executable
ENTRYPOINT ["python", "app.py"]
# Mit CMD für Default-Argumente
ENTRYPOINT ["python"]
CMD ["app.py"]
Praktische Beispiele
Node.js-Anwendung
FROM node:18-alpine
WORKDIR /app
# Dependencies zuerst (für besseres Caching)
COPY package*.json ./
RUN npm ci --only=production
# Dann Source Code
COPY . .
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "server.js"]
PHP mit Apache
FROM php:8.2-apache
# PHP Extensions installieren
RUN docker-php-ext-install pdo pdo_mysql mysqli
# Apache Rewrite aktivieren
RUN a2enmod rewrite
# Code kopieren
COPY ./src /var/www/html/
# Berechtigungen setzen
RUN chown -R www-data:www-data /var/www/html
EXPOSE 80
Python Flask-App
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
CMD ["flask", "run", "--host=0.0.0.0"]
Image bauen
# Basis-Build
docker build -t mein-image .
# Mit Tag
docker build -t mein-image:1.0.0 .
# Mit mehreren Tags
docker build -t mein-image:1.0.0 -t mein-image:latest .
# Ohne Cache
docker build --no-cache -t mein-image .
# Mit Build-Argumenten
docker build --build-arg VERSION=1.0 -t mein-image .
# Anderes Dockerfile
docker build -f Dockerfile.prod -t mein-image:prod .
.dockerignore erstellen
Erstellen Sie .dockerignore um Dateien auszuschließen:
node_modules
npm-debug.log
.git
.env
*.md
Dockerfile
.dockerignore
dist
coverage
.vscode
.idea
__pycache__
*.pyc
Das beschleunigt den Build und reduziert die Image-Größe.
Build-Argumente (ARG)
FROM node:18-alpine
ARG VERSION=latest
ARG BUILD_DATE
LABEL version=$VERSION
LABEL build-date=$BUILD_DATE
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
Bauen mit Argumenten:
docker build \
--build-arg VERSION=1.2.3 \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
-t myapp:1.2.3 .
Layer-Caching verstehen
Docker cached jede Anweisung als Layer. Wenn eine Layer sich ändert, müssen alle folgenden neu gebaut werden.
Schlechtes Beispiel:
FROM node:18-alpine
COPY . /app
RUN npm install
Jede Code-Änderung triggert npm install neu!
Gutes Beispiel:
FROM node:18-alpine
COPY package*.json /app/
RUN npm install
COPY . /app
npm install läuft nur neu, wenn package.json sich ändert.
Best Practices
1. Minimale Base Images
# Groß (>1GB)
FROM node:18
# Klein (~150MB)
FROM node:18-alpine
2. Multi-Stage Builds (Preview)
# Build Stage
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
# Production Stage
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
3. Nicht-Root-User
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
COPY --chown=nodejs:nodejs . .
USER nodejs
CMD ["node", "server.js"]
4. Labels für Metadaten
LABEL maintainer="email@example.com"
LABEL version="1.0"
LABEL description="Meine Anwendung"
5. Aufräumen in der gleichen Layer
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Troubleshooting
Build schlägt fehl
# Verbose Output
docker build --progress=plain -t myapp .
# Bestimmte Stage bauen
docker build --target builder -t myapp:builder .
Image zu groß
# Image-Größe prüfen
docker images
# Layer-Historie anschauen
docker history mein-image:latest
Cache-Probleme
# Ohne Cache neu bauen
docker build --no-cache -t myapp .
# Nur ab bestimmter Stage ohne Cache
docker build --no-cache --target production -t myapp .
Zusammenfassung
- Dockerfiles sind Bauanleitungen für Images
- Layer-Caching nutzen für schnellere Builds
- Alpine-Images für kleinere Größen
- .dockerignore nicht vergessen
- Kombinieren Sie RUN-Befehle
- Non-Root-User verwenden

Author: Andreas Lang
Andreas Lang konzentriert sich seit zwei Jahrzehnten auf die Webentwicklung und Webdesign mit dem Schwerpunkt PHP, Laravel und Javascript und betreut seine Kunden mit Herz und Seele in allen Bereichen von Entwicklung, Design, Suchmaschinenoptimierung, IT-Recht, IT-Sicherheit etc.
