Hub Gateway
The hub gateway pattern provides centralized HTTP ingress on the hub cluster with behaviors similar to an F5 BIG-IP ADC: VIP-style routing, TLS termination at the edge, and weighted traffic splits across backend services or spoke-derived routes.
Gateway API theory
Kubernetes Gateway API separates concerns:
Gateway— listens on addresses and ports; attaches TLS and listener policies.HTTPRoute— attaches to aGatewayand selects backendServiceresources with matches, filters, and weighted backends.
Weighted rules enable canary or active-active distributions between service versions or regions when paired with mesh or multi-cluster DNS strategies.
Cross-cluster routing architecture
The hub gateway routes traffic to spoke cluster OpenShift Routes via ExternalName services:
flowchart LR
Browser["Browser"] --> Router["Hub OpenShift<br/>Router (HTTPS)"]
Router --> IGW["Istio Gateway<br/>(port 8080)"]
IGW --> WP["Waypoint Proxy<br/>(circuit breaking)"]
WP --> EXT_E["ExternalName<br/>Service (east)"]
WP --> EXT_W["ExternalName<br/>Service (west)"]
EXT_E --> SPOKE_E["East OpenShift<br/>Router (HTTP:80)"]
EXT_W --> SPOKE_W["West OpenShift<br/>Router (HTTP:80)"]
SPOKE_E --> POD_E["Backend Pod"]
SPOKE_W --> POD_W["Backend Pod"]
Front / API split
Traffic is split into separate Service objects per cluster and traffic type to give Kiali and Grafana finer-grained visibility:
| Service | Purpose |
|---|---|
industrial-edge-east-front | Static frontend assets for east spoke |
industrial-edge-east-api | Socket.IO / API backend for east spoke |
industrial-edge-west-front | Static frontend assets for west spoke |
industrial-edge-west-api | Socket.IO / API backend for west spoke |
All four services use ExternalName pointing to the same spoke Route hostname, but Istio tracks them as distinct destinations. In Kiali’s traffic graph, each appears as a separate node.
The HTTPRoute uses two rules:
flowchart TB
REQ["Incoming Request"] --> MATCH{Path?}
MATCH -->|"/api/*"| API["API rule<br/>(session affinity)"]
MATCH -->|"/*"| FRONT["Frontend rule<br/>(weighted split)"]
API --> API_E["east-api<br/>weight: 100"]
API --> API_W["west-api<br/>weight: 0"]
FRONT --> FRONT_E["east-front<br/>weight: 50"]
FRONT --> FRONT_W["west-front<br/>weight: 50"]
/apiprefix → routed to*-apiservices (defaults to east 100%, west 0% for Socket.IO session affinity).- Catch-all → routed to
*-frontservices (split bygateway.weights.east/west).
Override API weights with gateway.apiWeights in values when your application supports cross-cluster API load balancing.
Key requirements
-
HTTP port 80, not HTTPS — Istio ambient mode gateways do not apply
DestinationRuleTLS settings. Using HTTPS causesCERTIFICATE_VERIFY_FAILEDerrors. Spoke Routes must setinsecureEdgeTerminationPolicy: Allow. -
ServiceEntry for each external host — without a
ServiceEntry, Envoy has no cluster definition for the external hostname and returns HTTP 500. -
Per-backend Host header rewrite — the spoke OpenShift router routes by
Hostheader. UseRequestHeaderModifierfilters on eachbackendRefin the HTTPRoute. -
Session affinity for Socket.IO — when load-balancing across multiple backends, Socket.IO polling and WebSocket upgrade must reach the same backend. The
/apirule pins to a single spoke by default.
Circuit breaking
Each ExternalName service gets a DestinationRule with outlierDetection and connectionPool settings, enforced by the hub-gateway-system-waypoint proxy (Istio ambient L7).
Default configuration (aggressive / demo)
| Parameter | Default | Purpose |
|---|---|---|
connectionPool.tcp.maxConnections | 100 | Max concurrent TCP connections |
connectionPool.http.h2UpgradePolicy | DO_NOT_UPGRADE | Spokes expect HTTP/1.1 |
connectionPool.http.maxRequestsPerConnection | 10 | Force connection recycling |
outlierDetection.consecutive5xxErrors | 3 | Eject after 3 consecutive 5xx |
outlierDetection.interval | 10s | Health check interval |
outlierDetection.baseEjectionTime | 30s | Minimum ejection duration |
outlierDetection.maxEjectionPercent | 100 | Allow ejecting the only endpoint |
maxEjectionPercent: 100 is required because each ExternalName service resolves to a single endpoint; without it, Istio refuses to eject the last remaining host.
Overriding circuit breaker values
Set gateway.circuitBreaking.* in the hub-gateway values:
gateway:
circuitBreaking:
enabled: true
outlierDetection:
consecutive5xxErrors: 5
baseEjectionTime: 60s
Set enabled: false to disable circuit breaking entirely.
Relationship to Connectivity Link
Connectivity Link (Kuadrant) layers DNS automation, TLS policies, and advanced controls atop Gateway API. Start with plain HTTPRoutes if you need incremental adoption; enable Kuadrant policies when teams are ready.
IoT Dashboard integration
The Industrial Edge line-dashboard (iot-frontend) requires an iot-consumer sidecar to display sensor data:
- iot-consumer bridges MQTT to WebSocket via Socket.IO
- Mount a ConfigMap
config.jsonwithwebsocketHost: ""(empty = same origin) - Path-based Route
/apito port 3000 (iot-consumer) - The hub gateway proxies
/apirequests to the correct spoke backend where iot-consumer handles the Socket.IO connection
Operational notes
- Keep hostnames aligned with
deployer.domainand corporate DNS wildcard records. - Combine with Service Mesh ambient when east-west encryption between gateway hops and workloads matters.
- Monitor the gateway Envoy proxy metrics at port 15020
/stats/prometheus.
Next: attach HTTPRoutes per Connectivity Link policies when Kuadrant enforcement tightens, then verify backends inside Service Mesh ambient namespaces.
References
Implementation chart: charts/all/hub-gateway.
Next → Observability for Grafana dashboards, Kiali traffic graphs, and Kafka Console across clusters.