OMNI52
Service Mesh Cheatsheet OMNI52™ GmbH
Neu in Istio 1.31Artefakte nur noch auf Docker Hub, blob.istio.io und ghcr.io · Gateway API v1.6, TCPRoute-CRDs müssen mit · Geänderte Defaults, nur teilweise per compatibilityVersion umkehrbarAlle Neuerungen →

Istio Service Mesh
auf einem Blatt.

Dichte Referenz für Senior Platform Engineers und SREs. Traffic-Management, Security, Observability, Performance, Multi-Cluster und die häufigsten Fehlerbilder beim Betrieb. Keine Einsteiger-Folien.

Vorschau (3 Seiten A4 quer)

Cheatsheet Seite 1: Architektur, Traffic Management, Security
Cheatsheet Seite 2: Identity, Observability, Performance, Multi-Cluster, Diagnose
Cheatsheet Seite 3: Gateway API, Release & Support, Anti-Patterns, Kontakt & Lizenz

PDF herunterladen

Direkter Download, keine Mail-Adresse nötig. CC BY-SA 4.0: kopieren, drucken, weiterverteilen ist ausdrücklich erlaubt, solange die Quellenangabe sichtbar bleibt.

Service Mesh Cheatsheet (PDF, ~100 KB)

Was drin steht

Traffic

Gateway, VirtualService, DestinationRule, ServiceEntry, Sidecar-Scoping, mit Outlier Detection und Connection-Pool-Defaults.

Security

PeerAuthentication, AuthorizationPolicy mit JWT-Claims, RequestAuthentication, SPIFFE-Identity, Migrations-Pattern für mTLS STRICT.

Observability

Telemetry-API, Pflicht-Metriken aus dem Envoy-Stack, Tracing-Header-Propagation, OTel-Logging mit Filter.

Performance

Sidecar-Sizing nach Istio-Benchmark, Pilot-Tuning für Push-Storms, Ambient-Mode (GA seit 1.24) als sidecar-less Option.

Multi-Cluster

Primary-Remote, Multi-Primary, External Control-Plane. Trust-Domain-Setup und Endpoint-Discovery in einem Block.

Diagnose

istioctl-Werkzeuge in Reihenfolge des Vorfalls. Typische 404-NR- und 503-UF/UC/UH-Codes mit Ursache.

Cheatsheet im Volltext

Derselbe Inhalt wie im PDF, zum Mitlesen, Durchsuchen und direkten Kopieren der YAML-Snippets. Stand: Istio 1.31 (v1.31 · 2026.10).

Architektur

Datapath & Komponenten

Client → Ingress GW → Sidecar(src) → Sidecar(dst) → App. mTLS-Handshake nur beim Connect.

istiod: Pilot/Citadel/Galley vereint, xDS-Push + CA. istio-proxy: Envoy-Sidecar (Mutating Webhook) oder Ambient ztunnel. Gateway: dedizierte Envoys, Deployment + LB-Service.

Envoy-Version = Istio-Version + 8. Istio 1.31 → Envoy 1.39.

Install-Profile

default (prod), minimal (nur istiod), ambient (sidecar-less), remote (MC-Workload-Cluster), demo (laut). Canary via --revision=1-31-1, Namespaces hängen an einem Revision-Tag (istio.io/rev=prod-stable).

istioctl x precheck
istioctl install --set profile=default --revision=1-31-1
istioctl tag set prod-stable --revision 1-31-1 --overwrite

Traffic Management

Gateway

Bindet Ports/Hosts/TLS am Edge-Proxy. servers[].port + tls.mode: SIMPLE, MUTUAL, PASSTHROUGH, ISTIO_MUTUAL. credentialName verweist auf Secret im selben Namespace wie der Gateway-Pod.

apiVersion: networking.istio.io/v1
kind: Gateway
metadata: {name: web-gw, namespace: istio-system}
spec:
  selector: {istio: ingressgateway}
  servers:
  - port: {number: 443, name: https, protocol: HTTPS}
    hosts: ["www.istio-cheatsheet.de"]
    tls:
      mode: SIMPLE
      credentialName: web-cert

VirtualService

Routing-Regeln. Bindet hosts an gateways (oder mesh für Ost-West). Match-Reihenfolge ist signifikant, erste Treffer-Regel gewinnt. Header-Keys lowercase, Header-Werte und Pfad case-sensitive (Pfad: ignoreUriCase: true).

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata: {name: reviews}
spec:
  hosts: [reviews]
  http:
  - match:
    - headers: {end-user: {exact: jason}}
    route:
    - destination: {host: reviews, subset: v2}
  - route:
    - destination: {host: reviews, subset: v1}
      weight: 90
    - destination: {host: reviews, subset: v3}
      weight: 10
    retries:
      attempts: 3
      perTryTimeout: 2s
      retryOn: gateway-error,connect-failure,refused-stream
    timeout: 10s

