Von Code Sentinel, Technical Project Manager bei Java Fleet Systems Consulting


Production Docker

Was bisher geschah

Jetzt geht’s in die „echte Welt“ Production Docker: kleine, sichere Imagessichere DefaultsHealthchecks, Logs, Metriken und Deployment-Checks.


Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden

  • Multi-stage builds reduzieren Image-Größe und Angriffsfläche.
  • Security-Basics: non-root, least-privilege, read-only FS, secrets nicht ins Image backen.
  • Monitoring/Operations: Healthchecks (App + Compose), Logs, Metriken (Actuator/Prometheus).
  • Deployment-Readiness: Ressourcen-Limits, Neustart-Policies, SBOM/Scan, Version-Pinning & Immutability.

Moin! Code Sentinel hier – Zeit für den Realitäts-Check 🛡️

„Läuft bei mir“ ist nicht „Produktionsbereit“. Production Docker heißt: klein, sicher, beobachtbar. Alles andere ist Glückspiel. Lass uns das sauber bauen.


1) Multi‑Stage Build für die Personen‑API (Spring Boot)

Ziel: Build-Tool rausnur Runtime drin. Kleines, schnelles, sicheres Image.

app/Dockerfile (Multi‑Stage)

# --- Stage 1: Build (mit Maven) ---
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace
COPY pom.xml .
RUN mvn -q -B -e -DskipTests dependency:go-offline
COPY src ./src
RUN mvn -q -B -DskipTests package

# --- Stage 2: Runtime (schlank) ---
FROM eclipse-temurin:21-jre
# Alternative für minimalistischere Images:
# FROM gcr.io/distroless/java21-debian12

ENV APP_HOME=/opt/app
WORKDIR ${APP_HOME}

# Non-root User anlegen (least privilege)
RUN useradd --system --uid 10001 --create-home appuser
USER 10001:10001

# Nur das fette JAR übernehmen
COPY --from=build /workspace/target/persons-compose-demo-0.0.1-SNAPSHOT.jar app.jar

# Immutable defaults
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]

Warum so?

  • Stage 1 enthält Maven & Cache (bleibt im Build).
  • Stage 2 ist nur das JRE + App → kleineres Attack Surface.
  • USER 10001: kein root im Container.

Tipp: Mit Spring Boot Layered JARs (spring-boot:repackage -DskipTests) werden Libs/Classes getrennt gecacht → schnellere Rebuilds.


2) Security Best Practices (kompakt & wirksam)

  1. Non-root ausführen (USER 10001).
  2. Read-only root FS & tmpfs nur falls nötig.
  3. Capabilities droppen; in Dev meist nicht nötig.
  4. Secrets nie im Image backen: via Env, Docker Secrets, Vault.
  5. Minimal Base Image (JRE / distroless).
  6. Versions pinnen (Image-Tags, Dependencies).
  7. SBOM/Scan vor Deploy (z. B. Syft/Trivy/CycloneDX).

Compose-Härtung (Ausschnitt)

services:
  app:
    image: ghcr.io/dein-org/persons-app:1.0.0
    read_only: true
    tmpfs:
      - /tmp
    user: "10001:10001"
    cap_drop: ["ALL"]
    security_opt:
      - no-new-privileges:true
    environment:
      SPRING_PROFILES_ACTIVE: prod
      # Secrets NIE committen – Beispiel nur für Demo!
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/devdb
      SPRING_DATASOURCE_USERNAME: devuser
      SPRING_DATASOURCE_PASSWORD: devpass

Für echte Secrets: Docker Swarm/K8s Secrets oder Vault/1Password; Env‑Vars gelten sonst als Configuration, nicht als Geheimnis.


3) Healthchecks, Restart‑Policies & Ressourcen‑Limits

App‑Health (Spring Boot)

application.yml (Prod‑Profil):

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      probes:
        enabled: true

Spring Boot liefert /actuator/health (+ liveness/readiness, wenn aktiviert).

Compose‑Healthcheck

services:
  app:
    healthcheck:
      test: ["CMD", "wget", "-qO", "-", "http://localhost:8080/actuator/health"]
      interval: 10s
      timeout: 3s
      retries: 10
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: "768M"
        reservations:
          cpus: "0.25"
          memory: "256M"

Restart‑Policy schützt vor Crash-Loops. Limits/Reservations sorgen für Planbarkeit und faire Ressourcennutzung.


4) Logs & Metriken – was in Produktion zählt

Logs: Standard‑Output, strukturiert (JSON oder pattern). In Compose/K8s übernimmt die Plattform das Sammeln.
Spring Boot Konfiguration (Beispiel Pattern statt Logback‑XML):

# application-prod.properties
logging.pattern.level=%5p
logging.pattern.console=%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} [%thread] %-5level %logger{36} - %msg%n

Metriken:

  • Aktiviere Spring Boot Actuator + Prometheus‑Exporter (Micrometer).
  • Sammle CPU, Heap, GC, HTTP‑Latenzen.
  • Visualisiere mit Grafana (optional ein Compose‑Profil „observability“ hinzufügen).

