Service Mesh · Kubernetes · Envoy

Wie Istio den Traffic durch euren Cluster lenkt

Ein Rundgang durch die wichtigsten Bausteine: vom externen Request bis zur Antwort eines internen Microservice, inklusive kontrolliertem Zugriff auf externe Systeme wie RabbitMQ.

1 Grundlagen: Data Plane & Control Plane

Istio besteht aus zwei getrennten Ebenen: der Data Plane, die den eigentlichen Netzwerkverkehr transportiert, und der Control Plane (istiod), die Konfiguration verteilt und Zertifikate ausstellt. Kein Anwendungscode wird verändert. Der gesamte Netzwerkverkehr eines Pods wird transparent über einen injizierten Envoy-Sidecar umgeleitet.

Control Plane: istiod

Verteilt Routing-Regeln, TLS-Zertifikate (mTLS) und Sicherheitsrichtlinien an alle Envoy-Proxys im Mesh. Selbst kein Teil des Datenpfads.

Data Plane: Envoy Sidecar

Ein Envoy-Proxy pro Pod, injiziert als zweiter Container. Fängt jeden ein- und ausgehenden Netzwerkverkehr des Pods ab (via iptables oder eBPF).

Transparent für die App

Die Anwendung spricht wie gewohnt HTTP/gRPC über localhost oder DNS-Namen. Vom Sidecar merkt sie im Idealfall nichts.

Sidecar Injection: Traffic-Interception im Pod

App-Container Envoy Sidecar istiod (Control Plane)

iptables-Regeln leiten allen In-/Outbound-Traffic des Pods zunächst durch den lokalen Envoy-Sidecar. istiod pusht Konfiguration & Zertifikate per gRPC (xDS-API) an jeden Sidecar.

2 Ingress Gateway: der Eintrittspunkt

Was macht das Ingress Gateway?

Ein eigenständiges Envoy-Deployment am Rand des Mesh (kein Sidecar!). Es terminiert externen Traffic (TLS-Termination), ist über eine Gateway-Resource konfiguriert (Ports, Hosts, Zertifikate) und benötigt zwingend ein VirtualService, das an dieses Gateway gebunden ist, um Requests tatsächlich weiterzuleiten.

Externer Request trifft auf das Mesh

Gateway-Resource (Beispiel)

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: shop-ingress-gw
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: shop-tls-cert
      hosts:
        - "shop.example.com"

3 VirtualService: Layer-7-Routing

Wofür ist das VirtualService zuständig?

Definiert wie ein Request geroutet wird: Matching auf Host, URI-Pfad, Header oder Methode, Aufteilung auf mehrere Ziel-Subsets (z.B. Canary-Releases per Gewichtung), Retries, Timeouts und Fault-Injection. Ein VirtualService kann an ein Gateway (Nord-Süd-Traffic) oder ohne Gateway direkt im Mesh (Ost-West-Traffic zwischen Services) gebunden werden.

Routing-Beispiel: Canary-Release per Gewichtung

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: reviews-route
spec:
  hosts:
    - reviews.prod.svc.cluster.local
  http:
    - match:
        - headers:
            x-canary:
              exact: "true"
      route:
        - destination:
            host: reviews.prod.svc.cluster.local
            subset: v2
    - route:
        - destination:
            host: reviews.prod.svc.cluster.local
            subset: v1
          weight: 90
        - destination:
            host: reviews.prod.svc.cluster.local
            subset: v2
          weight: 10

4 DestinationRule: Subsets & Policies

Wofür ist die DestinationRule zuständig?

Wird nach dem Routing-Entscheid des VirtualService angewendet. Definiert Subsets (Pod-Gruppen anhand von Labels, z.B. version: v1), die Load-Balancing-Strategie (Round Robin, Least Request, Consistent Hash), Connection-Pool-Limits, Outlier Detection (Circuit Breaking) sowie den TLS-Modus für die Verbindung zum Ziel (ISTIO_MUTUAL für automatisches mTLS).

