1 Drei Datenpfade auseinanderhalten
OVN, Router und Service Mesh lösen unterschiedliche Aufgaben. Sie liegen bei einem Request hintereinander, sind aber keine austauschbaren Produkte.
Begriffe: Ein Overlay ist ein virtuelles Netz über dem physischen Node-Netz. DNAT ersetzt die Zieladresse eines Pakets. Eine EndpointSlice enthält die aktuell bereiten Pod-Adressen eines Service. Ein Sidecar ist ein zusätzlicher Container im selben Pod.
2 Netzwerktyp und Komponenten prüfen
Diese Befehle lesen nur. Für System-Namespaces und spätere OVN-Vertiefung brauchst du cluster-admin.
oc whoami
oc get network.operator.openshift.io cluster -o jsonpath='{.spec.defaultNetwork.type}{"\n"}'
oc get network.config.openshift.io cluster -o yaml
oc get co network ingress
oc get pods -n openshift-ovn-kubernetes -o wide
oc get pods -n openshift-ingress -o wideErwartung: Der Netzwerktyp ist OVNKubernetes. Die ClusterOperatoren network und ingress melden Available=True und Degraded=False.
3 Zwei isolierte Test-Workloads anlegen
Die beiden Deployments dienen nur als Quell- und Ziel-Pod. Das Ziel erhält zusätzlich einen Service und eine Route.
oc new-project network-path-lab oc create deployment source --image=quay.io/openshifttest/nginx oc create deployment destination --image=quay.io/openshifttest/nginx oc expose deployment destination --port=80 oc expose service destination oc wait --for=condition=Available deployment/source deployment/destination --timeout=120s oc get pod,service,route -o wide
Die Deployments und ihre Pods werden von dir angefordert. ReplicaSets und Pods werden von Kubernetes generiert. Die ClusterIP des Service und der Hostname der Route werden vom Cluster vergeben.
4 Service bis zur Pod-IP verfolgen
Eine ClusterIP ist kein Prozess und keine Netzwerkkarte. Sie ist eine virtuelle Adresse, für die OVN Regeln mit den EndpointSlice-Adressen programmiert.
oc get service destination -o yaml oc get endpointslice -l kubernetes.io/service-name=destination -o yaml oc get pods -l app=destination -o wide oc describe service destination
oc run probe --rm -i --restart=Never --image=registry.access.redhat.com/ubi9/ubi-minimal:latest --command -- curl -sS http://destination:80/
| Objekt | entscheidender Wert | Wirkung |
|---|---|---|
Service | spec.clusterIP und Port 80 | stabile virtuelle Adresse und Name |
EndpointSlice | endpoints[].addresses und conditions.ready | aktuelle erreichbare Ziele |
Pod | status.podIP und Node | reales Ziel des umgeschriebenen Pakets |
Wenn der Curl-Aufruf funktioniert, wurden DNS-Auflösung, Service-Auswahl, OVN-Regeln und Zielprozess gemeinsam bestätigt. Eine leere EndpointSlice bedeutet meist, dass Selector oder Readiness nicht passen.
5 Geneve und OVN am Node sehen
Geneve kapselt Podverkehr nur dann, wenn Quell- und Ziel-Pod auf verschiedenen Nodes liegen. Auf einer SNO bleibt der Verkehr lokal und zeigt keinen echten Tunnel-Hop.
Das CNI-Plugin. Jeder Node führt OVN lokal, darüber liegt eine logische Netz-Topologie (Switches, ein verteilter Router) auf dem physischen Netz. Pods auf demselben Node reden über die lokale OVS-Bridge, Pods auf verschiedenen Nodes über einen Geneve-Tunnel. Services (B3) sind OVN-Load-Balancer – kein kube-proxy.
ovnkube-node), die Datenbank schreibt ovnkube-control-plane. Pods bekommen eine IP aus dem Ausschnitt ihres Nodes. Reden zwei Pods auf demselben Node, geht das Paket nur durch die OVS-Bridge br-int. Sind sie auf verschiedenen Node, kapselt OVN es in einen Geneve-Tunnel (UDP 6081) über das Node-Netz. Services sind OVN-Load-Balancer, NetworkPolicy (F2) OVN-Regeln – kein kube-proxy.- das CNI-Plugin: jeder Node führt OVN lokal (
ovnkube-node-Pod +ovn-controller), die Datenbank schreibtovnkube-control-planeinopenshift-ovn-kubernetes - jeder Node bekommt einen Ausschnitt des Cluster-Pod-CIDR, ein Pod bekommt eine IP daraus über ein
veth-Paar in die OVS-Bridgebr-int - Pod zu Pod auf demselben Node: das Paket geht nur durch
br-intund verlässt den Node nie - Pod zu Pod auf verschiedenen Nodes: OVS kapselt in einen
Geneve-Tunnel (UDP 6081) über das physische Node-Netz, der Ziel-Node packt aus - über dem physischen Netz liegt eine logische Topologie aus Switches (einer je Node) und einem verteilten logischen Router
- der Pod sieht ein flaches Netz – jede Pod-IP ist von jeder anderen erreichbar, solange keine
NetworkPolicy(F2) greift - Services (B3) sind OVN-Load-Balancer: ruft
api-gatewaydencatalog-service, macht OVN das DNAT direkt in der Datapath – kein kube-proxy, keine iptables-Regeln - OVN erzwingt auch NetworkPolicy (F2),
EgressIP(feste Quell-IP nach außen) undEgressFirewall(Ziel-Regeln je Namespace) - OVN-Kubernetes hat
OpenShift SDNabgelöst – SDN ist ab OCP 4.17 entfernt - Tunnel-Overhead: das Pod-MTU ist rund 50 Byte kleiner als das Node-MTU – wichtig bei Jumbo Frames oder VPN darunter
- auf der SNO: die 8 Dienste + Postgres/Redis/Kafka/Keycloak sind alle Pods auf einem Node – jeder Hop bleibt in
br-int, nie ein Geneve-Tunnel - nachsehen:
oc get pods -n openshift-ovn-kuberneteszeigt auf der SNO genau einenovnkube-node-Pod, dazu die Nodes-Annotationk8s.ovn.org/node-subnetsund dasNetwork-Objekt - ein lokaler 2-Node-
kind-Cluster zeigt das Kapselungs-Prinzip – für echtes OVN-K mit Geneve dasovn-kubernetes-kind-Setup - OCP-Unterschied: identisch – OVN-Kubernetes ist in OKD und OCP gleich, SDN als älteres Plugin ist weg
- von Hand verfolgen: Service-DNAT und Geneve – ClusterIP, EndpointSlice und Pod-IP zusammenführen und den berechneten OVN-Pfad optional mit
ovnkube-tracesimulieren
$node = oc get pod -l app=destination -o jsonpath='{.items[0].spec.nodeName}'
$ovnPod = oc get pod -n openshift-ovn-kubernetes -l app=ovnkube-node --field-selector spec.nodeName=$node -o jsonpath='{.items[0].metadata.name}'
"Node: $node"
"OVN-Pod: $ovnPod"
oc debug --as-root node/$node -- chroot /host ip -d link show genev_sys_6081
oc logs -n openshift-ovn-kubernetes pod/$ovnPod -c ovnkube-node --since=5mBash-Variante anzeigen
node=$(oc get pod -l app=destination -o jsonpath='{.items[0].spec.nodeName}')
ovn_pod=$(oc get pod -n openshift-ovn-kubernetes -l app=ovnkube-node --field-selector "spec.nodeName=$node" -o jsonpath='{.items[0].metadata.name}')
printf 'Node: %s\nOVN-Pod: %s\n' "$node" "$ovn_pod"
oc debug --as-root "node/$node" -- chroot /host ip -d link show genev_sys_6081
oc logs -n openshift-ovn-kubernetes "pod/$ovn_pod" -c ovnkube-node --since=5mVersionsgrenze: Container- und Interface-Namen können sich ändern. Wenn ovnkube-node oder genev_sys_6081 fehlt, zuerst die tatsächlichen Container mit oc get pod -n openshift-ovn-kubernetes $ovnPod -o jsonpath='{.spec.containers[*].name}' und die Interfaces mit oc debug --as-root node/$node -- chroot /host ip -d link lesen.
6 Optional: den OVN-Pfad simulieren
ovnkube-trace simuliert einen TCP- oder UDP-Pfad durch logische OVN- und OpenFlow-Regeln. Diese Vertiefung läuft in Linux oder WSL mit funktionierendem oc-Zugang.
pod=$(oc get pods -n openshift-ovn-kubernetes -l app=ovnkube-control-plane -o name | head -1 | cut -d/ -f2) oc cp -n openshift-ovn-kubernetes "$pod:/usr/bin/ovnkube-trace" -c ovnkube-cluster-manager ./ovnkube-trace chmod +x ./ovnkube-trace ./ovnkube-trace \ -src source -src-namespace network-path-lab \ -dst destination -dst-namespace network-path-lab \ -service destination -tcp -dst-port 80 -loglevel 2
ovnkube-trace wird aus dem laufenden Control-Plane-Pod kopiert. Es ist ein temporäres Diagnosewerkzeug und gehört nicht ins Repository. Entferne es danach mit rm -- ./ovnkube-trace.
Leseschlüssel: Ein erfolgreicher Trace bestätigt nur die berechneten OVN-Regeln. Ein echter Curl-Aufruf bestätigt zusätzlich DNS, Prozesse, Ports und den realen Datenpfad. Beide Tests beantworten unterschiedliche Fragen.
7 Route bis zur generierten HAProxy-Konfiguration verfolgen
Der Ingress Operator verwaltet einen IngressController. Dieser erzeugt das Router-Deployment. Der Router beobachtet Routes und Endpoints und rendert daraus seine HAProxy-Konfiguration.
B4 hat den Router als Kasten gezeigt – hier von innen. Ein Controller im Router-Pod beobachtet Route und Endpoints, rendert daraus die haproxy.config neu und lädt HAProxy neu. Jede Route wird zu einem Frontend-Match plus einem Backend mit einer server-Zeile je bereitem Pod. Mehrere IngressController teilen die Routes per Selektor auf.
router-default-Pod beobachtet Route und Endpoints, rendert daraus die haproxy.config aus einer Go-Template und lädt HAProxy graceful neu. Jede Route wird zu einem Frontend-Match (SNI bei passthrough, Host-Header sonst) plus einem Backend mit einer server-Zeile je bereitem Endpoint. Mit mehreren IngressController-Objekten und routeSelector teilt man die Routes auf – intern vs extern, oder mehr Router-Pods gegen Last.- ein Controller im Router-Pod beobachtet
RouteundEndpointSlice(B3) und rendert daraus die/var/lib/haproxy/conf/haproxy.configaus einer Go-Template - nach jedem Rendern wird HAProxy graceful neu geladen – bestehende Verbindungen laufen aus
- jede der 3 Bibliothek-Routes (
library-frontend,keycloak,api-gateway) wird zu einem Frontend-Match auf den Host-Header (beiedge) plus einem Backend - im Backend steht eine
server-Zeile je bereitem Endpoint – ein not-readycatalog-service-Pod (B5) bekommt keine Zeile und damit keinen Verkehr - eine Route wird
Admitted, wenn ihr Host frei ist –HostAlreadyClaimedbei zwei Routes auf denselben Host - Sharding: mehrere
IngressController-Objekte, je einer mit eigenerdomain, eigenen Nodes und einemrouteSelectorodernamespaceSelector - typisch: ein interner und ein internetseitiger Router – auf der SNO mit einem Node und einem
router-default-Pod ist das gegenstandslos, es gibt genau einen - Feintuning je Route: Annotationen
haproxy.router.openshift.io/timeout,balance,disable_cookies,rate-limit-connections,ip_whitelist - global im
IngressController:tuningOptions,logging.access,tlsSecurityProfile,httpErrorCodePages,replicas - der Reload-Sturm: bei tausenden Routes rendert jede Änderung die ganze Datei neu → ständige Reloads, die
RELOAD_INTERVALbündelt – bei 3 Routes nie ein Thema - bei Rollouts (B1) flackern die
server-Zeilen – das erklärt die sporadischen 502/503 aus B4 - nachsehen auf der SNO:
oc rsh -n openshift-ingress deploy/router-default cat /var/lib/haproxy/conf/haproxy.config,oc get route -n library -o wide - Bibliothek: der Default-Router reicht – seine selbstsignierte Wildcard bedient alle 3
edge-Routes, ein zweiter IngressController wäre erst bei getrenntem internem und externem Zugang nötig - OCP-Unterschied: identisch – HAProxy-Router und IngressController-Operator sind Kern-OpenShift
- von Hand nachsehen: Route bis HAProxy verfolgen – eine eigene Lab-Route in der generierten Konfiguration finden, TLS-Termination umstellen und Sharding-Felder lesen
$hostName = oc get route destination -o jsonpath='{.spec.host}'
$hostName
curl.exe -i "http://$hostName"
oc get ingresscontroller default -n openshift-ingress-operator -o yaml
oc get deployment router-default -n openshift-ingress -o yaml
oc exec -n openshift-ingress deployment/router-default -- cat /var/lib/haproxy/conf/haproxy.config | Select-String -Pattern 'network-path-lab'Bash-Variante anzeigen
host_name=$(oc get route destination -o jsonpath='{.spec.host}')
printf '%s\n' "$host_name"
curl -i "http://$host_name"
oc get ingresscontroller default -n openshift-ingress-operator -o yaml
oc get deployment router-default -n openshift-ingress -o yaml
oc exec -n openshift-ingress deployment/router-default -- cat /var/lib/haproxy/conf/haproxy.config | grep -n 'network-path-lab'/var/lib/haproxy/conf/haproxy.config wird vom Router generiert. Du liest sie zur Diagnose, bearbeitest sie aber nie. Eine direkte Änderung würde beim nächsten Reconcile überschrieben.
TLS am eigenen Lab einschalten
oc patch route destination --type=merge -p '{"spec":{"tls":{"termination":"edge","insecureEdgeTerminationPolicy":"Redirect"}}}'
oc get route destination -o yaml
$hostName = oc get route destination -o jsonpath='{.spec.host}'
curl.exe -Ik "https://$hostName"Bash-Variante anzeigen
oc patch route destination --type=merge -p '{"spec":{"tls":{"termination":"edge","insecureEdgeTerminationPolicy":"Redirect"}}}'
oc get route destination -o yaml
host_name=$(oc get route destination -o jsonpath='{.spec.host}')
curl -Ik "https://$host_name"Bei edge endet TLS am Router. Zwischen Router und Pod läuft hier HTTP. reencrypt baut eine zweite TLS-Verbindung zum Backend auf. passthrough lässt die verschlüsselte Verbindung bis zum Pod durch und kann deshalb nur anhand des TLS-Servernamens auswählen.
9 Service-Mesh-Generation erkennen
OpenShift Service Mesh 3 nutzt den OpenShift Service Mesh Operator und Sail-APIs. Die frühere Generation nutzte Maistra-Ressourcen wie ServiceMeshControlPlane.
OpenShift Service Mesh ist Istio als Operator. In jeden Pod wird ein Envoy-Sidecar eingespritzt, das allen Verkehr abfängt. Der Sidecar bringt automatisches mTLS, feingranulares Routing (Canary, Retries, Timeouts) und L7-Telemetrie – kostet aber Latenz, Speicher und Betrieb. Für 8 Dienste ist das oft zu viel.
istiod konfiguriert alle Sidecars und verteilt kurzlebige Zertifikate. Zwischen den Sidecars läuft automatisch mTLS. Über CRDs (VirtualService, DestinationRule, Gateway) steuert man Routing, Canary und Retries ohne App-Code; Kiali zeigt den Graphen. Der Preis: Latenz, ~50–100 MiB je Pod und ein weiterer Betriebs-Stack – für 8 Dienste meist zu viel.- OpenShift Service Mesh = Istio als Operator-Paket – ein Envoy-Sidecar wird in jeden Pod eines Member-Namespace eingespritzt (Namespace-Label + Pod-Annotation)
- der Sidecar fängt allen ein- und ausgehenden Verkehr ab – die App redet nur mit
localhostund merkt vom Mesh nichts istiodist die Control Plane: konfiguriert alle Sidecars und verteilt kurzlebige mTLS-Zertifikate- automatisches mTLS zwischen den Sidecars (
PeerAuthentication STRICT) – Zero-Trust ohne App-Änderung - Traffic-Management über CRDs:
VirtualService(Routing, Canary-Anteil, Retries, Timeouts, Fault-Injection),DestinationRule(Subsets, Circuit-Breaking),Gateway(Mesh-Ingress statt Route) - Observability:
Kiali(Topologie-Graph), Tracing (Jaeger/Tempo), Metriken (Prometheus/Grafana, G1) - Retries und Timeouts im Mesh statt im App-Code – in der Bibliothek macht das heute das
api-gatewayselbst (Spring), das Mesh würde es über alle Dienste zentralisieren - wann es sich lohnt: viele Dienste, feines Traffic-Shifting, mTLS als Compliance-Vorgabe, reiche L7-Telemetrie – typisch ab rund 15 bis 20 Diensten
- wann Overkill: eine Handvoll Dienste, wo
NetworkPolicy(F2) plus Routes (B4) plus App-Retries reichen – genau der Bibliothek-Fall - der Preis: Latenz, ~50–100 MiB je Pod – bei einem guten Dutzend Bibliothek-Pods fast 1 GB nur für Sidecars, plus
istiod/Kiali/Jaeger als eigene Betriebsobjekte - Debugging wird schwerer: ein Hop mehr, die Envoy-Konfig lesen (
istioctl proxy-config) - Bibliothek: nicht nötig für das gute Dutzend Pods – notiert als Option, wenn die App wächst. Reiner mTLS-Bedarf zwischen zwei Diensten → erst serving-certs (F5)
- OCP-Unterschied: OSSM ist Istio als unterstützter Operator (
ServiceMeshControlPlane, neu dieIstio-CR) – Kubernetes pur ist Upstream-Istio von Hand - Generation und Bedarf prüfen: Service Mesh erkennen – Sail- und Maistra-Ressourcen unterscheiden, Sidecars im Pod nachweisen und gegen Service-CA oder NetworkPolicy abwägen
oc get csv -A | Select-String -Pattern 'servicemesh|sail|istio|maistra'
oc api-resources | Select-String -Pattern 'Istio|ServiceMeshControlPlane|ServiceMeshMemberRoll'
oc get istio.sailoperator.io -A
oc get istiorevision.sailoperator.io -A
oc get servicemeshcontrolplane.maistra.io -A
oc get pod -n network-path-lab -o jsonpath='{range .items[*]}{.metadata.name}{": "}{.spec.containers[*].name}{"\n"}{end}'Bash-Variante anzeigen
oc get csv -A | grep -Ei 'servicemesh|sail|istio|maistra'
oc api-resources | grep -E 'Istio|ServiceMeshControlPlane|ServiceMeshMemberRoll'
oc get istio.sailoperator.io -A
oc get istiorevision.sailoperator.io -A
oc get servicemeshcontrolplane.maistra.io -A
oc get pod -n network-path-lab -o jsonpath='{range .items[*]}{.metadata.name}{": "}{.spec.containers[*].name}{"\n"}{end}'| Fund | Bedeutung |
|---|---|
Istio und IstioRevision aus sailoperator.io | Service Mesh 3. Der Operator erzeugt aus der Istio-CR die Control Plane. |
ServiceMeshControlPlane aus maistra.io | frühere Service-Mesh-Generation. Keine Mesh-3-Manifeste blind darüber anwenden. |
Container istio-proxy im Pod | Sidecar-Datenpfad ist für diesen Pod aktiv. |
| keine dieser Ressourcen | kein Service Mesh installiert. OVN und Router funktionieren trotzdem vollständig. |
Nicht nebenbei installieren: Service Mesh 3 benötigt Operator, Istio-CR, IstioCNI und eine bewusst begrenzte Discovery-Konfiguration. Mesh 2 und Mesh 3 dürfen nicht unkontrolliert im selben Cluster betrieben werden.
10 Entscheiden, ob ein Mesh nötig ist
| Anforderung | kleinste passende Lösung |
|---|---|
| Nur interner Service-Aufruf und Lastverteilung | Service und OVN. Kein Mesh. |
| Öffentliche HTTP- oder TLS-Adresse | OpenShift Route und Router. Kein Mesh erforderlich. |
| Interne TLS-Zertifikate für wenige Dienste | Service-CA aus der Reconcile- und Service-CA-Anleitung. |
| Netzwerkzugriff zwischen Namespaces begrenzen | NetworkPolicy aus der NetworkPolicy-Anleitung. |
| Identitätsbasiertes mTLS, Traffic-Splitting und gemeinsame Telemetrie für viele Dienste | Service Mesh prüfen. |
Auf einer kleinen SNO kostet ein Sidecar pro Pod zusätzlichen RAM und CPU. Für die Bibliothek ist Service-CA plus NetworkPolicy meist die kleinere und verständlichere Lösung. Ein Mesh lohnt sich erst, wenn seine zentralen Regeln mehrere Anwendungen konsistent entlasten.
11 Fehler an der richtigen Schicht eingrenzen
- DNS: Löst
destination.network-path-lab.svcauf? - Service: Stimmen Selector, Port und
targetPort? - EndpointSlice: Gibt es eine bereite Pod-IP?
- NetworkPolicy: Ist Quell- oder Zielverkehr erlaubt?
- OVN: Sind Netzwerk-Operator und Node-Pod gesund? Was zeigt
ovnkube-trace? - Route: Hat der Router die Route in die generierte HAProxy-Konfiguration übernommen?
- Mesh: Ist ein Sidecar injiziert und akzeptieren mTLS- sowie AuthorizationPolicy-Regeln den Request?
Die Grundlagen für Service, Route und Readiness stehen in der Service-, Route- und Probes-Anleitung. Policy-Fehler werden in der NetworkPolicy-Anleitung praktisch isoliert.
12 Lab vollständig aufräumen
oc delete project network-path-lab
Falls du ovnkube-trace kopiert hast, entferne zusätzlich die lokale Binärdatei. Das Service Mesh und die OpenShift-Systemkomponenten wurden durch dieses Lab nicht verändert.