Kubernetes Gateway API Migration in 2026: From Ingress to Gateway API

云原生

Kubernetes Gateway API Migration in 2026: From Ingress to Gateway API

If you're still using Kubernetes Ingress to manage cluster traffic in 2026, you're missing the future of cloud-native traffic management. Gateway API officially became a Kubernetes GA API in late 2025, with major vendors (Istio, Contour, Traefik, Kong) fully supporting it. Ingress's design flaws—single-role, poor extensibility, inability to express complex routing—are no longer acceptable in today's exploding microservice scale.

Gateway API introduces role-separated APIs like Gateway, GatewayClass, and HTTPRoute, supporting multi-team collaboration, multi-cluster deployment, traffic splitting, and mirroring. This article starts with architecture comparison, provides complete migration steps, and covers traffic management, multi-tenancy, and multi-cluster scenarios.

Why Gateway API Replaces Ingress

Dimension Ingress Gateway API
API Maturity GA (but stopped evolving) GA (2025.11)
Role Separation None (all managed by cluster admin) Three roles (infra/cluster/app)
Routing Expressiveness Limited (path+host) Powerful (header/query/weight/mirror)
Multi-protocol Support HTTP/HTTPS only HTTP/TLS/TCP/UDP/gRPC
Traffic Splitting Requires annotation hack Native support (weight field)
Multi-tenancy Not supported Native support (Namespace isolation)
Extensibility Relies on annotations Native extension (PolicyAttachment)
Cross-cluster Not supported Multi-cluster Gateway support

Core difference: Ingress mixes all configuration together, while Gateway API achieves role separation—infrastructure teams manage GatewayClass, ops teams manage Gateway, dev teams manage Routes.


1. Architecture Comparison

