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)



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.
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
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.