Skip to content

Gateway API cho Multi-Cluster Routing

Tại sao Gateway API Thay Thế MCI Custom Resources

MCI (Multi-Cluster Ingress) là GCP-specific resource. Nó hoạt động, nhưng:

yaml
# MCI (GCP-only)
apiVersion: cloud.google.com/v1
kind: MultiClusterIngress
metadata:
  name: app

# Gateway API (Kubernetes standard)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: app-gateway

Gateway API: Kubernetes standard abstraction cho ingress/routing. Nó replace MCI custom resources & cung cấp:

  1. Portability: Same YAML works trên GKE, EKS, AKS, on-prem Kubernetes
  2. Rich routing: HTTPRoute (path/host/header-based), TCPRoute, TLSRoute, UDPRoute
  3. Resource model: GatewayClass → Gateway → Routes (better separation of concerns)
  4. Multi-namespace: Route từ multiple namespaces bisa bind ke single Gateway

Mental model:

MCI = "Ingress v1 para multi-cluster"
Gateway API = "Ingress v2++ with routing DSL"

Resource Model

GatewayClass: Infrastructure Provider Abstraction

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: gke-l7
spec:
  controllerName: gke.io/l7-gxlb  # GCP Global HTTP(S) LB controller
  description: GKE Global HTTP(S) Load Balancer

Ý nghĩa: "Khi tôi tạo Gateway dengan gatewayClassName=gke-l7, hãy sử dụng GCP's global LB".

Khác với ingress.class, GatewayClass rõ ràng khai báo controller provider.


Gateway: Ingress Entry Point

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: app-gateway
  namespace: default
spec:
  gatewayClassName: gke-l7
  listeners:
    - name: http
      port: 80
      protocol: HTTP
    - name: https
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: app-cert
            namespace: default
status:
  addresses:
    - type: IPAddress
      value: 1.2.3.4  # Global anycast IP (same as MCI)
  listeners:
    - name: http
      supportedKinds:
        - group: gateway.networking.k8s.io
          kind: HTTPRoute

Cơ chế:

  1. Gateway resource tạo global LB (cùng ip như MCI)
  2. Controller watches để HTTPRoute resources
  3. HTTPRoute define routing rules (path/host/header)
  4. Routes bind to backendServices

HTTPRoute: Routing Rules

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-routes
  namespace: backend
spec:
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: app-gateway
      namespace: default
  hostnames:
    - "api.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api/v1
      backendRefs:
        - group: ""
          kind: Service
          name: api-service
          port: 8080
          weight: 100  # 100% traffic to this service
    - matches:
        - path:
            type: PathPrefix
            value: /frontend
      backendRefs:
        - group: ""
          kind: Service
          name: frontend-service
          port: 3000
          weight: 100

Routing logic:

Client: GET http://api.example.com/api/v1/users

1. Gateway matches hostname: api.example.com ✓
2. HTTPRoute checks path: /api/v1 ✓
3. Backend: api-service (port 8080)
4. Service discovery: MCS ServiceImport untuk multi-cluster
5. LB route to endpoints (via NEG)

Multi-Cluster Configuration

Cross-Cluster BackendRef

yaml
# Config Cluster: HTTPRoute yang target services ở member clusters
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: global-app-routes
  namespace: default
spec:
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: app-gateway
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: api-service
          namespace: backend  # Cross-namespace
          weight: 50
        - group: net.gke.io
          kind: ServiceExport  # Cross-cluster!
          name: api-service
          namespace: backend
          weight: 50

Cơ chế:

HTTPRoute backendRef:
  → kind: Service (local)
  → kind: ServiceExport (via MCS, target multi-cluster)

When backendRef specifies ServiceExport:
  → GKE Gateway controller looks for matching ServiceImport
  → Finds MCS endpoints từ cluster A, B, C
  → Creates NEG entries
  → LB route to multi-cluster endpoints

Traffic Splitting Across Clusters

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-split
  namespace: default
spec:
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: api-v1  # Stable version
          weight: 90    # 90% traffic
        - group: ""
          kind: Service
          name: api-v2  # Canary version
          weight: 10    # 10% traffic

Canary deployment:

Cluster A (v1.0): 90% traffic
Cluster B (v1.1): 10% traffic

Monitor metrics:
  - Error rate: v1.1 < 0.5%? OK
  - Latency p99: v1.1 < 50ms? OK
  
If OK: increase weight 10→50→100
If FAIL: decrease weight 10→0 (drain)

Advanced Routing: Host, Path, Header-Based

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: advanced-routing
spec:
  rules:
    # Rule 1: Exact host
    - matches:
        - hostname: "internal.example.com"
      backendRefs:
        - name: internal-service
          port: 8080
    
    # Rule 2: Path + Method
    - matches:
        - path:
            type: PathPrefix
            value: /api/admin
          method: POST
      backendRefs:
        - name: admin-service
          port: 9000
    
    # Rule 3: Header-based routing (canary by user)
    - matches:
        - headers:
            - name: X-Canary
              value: "true"
      backendRefs:
        - name: api-v2-canary
          port: 8080
          weight: 100
    
    # Rule 4: Default
    - backendRefs:
        - name: api-v1-stable
          port: 8080
          weight: 100

Request flow:

