Gateway API Advanced Patterns: Beyond Basic Ingress
Gateway API is the successor to Ingress. It separates concerns between infrastructure operators, cluster operators, and app developers. This guide covers advanced patterns you can’t do with traditional Ingress.
TL;DR
- Gateway API = role-based ingress configuration
- Traffic splitting for canary deployments
- Header-based routing for A/B testing
- Cross-namespace references with ReferenceGrants
- TLS passthrough for end-to-end encryption
Role Separation
ROLE RESOURCE RESPONSIBILITY
==== ======== ==============
Infrastructure Admin GatewayClass Define infrastructure
Cluster Operator Gateway Deploy/configure gateways
App Developer HTTPRoute/TCPRoute Define routing rules
┌─────────────────────────────────────────────────────────────────┐
│ GatewayClass (Infra Admin) │
│ "Which controller? What features?" │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Gateway (Cluster Operator) │
│ "Which ports? What TLS? Which IPs?" │
└─────────────────────────────────────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ HTTPRoute │ │ HTTPRoute │ │ TCPRoute │
│ (Team A) │ │ (Team B) │ │ (Team C) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Install Gateway API
# Install CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
# Install experimental features (TCPRoute, TLSRoute)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/experimental-install.yaml
Basic Setup
# GatewayClass - defines the controller
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: production
spec:
controllerName: gateway.nginx.org/nginx-gateway-controller # or cilium, istio, etc.
---
# Gateway - the actual load balancer
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: gateway-system
spec:
gatewayClassName: production
listeners:
- name: http
port: 80
protocol: HTTP
hostname: "*.company.com"
allowedRoutes:
namespaces:
from: All
- name: https
port: 443
protocol: HTTPS
hostname: "*.company.com"
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
allowedRoutes:
namespaces:
from: All
Traffic Splitting (Canary)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-canary
namespace: production
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
hostnames:
- "api.company.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: api-stable
port: 8080
weight: 90
- name: api-canary
port: 8080
weight: 10
Progressive rollout:
# Start: 100% stable
weight: 100 / 0
# Phase 1: 10% canary
weight: 90 / 10
# Phase 2: 50% canary
weight: 50 / 50
# Phase 3: 100% canary
weight: 0 / 100
# Promote: rename canary to stable
Header-Based Routing
Route based on headers for A/B testing or feature flags:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: feature-routing
namespace: production
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
hostnames:
- "app.company.com"
rules:
# Beta users (header-based)
- matches:
- headers:
- name: X-Beta-User
value: "true"
backendRefs:
- name: app-beta
port: 8080
# Internal testing (header-based)
- matches:
- headers:
- name: X-Internal
value: "true"
- name: X-Test-Group
value: "experiment-123"
backendRefs:
- name: app-experiment
port: 8080
# Default
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-stable
port: 8080
Query Parameter Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: query-routing
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
rules:
# Route by version query param
- matches:
- queryParams:
- name: version
value: v2
backendRefs:
- name: api-v2
port: 8080
# Route by debug flag
- matches:
- queryParams:
- name: debug
value: "true"
backendRefs:
- name: api-debug
port: 8080
Method-Based Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: method-routing
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
hostnames:
- "api.company.com"
rules:
# Reads go to replicas
- matches:
- method: GET
path:
type: PathPrefix
value: /api/
backendRefs:
- name: api-read-replica
port: 8080
# Writes go to primary
- matches:
- method: POST
path:
type: PathPrefix
value: /api/
- method: PUT
path:
type: PathPrefix
value: /api/
- method: DELETE
path:
type: PathPrefix
value: /api/
backendRefs:
- name: api-primary
port: 8080
Cross-Namespace References
Allow routes in one namespace to reference services in another:
# In the service namespace (backend)
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-frontend-namespace
namespace: backend
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: frontend
to:
- group: ""
kind: Service
---
# In the frontend namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: frontend-route
namespace: frontend
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
rules:
- matches:
- path:
type: PathPrefix
value: /api/
backendRefs:
- name: api-service
namespace: backend # Cross-namespace reference
port: 8080
TLS Passthrough
For end-to-end encryption where the gateway doesn’t terminate TLS:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: passthrough-gateway
spec:
gatewayClassName: production
listeners:
- name: tls-passthrough
port: 443
protocol: TLS
hostname: "secure.company.com"
tls:
mode: Passthrough
allowedRoutes:
kinds:
- kind: TLSRoute
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: secure-passthrough
spec:
parentRefs:
- name: passthrough-gateway
hostnames:
- "secure.company.com"
rules:
- backendRefs:
- name: secure-backend
port: 8443
Request/Response Modification
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-modification
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
# Add request headers
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Request-ID
value: "${request_id}"
- name: X-Forwarded-Proto
value: https
set:
- name: Host
value: internal-api.default.svc.cluster.local
remove:
- X-Internal-Token
# Add response headers
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: X-Frame-Options
value: DENY
- name: X-Content-Type-Options
value: nosniff
backendRefs:
- name: api
port: 8080
URL Rewriting
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: url-rewrite
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
hostnames:
- "api.company.com"
rules:
# Rewrite /v1/* to /*
- matches:
- path:
type: PathPrefix
value: /v1/
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-v1
port: 8080
# Rewrite /legacy/* to /api/v0/*
- matches:
- path:
type: PathPrefix
value: /legacy/
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /api/v0/
backendRefs:
- name: legacy-api
port: 8080
Redirects
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirects
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
rules:
# HTTP to HTTPS redirect
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
# Domain redirect
- matches:
- headers:
- name: Host
value: old.company.com
filters:
- type: RequestRedirect
requestRedirect:
hostname: new.company.com
statusCode: 301
Timeouts and Retries
Using BackendRef extensions:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: with-timeouts
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
rules:
- matches:
- path:
type: PathPrefix
value: /api/
timeouts:
request: 30s
backendRequest: 10s
backendRefs:
- name: api
port: 8080
TCP/UDP Routes
# TCP Route for databases
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: postgres-route
spec:
parentRefs:
- name: tcp-gateway
rules:
- backendRefs:
- name: postgres
port: 5432
---
# Gateway with TCP listener
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tcp-gateway
spec:
gatewayClassName: production
listeners:
- name: postgres
port: 5432
protocol: TCP
allowedRoutes:
kinds:
- kind: TCPRoute
Multi-Gateway Setup
# Internal gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: internal-gateway
namespace: gateway-system
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
spec:
gatewayClassName: internal
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: internal
---
# External gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external-gateway
namespace: gateway-system
spec:
gatewayClassName: external
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: external
Troubleshooting
# Check Gateway status
kubectl get gateway -A
# Check HTTPRoute status
kubectl get httproute -A -o wide
# Check attached routes
kubectl describe gateway production-gateway
# Check if routes are accepted
kubectl get httproute my-route -o jsonpath='{.status.parents}'
References
- Gateway API Docs: https://gateway-api.sigs.k8s.io
- Implementations: https://gateway-api.sigs.k8s.io/implementations/
- GEPs (Enhancement Proposals): https://gateway-api.sigs.k8s.io/geps/