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.