GET /api/admin (POST)
  Header: X-Canary: true
  
1. Check hostname: default (no match)
2. Check /api/admin + POST: ✓ Match rule 2
3. Route to: admin-service

Constraints & Best Practices

Gateway API Maturity

  • GA (General Availability): HTTPRoute, TCPRoute (v1)
  • Experimental: Service binding, policy attachment
  • Not yet: Dynamic weight changes (use headless service + MCS locality instead)

Cross-Cluster Limitations

  • Same project requirement: All clusters must be in same GCP project
  • VPC peering must be configured: Gateway controller needs reach pod endpoints
  • Service name consistency: Service di cluster A & B harus same name di same namespace
  • No cross-namespace ServiceExport: ServiceExport hanya visible di same namespace

Production Patterns

✅ Pattern: Canary dengan Gateway API + ServiceExport

yaml
# Stable version (Cluster A)
apiVersion: v1
kind: Service
metadata:
  name: app
  namespace: default
spec:
  selector:
    app: app
    version: v1.0
  ports:
    - port: 8080
      name: http
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
  name: app
  namespace: default
---
# Canary version (Cluster B)
apiVersion: v1
kind: Service
metadata:
  name: app
  namespace: default
spec:
  selector:
    app: app
    version: v1.1
  ports:
    - port: 8080
      name: http
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
  name: app
  namespace: default
---
# Config Cluster: Gateway + routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-canary
  namespace: default
spec:
  rules:
    - backendRefs:
        - kind: Service
          name: app
          namespace: default
          weight: 90
        - kind: Service
          name: app  # Same name, different cluster via MCS
          namespace: default
          weight: 10

Workflow:

1. Deploy v1.1 to Cluster B
2. ServiceExport immediately visible via MCS
3. Gateway controller adds to NEG (weight: 10)
4. Monitor error rate: if OK, increase weight
5. Gradually: 10→25→50→100
6. Promote: v1.1 becomes stable

✅ Pattern: Path-Based Routing to Services

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-router
spec:
  rules:
    # /api/* → API service
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api-service
          port: 8080
    
    # /static/* → Frontend service
    - matches:
        - path:
            type: PathPrefix
            value: /static
      backendRefs:
        - name: frontend-service
          port: 3000
    
    # Default → Homepage
    - backendRefs:
        - name: homepage-service
          port: 8080

❌ Anti-Pattern: Mixing Gateway API + MCI

yaml
# WRONG: Using both Gateway API AND MultiClusterIngress
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
spec:
  gatewayClassName: gke-l7

---

apiVersion: cloud.google.com/v1
kind: MultiClusterIngress
spec:
  backend:
    serviceName: app

Problem: Dua resources compete untuk same global LB → undefined behavior.

Fix: Use ONLY Gateway API (modern, recommended).


Debugging Gateway API

Check Gateway Status

bash
kubectl get gateway -n default
NAME          CLASS     ADDRESS   READY
app-gateway   gke-l7    1.2.3.4   True

# Detailed status
kubectl describe gateway app-gateway -n default
Status:
  Addresses:
    - Type: IPAddress
      Value: 1.2.3.4
  Listeners:
    - Name: http
      Conditions:
        - Type: Accepted
          Status: "True"
          Reason: Accepted
        - Type: Programmed
          Status: "True"
          Reason: Programmed

Check HTTPRoute Status

bash
kubectl get httproute -n default
NAME           HOSTNAMES            STATUS
app-routes     ["api.example.com"]   Accepted

# Check if route is accepted
kubectl describe httproute app-routes -n default
Status:
  Parents:
    - Name: app-gateway
      Conditions:
        - Type: Accepted
          Status: "True"
        - Type: ResolvedRefs
          Status: "True"

Verify Backend Resolution

bash
# Check if ServiceExport is visible from config cluster
kubectl get serviceexport -A
NAMESPACE   NAME           AGE
backend     api-service    5m

# Check MCS ServiceImport (auto-created)
kubectl get serviceimport -n backend
NAME           CLUSTER-IPS      AGE
api-service    [172.30.1.100]   5m

# Verify NEG created
gcloud compute network-endpoint-groups list | grep app

Comparison: MCI vs Gateway API

FeatureMCIGateway API
PortabilityGCP onlyK8s standard
Routing rulesBasic (service only)Advanced (HTTPRoute)
Resource modelFlat (MCIngress + MCSvc)Hierarchical (GatewayClass → Gateway → Routes)
Multi-namespace routesNoYes (HTTPRoute in different ns)
Traffic splittingNEG weightsHTTPRoute weights
Status visibilityLimitedRich (conditions, ready status)
AdoptionDecliningRising (GA in K8s 1.30+)

Summary

Gateway API adalah modern replacement untuk MCI custom resources. Nó provide:

  1. Standard Kubernetes abstraction (gateway.networking.k8s.io)
  2. Rich routing rules (path, host, method, header-based)
  3. Cross-namespace support (routes from multiple namespaces bind to single gateway)
  4. Better visibility (status conditions, debugging)
  5. Future-proof (evolving K8s standard, not GCP-specific)

Recommendation: Use Gateway API cho new multi-cluster deployments. MCI masih supported, tapi Gateway API adalah future direction.

References