- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
- Dapr (Distributed Application Runtime) injiziert einen Sidecar-Container in jeden Pod, der standardisierte APIs fuer Service-to-Service-Kommunikation, State Management, Pub/Sub und Secrets bereitstellt
- Anwendungscode bleibt unabhaengig von Infrastruktur: Ein HTTP-Aufruf an localhost reicht, um mit anderen Services zu kommunizieren, State zu speichern oder Events zu publizieren
- Building Blocks sind austauschbar: Redis als State Store heute, PostgreSQL morgen -- ohne Code-Aenderung, nur durch Komponenten-YAML
- Dapr ergaenzt Service Meshes wie Istio: Istio arbeitet auf Netzwerk-Ebene (mTLS, Traffic Management), Dapr auf Applikations-Ebene (State, Pub/Sub, Bindings)
- Distributed Tracing mit Zipkin oder Jaeger ist out-of-the-box integriert -- jeder Building-Block-Aufruf wird automatisch getracet
Dapr auf Kubernetes: Building Blocks fuer Microservices
Verteilte Microservices-Anwendungen haben wiederkehrende Probleme: Service Discovery, State Management, Event-Driven Communication, Secrets-Zugriff, Retries und Observability. Jedes Team loest diese Probleme individuell -- mit unterschiedlichen Libraries, SDKs und Patterns. Das Ergebnis: inkonsistente Implementierungen, Vendor Lock-in und hoher Wartungsaufwand.
Dapr (Distributed Application Runtime) loest das durch ein Sidecar-Pattern: Ein Dapr-Container wird neben jedem Anwendungs-Container in den Pod injiziert. Dieser Sidecar stellt standardisierte HTTP/gRPC-APIs bereit, die das Team ueber einfache localhost-Aufrufe nutzt. Die konkrete Infrastruktur (Redis, Kafka, PostgreSQL, Vault) wird deklarativ per YAML konfiguriert und kann ohne Code-Aenderung ausgetauscht werden.
Dapr Architektur: Sidecar-Pattern
Pod: order-service
+-----------------------------------------------+
| |
| +-------------------+ +------------------+ |
| | order-service | | daprd (Sidecar) | |
| | (Ihre Applikation)| | | |
| | | | - HTTP API :3500 | |
| | HTTP an localhost |--| - gRPC API :50001| |
| | Port 3500 | | - State Store | |
| | | | - Pub/Sub | |
| +-------------------+ | - Secrets | |
| | - Tracing | |
| +------------------+ |
+-----------------------------------------------+
Die Applikation spricht nur mit dem lokalen Dapr-Sidecar. Der Sidecar uebernimmt die Kommunikation mit anderen Services, Datenbanken, Message Brokern und Secret Stores. Das bedeutet: Null Abhaengigkeit auf spezifische SDKs oder Client-Libraries im Anwendungscode.
Installation auf Kubernetes
Dapr via Helm installieren
# Helm Repository hinzufuegen
helm repo add dapr https://dapr.github.io/helm-charts/
helm repo update
# Dapr installieren
helm install dapr dapr/dapr \
--namespace dapr-system \
--create-namespace \
--set global.logAsJson=true \
--set global.ha.enabled=true \
--wait
# Installation pruefen
kubectl get pods -n dapr-system
Erwartete Pods:
NAME READY STATUS
dapr-dashboard-xxx 1/1 Running
dapr-operator-xxx 1/1 Running
dapr-placement-server-0 1/1 Running
dapr-sentry-xxx 1/1 Running
dapr-sidecar-injector-xxx 1/1 Running
Dapr CLI installieren (optional, fuer lokale Entwicklung)
# macOS
brew install dapr/tap/dapr-cli
# Linux
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | bash
# Dapr-Status pruefen
dapr status -k
Building Block 1: Service Invocation
Service-to-Service-Kommunikation ohne Service Discovery, DNS oder Load Balancer konfigurieren zu muessen.
Service A aufrufen von Service B
Statt direkt http://order-service.default.svc.cluster.local:8080/api/orders aufzurufen, ruft der Client einfach den Dapr-Sidecar:
# HTTP-Aufruf ueber Dapr Sidecar (aus dem Pod heraus)
curl http://localhost:3500/v1.0/invoke/order-service/method/api/orders
Dapr uebernimmt: Service Discovery, Load Balancing, Retries, mTLS-Verschluesselung und Tracing.
Deployment mit Dapr-Annotation
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: shop
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "order-service"
dapr.io/app-port: "8080"
dapr.io/app-protocol: "http"
dapr.io/log-level: "info"
dapr.io/sidecar-cpu-request: "100m"
dapr.io/sidecar-memory-request: "128Mi"
dapr.io/sidecar-cpu-limit: "300m"
dapr.io/sidecar-memory-limit: "256Mi"
spec:
containers:
- name: order-service
image: registry.internal/order-service:2.1.0
ports:
- containerPort: 8080
env:
- name: DAPR_HTTP_PORT
value: "3500"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
Die Annotation dapr.io/enabled: "true" reicht aus, damit der Dapr Sidecar Injector automatisch einen daprd-Container in den Pod einfuegt.
Building Block 2: State Management
State Management abstrahiert die Datenspeicherung. Die Applikation speichert und liest Key-Value-Paare ueber den Dapr-Sidecar -- welches Backend dahinter liegt, ist konfigurierbar.
State Store Component: Redis
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: shop
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: "redis-master.shop.svc.cluster.local:6379"
- name: redisPassword
secretKeyRef:
name: redis-credentials
key: password
- name: actorStateStore
value: "true"
scopes:
- order-service
- inventory-service
State Store Component: PostgreSQL
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: shop
spec:
type: state.postgresql
version: v1
metadata:
- name: connectionString
secretKeyRef:
name: postgres-credentials
key: connection-string
- name: tableName
value: "dapr_state"
- name: metadataTableName
value: "dapr_metadata"
scopes:
- order-service
State API verwenden
# State speichern
curl -X POST http://localhost:3500/v1.0/state/statestore \
-H "Content-Type: application/json" \
-d '[{
"key": "order-123",
"value": {
"customer": "Mueller GmbH",
"items": 5,
"total": 499.99,
"status": "pending"
}
}]'
# State lesen
curl http://localhost:3500/v1.0/state/statestore/order-123
# State loeschen
curl -X DELETE http://localhost:3500/v1.0/state/statestore/order-123
Der Wechsel von Redis zu PostgreSQL erfordert nur die Aenderung der Component-YAML. Der Anwendungscode bleibt identisch.
Building Block 3: Pub/Sub Messaging
Event-Driven Kommunikation zwischen Services. Ein Service publiziert Events, andere abonnieren Topics.
Pub/Sub Component: Redis Streams
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: shop
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: "redis-master.shop.svc.cluster.local:6379"
- name: redisPassword
secretKeyRef:
name: redis-credentials
key: password
- name: consumerID
value: "shop-consumers"
Pub/Sub Component: Kafka
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: shop
spec:
type: pubsub.kafka
version: v1
metadata:
- name: brokers
value: "kafka-0.kafka.shop.svc.cluster.local:9092,kafka-1.kafka.shop.svc.cluster.local:9092"
- name: consumerGroup
value: "shop-group"
- name: authType
value: "none"
- name: maxMessageBytes
value: "1048576"
Events publizieren
# Event publizieren
curl -X POST http://localhost:3500/v1.0/publish/pubsub/orders \
-H "Content-Type: application/json" \
-d '{
"orderId": "order-123",
"event": "order.created",
"customer": "Mueller GmbH",
"total": 499.99
}'
Events abonnieren (deklarativ)
apiVersion: dapr.io/v2alpha1
kind: Subscription
metadata:
name: order-events-sub
namespace: shop
spec:
pubsubname: pubsub
topic: orders
routes:
default: /api/events/orders
scopes:
- notification-service
- inventory-service
Der notification-service empfaengt das Event als POST-Request auf /api/events/orders. Der Anwendungscode muss nur einen HTTP-Endpoint bereitstellen -- Dapr uebernimmt das Polling/Subscribing vom Message Broker.
Building Block 4: Secrets Management
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: kubernetes-secrets
namespace: shop
spec:
type: secretstores.kubernetes
version: v1
metadata: []
Fuer HashiCorp Vault:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: vault-secrets
namespace: shop
spec:
type: secretstores.hashicorp.vault
version: v1
metadata:
- name: vaultAddr
value: "http://vault.vault.svc.cluster.local:8200"
- name: vaultToken
secretKeyRef:
name: vault-token
key: token
- name: vaultKVUsePrefix
value: "false"
Secrets abrufen
# Secret aus Kubernetes Secrets lesen
curl http://localhost:3500/v1.0/secrets/kubernetes-secrets/redis-credentials
# Secret aus Vault lesen
curl http://localhost:3500/v1.0/secrets/vault-secrets/database-credentials
Praxis-Beispiel: Shop-Applikation mit 3 Services
Eine realistische Microservices-Applikation mit drei Services, die ueber Dapr kommunizieren:
Bestellung Dapr Pub/Sub Benachrichtigung
+-----------+ +------------+ +-------------------+
| order- |-*/}| Topic: |-*/}| notification- |
| service | | orders | | service |
+-----------+ +------------+ +-------------------+
| |
| Dapr Service | Dapr State
| Invocation | Store
v v
+-----------+ +-----------+
| inventory-| | Redis |
| service | | (State) |
+-----------+ +-----------+
Order Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: shop
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "order-service"
dapr.io/app-port: "8080"
spec:
containers:
- name: app
image: registry.internal/order-service:2.1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 200m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: shop
spec:
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
Inventory Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory-service
namespace: shop
spec:
replicas: 2
selector:
matchLabels:
app: inventory-service
template:
metadata:
labels:
app: inventory-service
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "inventory-service"
dapr.io/app-port: "8081"
spec:
containers:
- name: app
image: registry.internal/inventory-service:1.4.0
ports:
- containerPort: 8081
resources:
requests:
cpu: 200m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: inventory-service
namespace: shop
spec:
selector:
app: inventory-service
ports:
- port: 8081
targetPort: 8081
Notification Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: notification-service
namespace: shop
spec:
replicas: 1
selector:
matchLabels:
app: notification-service
template:
metadata:
labels:
app: notification-service
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "notification-service"
dapr.io/app-port: "8082"
spec:
containers:
- name: app
image: registry.internal/notification-service:1.2.0
ports:
- containerPort: 8082
resources:
requests:
cpu: 100m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: notification-service
namespace: shop
spec:
selector:
app: notification-service
ports:
- port: 8082
targetPort: 8082
Workflow: Bestellung aufgeben
Der order-service prueft den Bestand via Service Invocation, speichert die Bestellung via State Store und publiziert ein Event via Pub/Sub:
# 1. Order-Service ruft Inventory-Service via Dapr auf
curl http://localhost:3500/v1.0/invoke/inventory-service/method/api/check-stock \
-H "Content-Type: application/json" \
-d '{"productId": "PROD-42", "quantity": 2}'
# 2. Bestellung im State Store speichern
curl -X POST http://localhost:3500/v1.0/state/statestore \
-H "Content-Type: application/json" \
-d '[{
"key": "order-456",
"value": {"productId": "PROD-42", "quantity": 2, "status": "confirmed"}
}]'
# 3. Event publizieren (Notification-Service empfaengt automatisch)
curl -X POST http://localhost:3500/v1.0/publish/pubsub/orders \
-H "Content-Type: application/json" \
-d '{"orderId": "order-456", "event": "order.confirmed"}'
Dapr vs. Istio: Applikations-Ebene vs. Infrastruktur-Ebene
Dapr und Istio sind keine Alternativen -- sie ergaenzen sich. Der haeufige Irrtum: Beide nutzen Sidecars, also ersetzen sie sich gegenseitig. Das stimmt nicht.
| Aspekt | Dapr | Istio |
|---|---|---|
| Fokus | Applikations-Building-Blocks | Netzwerk-Infrastruktur |
| Sidecar-Zweck | APIs fuer State, Pub/Sub, Secrets | mTLS, Traffic Routing, Rate Limiting |
| Wer profitiert | Entwickler (weniger Boilerplate-Code) | Plattform-Team (Netzwerk-Sicherheit) |
| Service-to-Service | Dapr Service Invocation (app-level) | Envoy Proxy (network-level) |
| Verschluesselung | Dapr-eigenes mTLS | Istio mTLS via Envoy |
| State Management | Ja (Kernfeature) | Nein |
| Pub/Sub | Ja (Kernfeature) | Nein |
| Traffic Management | Nein | Ja (Canary, Blue-Green, Fault Injection) |
| Observability | Tracing via Zipkin/Jaeger | Metriken, Tracing, Logging |
| Zusammen nutzbar | Ja | Ja |
Wann nur Dapr: Ihre Services brauchen State Management, Pub/Sub oder Secrets-Abstraktion, aber kein Traffic Splitting oder Fault Injection.
Wann nur Istio: Sie brauchen mTLS, Traffic Management und Netzwerk-Observability, aber Ihre Applikationslogik handhabt State und Messaging selbst.
Wann beides: Komplexe Plattformen, die sowohl Applikations-Building-Blocks als auch Netzwerk-Infrastruktur standardisieren wollen. Dapr und Istio laufen als separate Sidecars im gleichen Pod.
Mehr zu Service Meshes unter Istio vs. Linkerd Vergleich.
Distributed Tracing mit Zipkin und Jaeger
Dapr integriert Distributed Tracing automatisch. Jeder Building-Block-Aufruf wird als Span erfasst und an einen Tracing-Backend weitergeleitet.
Tracing-Konfiguration
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: dapr-config
namespace: shop
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.observability.svc.cluster.local:9411/api/v2/spans"
metric:
enabled: true
logging:
apiLogging:
enabled: true
obfuscateURLs: false
Zipkin installieren
kubectl create namespace observability
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: zipkin
namespace: observability
spec:
replicas: 1
selector:
matchLabels:
app: zipkin
template:
metadata:
labels:
app: zipkin
spec:
containers:
- name: zipkin
image: openzipkin/zipkin:latest
ports:
- containerPort: 9411
resources:
requests:
cpu: 100m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: zipkin
namespace: observability
spec:
selector:
app: zipkin
ports:
- port: 9411
targetPort: 9411
EOF
# Zipkin UI oeffnen
kubectl port-forward svc/zipkin -n observability 9411:9411
# Browser: http://localhost:9411
Alternativ Jaeger verwenden:
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo update
helm install jaeger jaegertracing/jaeger \
--namespace observability \
--create-namespace \
--set query.service.type=ClusterIP
Bei Jaeger die Dapr-Konfiguration anpassen:
spec:
tracing:
samplingRate: "1"
otel:
endpointAddress: "jaeger-collector.observability.svc.cluster.local:4317"
isSecure: false
protocol: "grpc"
Wann Dapr einsetzen -- und wann nicht
Dapr ist sinnvoll wenn:
- Sie mehrere Microservices haben, die State, Pub/Sub oder Secrets teilen
- Sie Infrastruktur-Abhaengigkeiten (Redis, Kafka, Vault) aus dem Anwendungscode heraushalten wollen
- Ihr Team polyglott entwickelt (verschiedene Sprachen) und konsistente APIs braucht
- Sie Vendor Lock-in vermeiden wollen (Backend austauschbar ohne Code-Aenderung)
- Sie Distributed Tracing ohne zusaetzlichen Instrumentierungs-Aufwand wollen
Dapr ist zu viel wenn:
- Sie einen einzelnen Monolith oder wenige Services betreiben
- Ihre Services bereits ein gut funktionierendes SDK fuer State und Messaging verwenden
- Der zusaetzliche Sidecar-Overhead (ca. 50-100 MB RAM pro Pod) nicht tragbar ist
- Sie nur Netzwerk-Level-Features brauchen (mTLS, Traffic Routing) -- dann reicht ein Service Mesh
Zusammenfassung
| Building Block | Zweck | API-Beispiel |
|---|---|---|
| Service Invocation | Service-zu-Service-Aufrufe | GET /v1.0/invoke/APP_ID/method/ENDPOINT |
| State Management | Key-Value State speichern/lesen | POST /v1.0/state/STORE_NAME |
| Pub/Sub | Events publizieren/abonnieren | POST /v1.0/publish/PUBSUB_NAME/TOPIC |
| Secrets | Secrets aus Stores abrufen | GET /v1.0/secrets/STORE_NAME/SECRET |
| Bindings | Externe Systeme anbinden | POST /v1.0/bindings/BINDING_NAME |
Dapr auf Kubernetes vereinfacht die Entwicklung verteilter Microservices erheblich. Die Building Blocks abstrahieren wiederkehrende Infrastruktur-Probleme hinter einfachen HTTP-APIs. Der Sidecar-Ansatz haelt den Anwendungscode sauber und die Infrastruktur austauschbar.
Verwandte Artikel
- Istio vs. Linkerd: Service Mesh Vergleich
- Kubernetes Sidecar Pattern
- Kubernetes Secrets Management mit Vault
- Kubernetes Service Discovery
- OpenTelemetry mit Kubernetes
Sie planen den Einsatz von Dapr in Ihrer Microservices-Architektur oder moechten bestehende Services migrieren? Wir unterstuetzen Sie bei der Architektur, Component-Konfiguration und dem produktiven Betrieb. 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
Kubernetes Sidecar Pattern: Multi-Container Pods erklärt
Sidecar Pattern in Kubernetes mit YAML-Beispielen: Logging, Monitoring und Security-Sidecars konfigurieren und native Sidecar Containers ab v1.28 nutzen.
Ambassador Pattern in Kubernetes: Envoy als Sidecar-Proxy
Das Ambassador Pattern in Kubernetes erklärt: Envoy als Sidecar-Proxy für TLS-Termination, Retry-Logik und Routing mit YAML-Beispielen.
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.
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.
Microservices Decomposition: Monolith aufteilen
Monolith in Microservices aufteilen mit Domain-Driven Design und Strangler Fig Pattern. Praktischer Kubernetes-Guide mit Deployments und Datenbank-Strategien.