DestinationRule

Subsets (Versionen) + Traffic-Policy (LB, Connection-Pool, Outlier Detection, TLS). Wirkt erst nach VirtualService-Routing. host muss FQDN oder Short-Name im selben Namespace sein.

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata: {name: reviews}
spec:
  host: reviews
  trafficPolicy:
    connectionPool:
      tcp: {maxConnections: 100}
      http:
        http1MaxPendingRequests: 64
        http2MaxRequests: 1000
        maxRequestsPerConnection: 10
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 60s
    loadBalancer:
      consistentHash:
        httpHeaderName: x-user-id
  subsets:
  - name: v1
    labels: {version: v1}
  - name: v2
    labels: {version: v2}

ServiceEntry

Externe Hosts in den Mesh-Registry holen (DB, SaaS, REST-APIs). MESH_EXTERNAL + DNS resolution sind die saubere Default-Kombi. Ohne ServiceEntry bleibt der Egress-Traffic BlackHoleCluster wenn outboundTrafficPolicy=REGISTRY_ONLY.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata: {name: stripe-api}
spec:
  hosts: ["api.stripe.com"]
  ports:
  - {number: 443, name: https, protocol: HTTPS}
  resolution: DNS
  location: MESH_EXTERNAL

Sidecar

Begrenzt, was ein Workload aus der Mesh-Registry sieht, kritisch für Memory-Footprint und Push-Latenz in großen Clustern. Default: jeder Sidecar bekommt Config für alle Services. Ab 1.31 zieht ein ~-Präfix Namespaces ab: */* + ~ns1/*.

apiVersion: networking.istio.io/v1
kind: Sidecar
metadata: {name: default, namespace: prod}
spec:
  egress:
  - hosts:
    - "./*"             # nur eigener Namespace
    - "istio-system/*"
    - "shared/*"

Security

PeerAuthentication

mTLS-Modus zwischen Sidecars. STRICT, PERMISSIVE, DISABLE. Geltung: Mesh (in istio-system), Namespace, oder Workload via selector. Migration zu STRICT: zuerst Mesh-weit PERMISSIVE, dann namespace-weise umstellen.

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: {name: default, namespace: prod}
spec:
  mtls: {mode: STRICT}
---
# Port-Ausnahme (Container-Port): NLB-Health-Check ohne mTLS
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: {name: legacy, namespace: prod}
spec:
  selector: {matchLabels: {app: legacy}}
  mtls: {mode: STRICT}
  portLevelMtls:
    8080: {mode: PERMISSIVE}

AuthorizationPolicy

L7-Zugriffskontrolle. Auswertung CUSTOM → DENY → ALLOW, AUDIT entscheidet nichts. Gibt es ALLOW-Policies, muss eine matchen, sonst Deny. Ohne rules matcht eine Policy nie: ALLOW ohne rules = deny-all, rules: [{}] = allow-all. Regeln OR, from/to/when je Regel AND.

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata: {name: reviews-allow, namespace: prod}
spec:
  selector: {matchLabels: {app: reviews}}
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - cluster.local/ns/prod/sa/productpage
    to:
    - operation:
        methods: [GET]
        paths: ["/reviews/*"]
    when:
    - key: request.auth.claims[groups]
      values: ["reader", "admin"]

RequestAuthentication (JWT)

Validiert JWT, füllt request.auth.* für AuthorizationPolicy. Wichtig: ohne zusätzliche DENY-Policy mit notRequestPrincipals=["*"] können unauthentifizierte Requests weiterhin durch.

apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata: {name: jwt, namespace: prod}
spec:
  selector: {matchLabels: {app: api}}
  jwtRules:
  - issuer: "https://auth.example.com"
    jwksUri: "https://auth.example.com/jwks"
    audiences: ["api.example.com"]
    forwardOriginalToken: true

Identity

SPIFFE-URI spiffe://<trust>/ns/<ns>/sa/<sa>. Trust-Domain default cluster.local, im Multi-Cluster-Mesh in allen Clustern gleich (eindeutig ist multiCluster.clusterName). Workload-Cert: 24 h Laufzeit (SECRET_TTL), istio-agent erneuert bei 50 % (SECRET_GRACE_PERIOD_RATIO) per CSR an istiod.

Observability

Telemetry-API

Metriken/Logs/Traces pro Workload oder Namespace. Ersetzt EnvoyFilter-Telemetry-Mods und die alten values.telemetry.v2.*-Settings.

apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata: {name: trace-prod, namespace: prod}
spec:
  tracing:
  - providers: [{name: tempo}]
    randomSamplingPercentage: 5.0
  metrics:
  - providers: [{name: prometheus}]
    overrides:
    - match: {metric: REQUEST_COUNT}
      tagOverrides:
        request_protocol: {operation: REMOVE}
  accessLogging:
  - providers: [{name: otel}]
    filter:
      expression: "response.code >= 400"

Metriken & Tracing

RED-Counter: istio_requests_total, Latency: istio_request_duration_milliseconds_bucket. Label reporter = source/destination, in Dashboards immer auf destination aggregieren (sonst Doppelzählung).

Der Sidecar startet Traces und schreibt Spans, kann aber eingehende und ausgehende Requests der App nicht verknüpfen. App muss eingehende Trace-Header (x-request-id, x-b3-*, traceparent) beim Ausgang weitergeben.

Performance & Tuning

Sidecar-Sizing

Default-Requests 100m / 128Mi (Limits 2000m / 1024Mi): bei hohem RPS zu knapp.
Richtwert (Istio-Benchmark 1.24, 1 KB, 2 Worker, 1000 RPS): Sidecar ≈ 0,2 vCPU + 60 MB, ztunnel ≈ 0,06 vCPU + 12 MB.
Memory wächst mit Listenern/Clustern/Routes, Sidecar-Resource schneidet.

Pilot-Tuning

PILOT_PUSH_THROTTLE: default auto = min(15+5n, 100). PILOT_DEBOUNCE_AFTER: default 100 ms. PILOT_DEBOUNCE_MAX: default 10 s. Bei Push-Storms (viele Pod-Restarts) Debounce hochziehen. Das n im Throttle-Default ist GOMAXPROCS von istiod, im Chart aus dem CPU-Limit (ohne Limit: Node-CPUs). Mehr CPU für istiod oder den Wert explizit setzen.

Ambient Mode (GA seit 1.24)

Kein Sidecar-Inject. ztunnel: Node-DaemonSet, L4 mTLS (HBONE). Waypoint: optional, L7, per istio.io/use-waypoint an Namespace, Service oder Pod; Policies dort per targetRefs, selector-Policies ignoriert der Waypoint. Opt-in: Label istio.io/dataplane-mode=ambient je Namespace oder Pod. Spart RAM bei vielen Pods, kostet Komplexität beim Debugging.

Jobs & CronJobs (Sidecar-Lifecycle)

App vor proxy ready → Connect-Refused. App fertig, Sidecar läuft → Job hängt.

Native Sidecar (GA ab K8s 1.33): Proxy läuft als initContainer mit restartPolicy: Always, sauberer Lifecycle. Default seit Istio 1.27: ENABLE_NATIVE_SIDECARS=auto injiziert nativ, wenn alle Nodes mindestens Kubelet 1.33 melden. Ein älterer Node schaltet es clusterweit ab. Per Pod (Vorrang): sidecar.istio.io/nativeSidecar: "true"|"false".

Pre-Native: holdApplicationUntilProxyStarts gegen Start-Race, trap mit POST :15020/quitquitquit auf EXIT gegen Shutdown-Hänger (feuert auch bei Crash/Signal, nicht nur bei Success).

# Pre-Native-Fallback: Start-Race + Shutdown-Hänger
metadata:
  annotations:
    proxy.istio.io/config: |
      { "holdApplicationUntilProxyStarts": true }
spec:
  containers:
  - name: worker
    command: ["/bin/sh","-c"]
    args:
    - |
      trap 'curl -fsS -XPOST \
        localhost:15020/quitquitquit||true' EXIT
      ./run-task

Graceful Drain: EXIT_ON_ZERO_ACTIVE_CONNECTIONS

Klassischer Sidecar: bei SIGTERM drainiert er nur terminationDrainDuration (Default 5 s), dann Hard-Exit, lang lebende Verbindungen (gRPC-Streams, WebSockets, DB-Pools) werden gekappt (503/Reset beim Rollout/Scale-Down). Native Sidecar (Default seit 1.27): preStop startet den Drain, SIGTERM kommt erst nach Ende der App-Container.

Fix: EXIT_ON_ZERO_ACTIVE_CONNECTIONS=true (via proxyMetadata bzw. proxy.istio.io/config): pilot-agent pollt aktive Envoy-Connections und beendet den Proxy sobald sie 0 erreichen statt nach fixem Timer.

Caveat: hängt eine Verbindung (Client schließt nie), blockt der Proxy bis terminationGracePeriodSeconds → SIGKILL. Grace-Period entsprechend hochsetzen. Mesh-weit via meshConfig.defaultConfig.proxyMetadata.

Multi-Cluster

Topologien & Setup

Primary-Remote: ein istiod, mehrere Workload-Cluster. Multi-Primary: istiod pro Cluster, gemeinsame Root-CA. External Control-Plane: istiod außerhalb.

Pflicht: gemeinsame Root-CA und trustDomain, gleiche meshID, eindeutiger multiCluster.clusterName. network im flachen Netz gleich, bei Multi-Network je Netz verschieden (plus East-West-Gateway). Endpoint-Discovery via istioctl create-remote-secret.

istioctl create-remote-secret \
  --context=cluster-b \
  --name=cluster-b \
  | kubectl apply --context=cluster-a -f -

Diagnose

Erstes istioctl-Werkzeug bei Vorfall

istioctl proxy-status zeigt Sync-Stand jedes Sidecars. SYNCED = ACK erhalten, NOT SENT = nichts zu senden (meist normal), STALE = gesendet, kein ACK → Netz istiod/Proxy oder Bug, ERROR = NACK → Config prüfen.

istioctl proxy-status
istioctl proxy-config routes <pod>.<ns> -o json
istioctl proxy-config clusters <pod>.<ns>
istioctl proxy-config listeners <pod>.<ns>
istioctl proxy-config endpoints <pod>.<ns>
istioctl proxy-config secrets <pod>.<ns>

# Statisches Lint: VirtualService/DR-Konflikte etc.
istioctl analyze -n prod

# Bug-Report: Konfig + Logs,
# Secret-Inhalte nur mit --full-secrets
istioctl bug-report

Live-Log eines Sidecars

istioctl proxy-config log <pod> --level debug setzt das Log-Level live ohne Pod-Restart. Komponenten-spezifisch z.B. --level rbac:debug,jwt:debug. Nach Diagnose zurück auf warning.

Häufige Fehlerbilder

404 NR: keine Route, Host- oder Path-Match greift nicht, Reihenfolge der http[] prüfen.
503 UF: Connect zum Upstream scheitert, App-Port ≠ targetPort oder TLS-Mismatch (DR DISABLE gegen STRICT).
503 UC: Upstream schließt bestehende Verbindung, oft Idle-Timeout der App kürzer als Envoy-Pool.
503 UH: kein gesunder Endpoint (Readiness, Outlier Detection).

OUTPUT_CERTS-Scraping: read: connection reset by peer

App-originated mTLS (Prometheus/Alloy-Federate via ISTIO_META_OUTPUT_CERTS + /etc/istio-certs/{cert-chain,key,root-cert}.pem) bricht mit RST, obwohl Ziel-PeerAuthentication PERMISSIVE ist und Allow-all greift.

Ursache fast immer: Client-Sidecar wrapt den App-mTLS in einen zweiten ISTIO_MUTUAL-Tunnel. Eine DR mit host: "*.local" + exportTo: ["*"] (typisch STRICT-Migration-Helper in istio-system) matcht via EndpointSlice-Lookup auch direkte Pod-IP-Calls. Ergebnis: TLS-in-TLS. Ziel-15006 terminiert nur outer; inner TLS-Bytes landen als Klartext auf dem App-Port, Backend erwartet HTTP, schließt.

PERMISSIVE ist Inbound-Entscheidung nach der Transport-Terminierung und heilt nichts, was outbound passiert.

Fix: excludeOutboundPorts am Scraper-Pod (port-scoped) oder eigene DR mit tls.mode: DISABLE auf den Ziel-Service (spezifischer als *.local, gewinnt).

# wraps der Client-Sidecar?
# alpn=istio* + sni=outbound_ = Beweis:
istioctl pc cluster <pod>.<ns> --fqdn <ziel> -o json \
  | jq '.[]|(.transportSocket,
    .transportSocketMatches[]?.transportSocket)
    |select(.).typedConfig
    |{sni,alpn:.commonTlsContext.alpnProtocols}'
# welche *.local-DR greift?
kubectl get dr -A -o json | jq -r '.items[]|
  select(.spec.host|test("\\*\\.local"))|
  .metadata.namespace+"/"+.metadata.name'

Gateway API (v1, kubernetes-sigs)

Status in 1.31

Istio 1.31 baut auf Gateway API v1.6 (HTTPRoute, GRPCRoute, TLSRoute, ListenerSet, BackendTLSPolicy, TCPRoute jetzt v1). CRDs vor dem Istio-Upgrade einspielen: zu alte CRDs ignoriert istiod still (TLSRoute < v1.5, TCPRoute < v1.6), istioctl analyze meldet IST0176. Neue Projekte: Gateway API, networking.istio.io bleibt supportet.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata: {name: web, namespace: istio-system}
spec:
  gatewayClassName: istio
  listeners:
  - name: https
    hostname: www.istio-cheatsheet.de
    port: 443
    protocol: HTTPS
    tls:
      certificateRefs:
      - {name: web-cert}
    allowedRoutes:  # Default Same, Prod: Selector
      namespaces: {from: All}
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: {name: site, namespace: web}
spec:
  parentRefs: [{name: web, namespace: istio-system}]
  hostnames: ["www.istio-cheatsheet.de"]
  rules:
  - matches: [{path: {type: PathPrefix, value: /}}]
    backendRefs: [{name: nginx, port: 80}]

Release & Support

Kadenz & Support-Fenster

Rund ein Minor pro Quartal. Ein Minor wird bis sechs Wochen nach dem N+2-Release gepflegt, danach keine Backports mehr, auch nicht für Security-Issues.

1.31: seit 31.08.2026, zuletzt 1.31.1 (21.09.2026), K8s 1.32–1.37.
1.30: supportet bis sechs Wochen nach 1.32, zuletzt 1.30.5 (21.09.2026).
1.29: EOL am 12.10.2026, zuletzt 1.29.8.

Praktisch: mehr als zwei Minors Rückstand heißt ungepatchte CVEs im Datenpfad. In-place nur Minor für Minor, per Revision (Canary) bis zu zwei Minors in einem Schritt (1.29 → 1.31). compatibilityVersion=1.30 führt Istio-Gateways aus anderen Namespaces wieder in Gateway-API-Gateways zusammen und schickt Unhealthy-Endpoints nicht mehr per EDS.

Registry: 1.30 zieht per Default von registry.istio.io/release, also hub: docker.io/istio oder Pull-Through-Cache setzen. Ab 1.31 Images nur auf docker.io/istio. Charts aller Versionen liegen auf blob.istio.io/istio-release/charts bzw. ghcr.io/istio/release/charts. gcr.io/istio-release, registry.istio.io und istio-release.storage.googleapis.com gehen im Dez. 2026 vom Netz, Scream-Tests 13.10., 17.11., 08.–09.12.2026. Signaturprüfung ab 1.31.1 mit istio-key-v2.pub.

Anti-Patterns

Was du nicht tun solltest

Default-Sidecar-Requests in Prod: 100m/128Mi überbuchen Nodes, ohne Sidecar-Scoping droht OOM am 1024Mi-Limit.
Mesh-VirtualService ohne Sidecar-Resource: jeder Sidecar bekommt jede Regel, Push-Storm.
STRICT ohne Migration: nicht injizierte Workloads brechen (Jobs, externe Health-Checks).
EnvoyFilter als Default-Tool: erst Telemetry-API, AuthorizationPolicy, TrafficExtension (Wasm/Lua, alpha seit 1.30). EnvoyFilter bricht zwischen Minors.
Tracing ohne App-Header-Propagation: Spans zerreißen.
Multi-Cluster ohne gemeinsame Root-CA: mTLS scheitert.

Verwandte Cheatsheets

Ebenfalls von OMNI52:
service-mesh-cheatsheet.de, Istio + Linkerd + Cilium im Vergleich
kubernetes-cheatsheet.de, Kubernetes Core
cilium-cheatsheet.de, eBPF-Networking
istio-quickref.de, Istio quick reference (EN)

Lizenz & Weiterverteilung

CC BY-SA 4.0. Du darfst dieses Cheatsheet kopieren, weiterverteilen, ausdrucken und in eigenen Materialien zitieren. Bedingung: Quellenangabe „Service Mesh Cheatsheet, OMNI52 GmbH, istio-cheatsheet.de“ bleibt sichtbar, und abgeleitete Werke stehen unter der gleichen Lizenz (Share-Alike).

Nicht erlaubt: Logo, Marken oder den Eindruck zu vermitteln, dass der Inhalt von dir/euch stammt oder dass OMNI52 GmbH die Weiterverwendung sponsort.

Volltext der Lizenz: creativecommons.org/licenses/by-sa/4.0/deed.de.

Istio is a registered trademark of The Linux Foundation. OMNI52™ is a trademark of OMNI52 GmbH (filed, not yet registered). This website is operated by OMNI52 GmbH and is not affiliated with, endorsed by, or sponsored by The Linux Foundation, the CNCF, or the Istio project. “Istio” is used in a descriptive sense to indicate the technology our consulting services focus on.