Beispiel Compose‑Erweiterung (optional):

services:
  prometheus:
    image: prom/prometheus:latest
    volumes: [./ops/prometheus.yml:/etc/prometheus/prometheus.yml:ro]
    ports: ["9090:9090"]

  grafana:
    image: grafana/grafana-oss:latest
    ports: ["3000:3000"]

5) Image‑Sicherheit & Lieferkette

  • SBOM erzeugen (z. B. mit Syft): Artefaktliste fürs Image.
  • Image scannen (z. B. mit Trivy): bekannte CVEs erkennen.
  • Signieren/Verifizieren (z. B. cosign): Supply‑Chain absichern.
  • Tagging‑Strategie: immutable tags (1.0.0, Git‑SHA), kein latest in Prod.

Quick‑Checks (lokal):

# SBOM
syft packages ghcr.io/dein-org/persons-app:1.0.0 -o cyclonedx-json > sbom.json

# Vulnerability Scan
trivy image --exit-code 1 --severity HIGH,CRITICAL ghcr.io/dein-org/persons-app:1.0.0

# Sign (Beispiel)
cosign sign ghcr.io/dein-org/persons-app:1.0.0
cosign verify ghcr.io/dein-org/persons-app:1.0.0

6) Deployment‑Varianten

  • Compose (Prod‑Light): Kleine Teams, Ein‑/Zwei‑Host‑Deployments.
  • Swarm: Secrets/Configs/Rolling Updates (einfacher als K8s).
  • Kubernetes: Standard ab Teamgröße N; Health/Scaling/Ingress nativ – aber auch mehr Komplexität.
  • GitOps: Manifeste versionieren, Deployments über Pull‑Requests.

Empfehlung: Starte mit Compose Prod‑Light, automatisiere Builds/Scans, nutze immutable Tags, beobachte Metriken. Wächst das System → K8s evaluieren.


7) Beispiel: Prod‑Overrides für unsere Personen‑API

docker-compose.prod.yml (Auszug)

services:
  app:
    image: ghcr.io/dein-org/persons-app:1.0.0
    environment:
      SPRING_PROFILES_ACTIVE: prod
    read_only: true
    tmpfs: ["/tmp"]
    user: "10001:10001"
    cap_drop: ["ALL"]
    security_opt: ["no-new-privileges:true"]
    healthcheck:
      test: ["CMD", "wget", "-qO", "-", "http://localhost:8080/actuator/health"]
      interval: 10s
      timeout: 3s
      retries: 10
    deploy:
      resources:
        limits: { cpus: "1.0", memory: "768M" }
        reservations: { cpus: "0.25", memory: "256M" }

Run:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

FAQ – Häufige Fragen

F1: Distroless oder JRE‑Image?
Distroless ist kleiner/sicherer (kein Paketmanager/Shell), Debugging aber schwieriger. Für Prod prima, für Dev oft JRE‑Image.

F2: Reicht ein Healthcheck auf /actuator/health?
Für Start/Readiness ja. Für tiefere Checks (DB, externe Services) eigene Health‑Contributor nutzen.

F3: Wie gehe ich mit Secrets um?
Nicht im Image, nicht im Repo. Nutze Docker/K8s Secrets oder Vault; lokal nur Platzhalter.

F4: Warum kein latest?
Nicht reproduzierbar. Verwende immutable Tags (Semver, Commit‑SHA).

F5: Was ist der schnellste Security‑Boost?
Non‑root, read‑only FS, cap_drop=ALL, kleines Base‑Image, regelmäßiger Trivy‑Scan.


Teaser & Projekt‑Verweis

Damit du loslegen kannst: Teil 2 Projekt (Persons‑API + Compose) ist bereit.
Für Prod‑Tests: Ersetze das Dockerfile im Projekt durch die Multi‑Stage‑Variante und ergänze die Compose‑Overrides.

📦 Projekt (Teil 2): persons-compose-demo.zip
👉 Download hier

Nächste Schritte (Code Sentinel‑Checkliste):

  • [ ] Multi‑Stage Dockerfile integrieren
  • [ ] Non‑root + read‑only + cap_drop aktivieren
  • [ ] Healthchecks + Limits setzen
  • [ ] SBOM/Trivy in CI einbauen
  • [ ] Immutable Tags + Registry‑Policies

Tags: #Docker #Security #MultiStageBuild #Monitoring #SpringBoot #DevOps #SBOM

Autor

  • Code Sentinel

    32 Jahre alt, Technical Project Manager und Security-Experte bei Java Fleet Systems Consulting. Code ist ein erfahrener Entwickler, der in die Projektleitung aufgestiegen ist, aber immer noch tief in der Technik verwurzelt bleibt. Seine Mission: Sicherstellen, dass Projekte termingerecht, sicher und wartbar geliefert werden.