Veröffentlicht am

Dapr auf Kubernetes: Sidecar-Pattern für Microservices

Teilen:
Authors

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.

AspektDaprIstio
FokusApplikations-Building-BlocksNetzwerk-Infrastruktur
Sidecar-ZweckAPIs fuer State, Pub/Sub, SecretsmTLS, Traffic Routing, Rate Limiting
Wer profitiertEntwickler (weniger Boilerplate-Code)Plattform-Team (Netzwerk-Sicherheit)
Service-to-ServiceDapr Service Invocation (app-level)Envoy Proxy (network-level)
VerschluesselungDapr-eigenes mTLSIstio mTLS via Envoy
State ManagementJa (Kernfeature)Nein
Pub/SubJa (Kernfeature)Nein
Traffic ManagementNeinJa (Canary, Blue-Green, Fault Injection)
ObservabilityTracing via Zipkin/JaegerMetriken, Tracing, Logging
Zusammen nutzbarJaJa

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 BlockZweckAPI-Beispiel
Service InvocationService-zu-Service-AufrufeGET /v1.0/invoke/APP_ID/method/ENDPOINT
State ManagementKey-Value State speichern/lesenPOST /v1.0/state/STORE_NAME
Pub/SubEvents publizieren/abonnierenPOST /v1.0/publish/PUBSUB_NAME/TOPIC
SecretsSecrets aus Stores abrufenGET /v1.0/secrets/STORE_NAME/SECRET
BindingsExterne Systeme anbindenPOST /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


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