Subset-Auflösung & Load Balancing

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: reviews-dr
spec:
  host: reviews.prod.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
    loadBalancer:
      simple: LEAST_REQUEST
    outlierDetection:
      consecutive5xxErrors: 3
      interval: 10s
      baseEjectionTime: 30s
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

5 Service, Sidecar & Zugriffskontrolle: Ost-West-Traffic im Detail

Kubernetes Service

Rein logische Abstraktion: stabiler DNS-Name + ClusterIP. Er ist kein zusätzlicher Netzwerk-Hop. Er liefert Envoy lediglich die Zieladresse, die tatsächliche Verbindung läuft direkt Pod-zu-Pod.

HTTP Client = die Anwendung selbst

Der "HTTP Client" ist der ganz normale HTTP-Client im Anwendungscode (z.B. RestTemplate, fetch, requests). Er ruft den Service-DNS-Namen auf. Von der Umleitung durch den Sidecar merkt er nichts.

Was zwischen Client-Aufruf und Antwort passiert

  1. App-Container ruft http://payment.prod.svc.cluster.local auf, ganz normaler HTTP-Client-Code.
  2. iptables im selben Pod-Netzwerk-Namespace fängt den Outbound-Call ab und leitet ihn transparent an den lokalen Envoy-Sidecar (Port 15001) um.
  3. Der Outbound-Sidecar löst den Service-Namen über die von istiod gelieferte Endpoint-Liste auf, wendet VirtualService/DestinationRule an und wählt eine konkrete Ziel-Pod-IP per Load Balancing.
  4. Verbindung wird per mTLS aufgebaut und direkt zum Inbound-Sidecar des Ziel-Pods gesendet (Service/ClusterIP wird dabei nur für die initiale Namensauflösung benötigt).
  5. Inbound-Sidecar terminiert mTLS, prüft AuthorizationPolicies und reicht den Klartext-Request an den lokalen App-Container weiter (localhost).

Ruft ein Microservice einen anderen auf, sind also immer zwei Sidecars beteiligt: der ausgehende Sidecar des Aufrufers und der eingehende Sidecar des Ziels. Dazwischen läuft die Verbindung automatisch mTLS-verschlüsselt und gegenseitig authentifiziert, ganz ohne Code-Änderung an den Microservices.

Microservice A → Sidecar A (outbound) → mTLS → Sidecar B (inbound) → Microservice B. Antwort läuft denselben Weg zurück.

Warum ist das sicher, ohne dass Entwickler etwas tun müssen?

istiod stellt jedem Sidecar ein kurzlebiges X.509-Zertifikat aus (SPIFFE-Identität pro Service-Account). Beim Verbindungsaufbau prüfen sich beide Sidecars gegenseitig. Erst danach wird der Klartext-Request an die jeweilige Anwendung weitergereicht. PeerAuthentication-Policies können mTLS pro Namespace verpflichtend machen (STRICT-Modus).

AuthorizationPolicy: wer darf wen aufrufen?

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: payment-allow-frontend
  namespace: prod
spec:
  selector:
    matchLabels:
      app: payment
  action: ALLOW
  rules:
    - from:
        - source:
            principals: ["cluster.local/ns/prod/sa/frontend"]
      to:
        - operation:
            methods: ["POST"]
            paths: ["/api/orders/*"]

Zugriffskontrolle auf Basis der mTLS-Identität

Die per SPIFFE-Zertifikat bewiesene Identität des Aufrufers (principal) ist die Grundlage für Layer-7-Zugriffskontrolle. Der Inbound-Sidecar wertet AuthorizationPolicy-Regeln aus, bevor der Request an die Anwendung weitergereicht wird, ohne dass die App selbst Auth-Logik implementieren muss. Ohne passende ALLOW-Regel wird der Request mit 403 abgelehnt, sobald für den Namespace/Workload mindestens eine AuthorizationPolicy existiert.

