
von Zelkulon19. März 20251 min Lesezeit
Docker und Docker Compose für Microservices richtig einsetzen: Multistage Builds, Service-Discovery, Healthchecks und eine vollständige lokale Entwicklungsumgebung.
Die Zielarchitektur
┌─────────────────────────────────────────────────────────────┐ │ docker-compose.yml │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Next.js │ │ Order │ │ Payment │ │ │ │ :3000 │ │ Service │ │ Service │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ └───────────────┴───────────────┘ │ │ Bridge-Network │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │PostgreSQL│ │ RabbitMQ │ │ Redis │ │ │ │ :5432 │ │ :5672 │ │ :6379 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘
Multistage Dockerfile (Spring Boot)
# ─── Stage 1: Build ────────────────────────────────────────────
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY .mvn .mvn
COPY mvnw .
RUN ./mvnw dependency:go-offline -B # Layer-Cache für Dependencies
COPY src src
RUN ./mvnw package -DskipTests -B
RUN java -Djarmode=layertools -jar target/*.jar extract --destination target/layers
# ─── Stage 2: Runtime (~150 MB statt ~600 MB) ──────────────────
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
COPY --from=build /app/target/layers/dependencies/ ./
COPY --from=build /app/target/layers/spring-boot-loader/ ./
COPY --from=build /app/target/layers/application/ ./
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 8081
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s CMD wget -qO- http://localhost:8081/actuator/health || exit 1
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]Docker Compose
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASS:-secret}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
retries: 5
rabbitmq:
image: rabbitmq:3.13-management-alpine
ports:
- "5672:5672"
- "15672:15672" # Management UI
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "ping"]
interval: 30s
order-service:
build: ./order-service
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orderdb
SPRING_RABBITMQ_HOST: rabbitmq
ports:
- "8081:8081"
depends_on:
postgres:
condition: service_healthy # Erst starten wenn DB bereit!
rabbitmq:
condition: service_healthy
restart: unless-stopped
volumes:
postgres_data:Nützliche Kommandos
# Alle Services starten
docker compose up -d
# Nur Infrastructure (für lokale Entwicklung)
docker compose up -d postgres rabbitmq redis
# Logs eines Services
docker compose logs -f order-service
# Service neu bauen
docker compose up -d --build order-service
# Status aller Services
docker compose ps
# In Container Shell
docker compose exec order-service sh
# Alles stoppen + Volumes löschen (Datenbankdaten zurücksetzen)
docker compose down -vStartsequenz mit Healthchecks
1. postgres, rabbitmq, redis starten
│
▼
2. Healthchecks (pg_isready, rabbit ping, redis ping)
│ alle healthy
▼
3. order-service, payment-service starten
│
▼
4. Healthchecks (actuator/health)
│ alle healthy
▼
5. Frontend startet
Ohne condition: service_healthy → Spring startet bevor
PostgreSQL bereit ist → Verbindungsfehler!Fazit
- Konsistenz: "Works on my machine" gehört der Vergangenheit an
- Isolierung: Services laufen ohne gegenseitige Abhängigkeiten
- Schnelles Onboarding: docker compose up – fertig
- Produktionsparität: Lokale Umgebung entspricht exakt dem Deployment
- Skalierbarkeit: docker compose up --scale order-service=3