1.1 Ingress Architecture

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /v1/users(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080

Problem: All configuration crammed into one Ingress object, annotations everywhere, vendor-specific annotations are incompatible.

1.2 Gateway API Architecture

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: istio
spec:
  controllerName: istio.io/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: api-gateway
  namespace: infra
spec:
  gatewayClassName: istio
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            shared-gateway-access: "true"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: user-route
  namespace: app-team
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /v1/users
    backendRefs:
    - name: user-service
      port: 8080
      weight: 90
    - name: user-service-canary
      port: 8080
      weight: 10

Advantage: Clear roles—GatewayClass defined by platform team, Gateway configured by ops team, HTTPRoute managed by dev team.


2. Step-by-Step Migration Guide

2.1 Install Gateway API CRDs

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml

2.2 Install Controller (Istio Example)

istioctl install --set profile=minimal
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: istio
spec:
  controllerName: istio.io/gateway-controller
EOF

2.3 Migrate Ingress to Gateway+HTTPRoute

Migration script approach:

for ingress in $(kubectl get ingress -A -o name); do
  namespace=$(echo $ingress | cut -d'/' -f1 | cut -d'.' -f1)
  name=$(echo $ingress | cut -d'/' -f2)

  kubectl get $ingress -o yaml | python3 ingress2gateway.py > gateway-route.yaml
  kubectl apply -f gateway-route.yaml

  echo "Migrated $ingress, verify before deleting"
done

2.4 Automated Migration Tool

import yaml
import sys

def ingress_to_gateway_route(ingress_data):
    metadata = ingress_data['metadata']
    spec = ingress_data['spec']

    gateway = {
        'apiVersion': 'gateway.networking.k8s.io/v1',
        'kind': 'Gateway',
        'metadata': {
            'name': f"{metadata['name']}-gateway",
            'namespace': metadata.get('namespace', 'default'),
        },
        'spec': {
            'gatewayClassName': 'istio',
            'listeners': [],
        },
    }

    routes = []
    for rule in spec.get('rules', []):
        host = rule.get('host', '*')
        listener_name = f"http-{host.replace('.', '-')}" if host != '*' else 'http'

        gateway['spec']['listeners'].append({
            'name': listener_name,
            'port': 80,
            'protocol': 'HTTP',
            'hostname': host if host != '*' else None,
        })

        for path in rule.get('http', {}).get('paths', []):
            route = {
                'apiVersion': 'gateway.networking.k8s.io/v1',
                'kind': 'HTTPRoute',
                'metadata': {
                    'name': f"{metadata['name']}-route",
                    'namespace': metadata.get('namespace', 'default'),
                },
                'spec': {
                    'parentRefs': [{'name': f"{metadata['name']}-gateway"}],
                    'rules': [{
                        'matches': [{
                            'path': {
                                'type': 'PathPrefix',
                                'value': path.get('path', '/'),
                            }
                        }],
                        'backendRefs': [{
                            'name': path['backend']['service']['name'],
                            'port': path['backend']['service']['port']['number'],
                        }],
                    }],
                },
            }
            routes.append(route)

    return gateway, routes

if __name__ == '__main__':
    ingress = yaml.safe_load(sys.stdin)
    gw, routes = ingress_to_gateway_route(ingress)
    for doc in [gw] + routes:
        print('---')
        print(yaml.dump(doc, default_flow_style=False))

3. Traffic Management

3.1 Traffic Splitting (Canary Release)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
spec:
  parentRefs:
  - name: api-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/v2
    backendRefs:
    - name: api-v2-stable
      port: 8080
      weight: 95
    - name: api-v2-canary
      port: 8080
      weight: 5

3.2 Traffic Mirroring

apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRouteFilter
metadata:
  name: mirror-filter
spec:
  type: RequestMirror
  requestMirror:
    backendRef:
      name: api-mirror
      port: 8080

3.3 Header-based Routing

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-route
spec:
  parentRefs:
  - name: api-gateway
  rules:
  - matches:
    - headers:
      - type: Exact
        name: X-Feature-Flag
        value: new-ui
    backendRefs:
    - name: frontend-v2
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: frontend-v1
      port: 80

3.4 gRPC Routing

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: grpc-route
spec:
  parentRefs:
  - name: api-gateway
  rules:
  - matches:
    - method:
        service: "com.example.UserService"
        method: "GetUser"
    backendRefs:
    - name: user-grpc-service
      port: 50051

4. Multi-cluster and Multi-tenant Scenarios

4.1 Multi-cluster Gateway

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-cross-namespace
  namespace: app-team
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: infra
  to:
  - group: ""
    kind: Service

4.2 Multi-tenant Isolation

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tenant-a-gateway
  namespace: tenant-a
spec:
  gatewayClassName: istio
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Same

Tenant Isolation Strategy Comparison:

Strategy Isolation Level Resource Overhead Management Complexity Use Case
Shared Gateway Route-level Low Low Small scale, trusted tenants
Namespace Isolation Namespace-level Medium Medium Medium scale, general isolation
Dedicated Gateway Gateway-level High High Large scale, strong isolation

5 Common Pitfalls

# Pitfall Consequence Solution
1 Delete Ingress before creating Gateway Traffic interruption Run in parallel, verify before deleting
2 HTTPRoute references Gateway in another Namespace Permission denied Create ReferenceGrant
3 Gateway's allowedRoutes too restrictive Route cannot bind Check namespace selector
4 Ignoring GatewayClass parameters Controller behavior unexpected Read GatewayClassSpec.parametersRef carefully
5 Traffic splitting weight sum ≠ 100 Undefined behavior (implementation-dependent) Ensure weight sum is 100

10 Error Troubleshooting Items

# Error Symptom Possible Cause Troubleshooting Method
1 HTTPRoute Parents condition Accepted: False Gateway doesn't allow this Namespace Check Gateway's allowedRoutes
2 Traffic not reaching backend Route matching rules incorrect Use kubectl describe httproute to check ResolvedRefs
3 GatewayNotReady Controller not installed or running Check GatewayClass and controller Pod
4 TLS certificate not working Certificate Secret missing or wrong format Check Secret type is kubernetes.io/tls
5 Canary traffic ratio wrong Weight calculation error Confirm weight sum is 100 and ratio is correct
6 Cross-Namespace reference denied Missing ReferenceGrant Create ReferenceGrant allowing cross-Namespace reference
7 GRPCRoute not working Gateway listener not configured for H2C Add protocol: HTTP listener with H2C enabled
8 Traffic mirroring not working Controller doesn't support RequestMirror Confirm controller implementation supports this extension
9 Gateway IP not assigned Controller not configured for LB Check controller's Service and LoadBalancer
10 Performance degradation after migration Controller config not optimized Compare Ingress and Gateway proxy configuration

Tool Recommendations

During Gateway API migration, these tools help with configuration and encoding tasks:

  • JSON Formatter — Format Gateway API Status and condition information for debugging route status
  • Base64 Encoder — Base64 encode TLS certificates and keys for Kubernetes Secrets
  • Hash Calculator — Generate version fingerprints for Gateway configuration for change detection and rollback decisions

Summary: Gateway API isn't an "upgrade" to Ingress—it's a "redesign" of traffic management. Three-role separation lets platform, ops, and dev teams each manage their own scope; native traffic splitting makes canary releases work without annotation hacks; multi-protocol support lets gRPC and TCP services enjoy unified traffic management. In 2026, Ingress has entered maintenance mode—Gateway API is the only direction forward. The core migration principle: run in parallel first, then verify, then switch—don't do it all at once. Let Ingress and Gateway API coexist for a while, confirm traffic is healthy, then remove Ingress.

Try these browser-local tools — no sign-up required →

#K8s Gateway API#Ingress迁移#服务网格#流量管理#2026