6 External Service & Egress Gateway

Verlässt ein Aufruf das Mesh, z.B. ein Microservice, der Nachrichten an ein externes RabbitMQ außerhalb des Clusters sendet, kennt Istio das Ziel standardmäßig nicht. Es gibt zwei Muster, um das zu lösen:

A) Direkter Egress (ServiceEntry)

Ein ServiceEntry registriert den externen Host im Mesh-Katalog. Der ausgehende Sidecar routet die Verbindung dann direkt an den externen Host. Einfach, aber jeder Pod mit Sidecar braucht Netzwerkroute nach außen.

B) Egress Gateway (kontrolliert)

Der Sidecar routet den Traffic stattdessen zu einem dedizierten Egress Gateway (Envoy am Mesh-Rand). Nur dieses Gateway benötigt Netzwerkzugriff nach außen. Das macht es zur zentralen Stelle für TLS-Origination, Logging und Policy-Durchsetzung (z.B. Compliance-Vorgabe: "nur über Egress Gateway nach draußen").

Traffic-Fluss zu einem externen RabbitMQ über das Egress Gateway

# 1. Externen Host im Mesh bekannt machen
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-rabbitmq
spec:
  hosts:
    - rabbitmq.partner.example.com
  ports:
    - number: 5671
      name: amqps
      protocol: TLS
  resolution: DNS
  location: MESH_EXTERNAL
---
# 2. Egress-Gateway-Deployment als Ziel für externen Traffic
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: rabbitmq-egress-gw
spec:
  selector:
    istio: egressgateway
  servers:
    - port:
        number: 5671
        name: tls
        protocol: TLS
      tls:
        mode: PASSTHROUGH
      hosts:
        - rabbitmq.partner.example.com
---
# 3. Mesh-internen Traffic explizit über das Egress Gateway leiten
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: rabbitmq-via-egress
spec:
  hosts:
    - rabbitmq.partner.example.com
  gateways:
    - mesh
    - rabbitmq-egress-gw
  tls:
    - match:
        - gateways: [mesh]
          port: 5671
          sniHosts: [rabbitmq.partner.example.com]
      route:
        - destination:
            host: rabbitmq-egress-gw.istio-system.svc.cluster.local
            port:
              number: 5671
    - match:
        - gateways: [rabbitmq-egress-gw]
          port: 5671
          sniHosts: [rabbitmq.partner.example.com]
      route:
        - destination:
            host: rabbitmq.partner.example.com
            port:
              number: 5671

7 Gesamtüberblick: kompletter Traffic-Pfad

Alle Bausteine zusammen: ein externer User ruft über das Ingress Gateway einen Microservice auf, der intern einen zweiten Microservice per mTLS anspricht, welcher wiederum über das Egress Gateway Nachrichten an ein externes RabbitMQ sendet.

Wer macht was? Kompakt-Übersicht

KomponenteTypZuständigkeitResource
Ingress GatewayEnvoy (dediziert, Mesh-Rand)Empfang & TLS-Termination von externem TrafficGateway
VirtualServiceKonfigurationL7-Routing: Host, Pfad, Header, Gewichtung, RetriesVirtualService
DestinationRuleKonfigurationSubsets, Load Balancing, mTLS-Modus, Circuit BreakingDestinationRule
ServiceKubernetes-ObjektStabiler DNS-Name / ClusterIP, kein echter HopService
Sidecar / HTTP ClientEnvoy (injiziert je Pod)Transparente Interception, mTLS, Policy-Enforcementautomatische Injection
AuthorizationPolicyKonfigurationL7-Zugriffskontrolle auf Basis der mTLS-Identität (principal)AuthorizationPolicy
Egress GatewayEnvoy (dediziert, Mesh-Rand)Kontrollierter, auditierbarer Austritt nach externGateway (egress)
External Serviceaußerhalb des Meshz.B. externes RabbitMQ, Partner-APIServiceEntry