- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
- Database per Service gibt jedem Microservice seine eigene Datenbank -- volle Autonomie, unabhaengige Deployments und Technology Freedom, aber Datenkonsistenz wird zur Herausforderung
- Shared Database ist einfacher zu starten, fuehrt aber zu enger Kopplung: Schema-Aenderungen betreffen alle Services, Deployments muessen koordiniert werden
- Eventual Consistency ersetzt starke Konsistenz: Services kommunizieren Aenderungen ueber Events, Lesezugriffe koennen kurzzeitig veraltete Daten liefern
- CQRS trennt Schreib- und Lese-Modelle -- optimierte Read-Stores fuer komplexe Abfragen ueber Service-Grenzen hinweg
- StatefulSets sind das Kubernetes-Primitiv fuer Datenbanken: stabile Netzwerk-Identitaet, geordnetes Scaling und persistente Volumes
Kubernetes Database per Service: Datenbank-Strategien fuer Microservices
Die Entscheidung fuer eine Datenbank-Strategie ist eine der folgenreichsten Architektur-Entscheidungen in einer Microservices-Landschaft. Sie beeinflusst Team-Autonomie, Deployment-Geschwindigkeit, Datenkonsistenz und operativen Aufwand.
Dieser Artikel vergleicht Database per Service und Shared Database mit ihren Trade-offs, zeigt wie Eventual Consistency und CQRS die Herausforderungen loesen, und liefert produktionsreife Kubernetes-Manifeste fuer Datenbanken als StatefulSets.
Das Spektrum: Shared Database bis Database per Service
Tight Coupling Loose Coupling
| |
v v
+------------------+ +------------------+ +------------------+
| Shared Database | | Schema per | | Database per |
| (eine DB, ein | | Service (eine | | Service (eigene |
| Schema) | | DB, getrennte | | DB-Instanz pro |
| | | Schemas) | | Service) |
+------------------+ +------------------+ +------------------+
Die meisten Teams starten mit einer Shared Database und migrieren schrittweise Richtung Database per Service. Getrennte Schemas in derselben Instanz bieten einen pragmatischen Zwischenschritt.
Shared Database: Einfach, aber riskant
Vorteile und Nachteile
| Aspekt | Bewertung |
|---|---|
| ACID-Transaktionen | JOINs und Foreign Keys funktionieren ueber Service-Grenzen |
| Setup | Eine Datenbank deployen, fertig |
| Schema-Kopplung | Aenderungen an Tabellen betreffen alle Services |
| Deployment-Kopplung | Schema-Migrationen erfordern koordinierte Releases |
| Skalierung | Alle Services teilen sich CPU, Memory und IOPS |
| Technology Lock-in | Alle Services nutzen dieselbe Datenbank-Technologie |
Kubernetes Deployment: Shared PostgreSQL
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres-shared
namespace: shop
spec:
serviceName: postgres-shared
replicas: 1
selector:
matchLabels:
app: postgres-shared
template:
metadata:
labels:
app: postgres-shared
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
- name: POSTGRES_DB
value: "shopdb"
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 4Gi
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
Database per Service: Volle Autonomie
Jeder Service waehlt die optimale Datenbank-Technologie: PostgreSQL fuer relationale Daten, MongoDB fuer flexible Dokumente, Redis fuer Caches, TimescaleDB fuer Zeitreihen.
+------------------+ +------------------+ +------------------+
| Order Service | | Inventory Service| | Customer Service |
+--------+---------+ +--------+---------+ +--------+---------+
| | |
+-----v------+ +-----v------+ +------v-----+
| orders-db | | inventory | | customers |
| PostgreSQL | | MongoDB | | PostgreSQL |
+------------+ +------------+ +------------+
StatefulSet: Orders Database
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: orders-db
namespace: shop
spec:
serviceName: orders-db
replicas: 3
selector:
matchLabels:
app: orders-db
template:
metadata:
labels:
app: orders-db
service: order-service
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- orders-db
topologyKey: kubernetes.io/hostname
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_DB
value: "orders"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: orders-db-credentials
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: orders-db-credentials
key: password
volumeMounts:
- name: orders-data
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumeClaimTemplates:
- metadata:
name: orders-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: Service
metadata:
name: orders-db
namespace: shop
spec:
selector:
app: orders-db
ports:
- port: 5432
targetPort: 5432
clusterIP: None
Pod Anti-Affinity stellt sicher, dass Datenbank-Replicas auf unterschiedlichen Nodes laufen. Das schuetzt vor Node-Ausfaellen. Fuer weitergehende Backup-Strategien: Kubernetes Backup und Disaster Recovery.
Eventual Consistency: Der Preis der Entkopplung
Wenn jeder Service seine eigene Datenbank hat, gibt es keine JOINs ueber Service-Grenzen. Datenkonsistenz wird durch Events hergestellt -- mit einer kurzen Verzoegerung.
1. Order Service: INSERT INTO orders (customer, items) ...
2. Order Service: PUBLISH Event "OrderCreated" an Kafka
3. Inventory Service: CONSUME "OrderCreated"
4. Inventory Service: UPDATE inventory SET stock = stock - 1
Zwischen Schritt 2 und 4: Eventual Consistency
(Order existiert, aber Lagerbestand noch nicht aktualisiert)
Konsistenz-Fenster minimieren mit KEDA
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: inventory-consumer-scaler
namespace: shop
spec:
scaleTargetRef:
name: inventory-service
minReplicaCount: 2
maxReplicaCount: 10
triggers:
- type: kafka
metadata:
bootstrapServers: "kafka-0.kafka.shop.svc.cluster.local:9092"
consumerGroup: "inventory-service"
topic: "order-events"
lagThreshold: "10"
Mit KEDA skaliert der Inventory Service automatisch, wenn Events sich stauen. Mehr dazu: Kubernetes KEDA Autoscaling.
CQRS: Getrennte Lese- und Schreib-Modelle
CQRS loest das Problem von Abfragen, die Daten aus mehreren Services benoetigen. Statt JOINs ueber Service-Grenzen baut man spezialisierte Read-Stores.
Schreib-Seite (Commands) Lese-Seite (Queries)
+------------------+ +------------------+
| Order Service |--Events*/} | Order Query |
| (PostgreSQL) | | (Elasticsearch) |
+------------------+ +------------------+
+------------------+ +------------------+
| Customer Service |--Events*/} | Dashboard Query |
| (PostgreSQL) | | (Redis) |
+------------------+ +------------------+
Event-Projektion Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-projector
namespace: shop
spec:
replicas: 2
selector:
matchLabels:
app: order-projector
template:
metadata:
labels:
app: order-projector
spec:
containers:
- name: projector
image: registry.example.com/order-projector:v1.2.0
env:
- name: KAFKA_BROKERS
value: "kafka-0.kafka.shop.svc.cluster.local:9092"
- name: KAFKA_TOPICS
value: "order-events,customer-events,inventory-events"
- name: KAFKA_CONSUMER_GROUP
value: "order-projector"
- name: ELASTICSEARCH_URL
value: "http://order-query-store.shop.svc.cluster.local:9200"
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
Der Projector konsumiert Events aus mehreren Topics und baut eine denormalisierte Sicht im Elasticsearch-Cluster. Abfragen wie "Alle Bestellungen von Kunde X mit Lagerbestand-Status" werden ohne Cross-Service-Calls beantwortet.
Vergleich: Shared Database vs. Database per Service
| Kriterium | Shared Database | Database per Service |
|---|---|---|
| Konsistenz | Strong (ACID) | Eventual |
| Team-Autonomie | Niedrig | Hoch |
| Deployment-Unabhaengigkeit | Nein | Ja |
| Technology Freedom | Nein | Ja (Polyglot Persistence) |
| Operativer Aufwand | Niedrig (eine DB) | Hoch (viele DBs) |
| Skalierbarkeit | Vertikal (eine Instanz) | Horizontal (pro Service) |
| Cross-Service-Queries | SQL JOINs | CQRS oder API Composition |
Migration: Von Shared Database zu Database per Service
- Schema-Ownership definieren: Welcher Service besitzt welche Tabellen?
- APIs statt JOINs: Direct-Table-Zugriffe durch API-Calls ersetzen
- Separate Schemas: Tabellen in Service-eigene Schemas verschieben (gleiche DB)
- Event-Publishing: Services publizieren Aenderungen als Events
- Separate Instanzen: Schemas in eigene Datenbank-Instanzen migrieren
Fuer die richtige Deployment-Strategie waehrend der Migration: Kubernetes Deployment Strategien.
Best Practices
- Klein starten: Beginnen Sie mit getrennten Schemas in einer Datenbank, bevor Sie separate Instanzen einfuehren.
- Backup-Strategie pro Datenbank: Jede Instanz braucht eigene Backup-Schedules und Retention Policies.
- Connection Pooling: PgBouncer oder ProxySQL als Sidecar reduzieren Connection-Overhead.
- Resource Limits setzen: Datenbanken ohne Limits koennen einen gesamten Node destabilisieren.
- Monitoring pro Instanz: Jede Datenbank braucht eigene Alerts fuer Disk Usage, Connection Count und Replication Lag.
Fuer Service-Kommunikation zwischen den Datenbank-besitzenden Services: Kubernetes Dapr fuer Microservices.
Verwandte Artikel
- Kubernetes Dapr: Sidecar-Pattern fuer Microservices
- Kubernetes KEDA: Event-driven Autoscaling
- Kubernetes Deployment Strategien
- Kubernetes Backup und Disaster Recovery
- Kubernetes Health Checks und Probes
Sie planen die Datenbank-Strategie fuer Ihre Microservices-Architektur oder migrieren von einer Shared Database? Wir unterstuetzen Sie bei der Architektur-Entscheidung, StatefulSet-Konfiguration und dem produktiven Betrieb auf Kubernetes. Kontaktieren Sie uns unter /kontakt.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
CQRS Pattern auf Kubernetes: Read/Write-Trennung umsetzen
CQRS Pattern auf Kubernetes umsetzen: Getrennte Read/Write-Services deployen, Event-driven Synchronisation mit Kafka und unabhängiges Scaling per HPA.
Event Sourcing auf Kubernetes mit Kafka und CQRS
Event Sourcing auf Kubernetes umsetzen: Event Stores mit Kafka oder EventStoreDB als StatefulSet, CQRS-Projections und Schema-Evolution für Microservices.
Kubernetes CQRS Pattern Microservices in Deutschland optimal nutzen
Optimieren Sie Skalierbarkeit und Performance Ihrer komplexen Microservices auf Kubernetes in Deutschland mit dem CQRS Pattern. Erfahren Sie, wie diese zukunftsweisende Architektur Compliance-Anforderungen erfüllt und digitale Souveränität für deutsche Unternehmen sichert.
Datenbanken auf Kubernetes: StatefulSets richtig nutzen
PostgreSQL mit StatefulSets auf Kubernetes betreiben: Stabile Netzwerk-Identitäten, PersistentVolumeClaims und automatisierte Backups per CronJob.
Distributed Tracing: Jaeger auf Kubernetes einrichten
Jaeger für Distributed Tracing auf Kubernetes einrichten mit dem Jaeger Operator. OpenTelemetry-Instrumentation in Go und Python, Trace-Propagation zwischen Microservices und praktische Analyse von Latenz-Problemen.