Split View: Kubernetes Gateway API 실전 마이그레이션 — Ingress에서 Gateway로
Kubernetes Gateway API 실전 마이그레이션 — Ingress에서 Gateway로

들어가며
Kubernetes의 네트워킹 스택이 진화하고 있습니다. 오랫동안 표준이었던 Ingress API는 이제 Gateway API로 대체되는 흐름입니다. Gateway API는 2023년 GA(General Availability)를 달성했고, 2025년부터는 대부분의 Ingress Controller가 Gateway API를 지원하기 시작했습니다.
이 글에서는 기존 Ingress 리소스를 Gateway API로 마이그레이션하는 실전 과정을 단계별로 살펴봅니다.
Ingress vs Gateway API: 왜 전환해야 하나?
Ingress의 한계
Ingress API는 단순한 HTTP 라우팅에는 충분하지만, 다음과 같은 한계가 있습니다:
- 역할 분리 불가: 인프라 관리자와 애플리케이션 개발자의 관심사가 하나의 리소스에 섞임
- 제한된 프로토콜: HTTP/HTTPS만 지원, TCP/UDP/gRPC는 비표준 어노테이션 필요
- 벤더 종속: nginx, traefik 등 구현체마다 다른 어노테이션
- 헤더 기반 라우팅 미지원: 가중치 기반 트래픽 분할 불가
Gateway API의 장점
┌─────────────────────────────────────┐
│ GatewayClass │ ← 인프라 제공자 (플랫폼 팀)
├─────────────────────────────────────┤
│ Gateway │ ← 클러스터 운영자
├─────────────────────────────────────┤
│ HTTPRoute / TCPRoute / GRPCRoute │ ← 애플리케이션 개발자
└─────────────────────────────────────┘
- 역할 기반 설계: GatewayClass → Gateway → Route 3계층으로 관심사 분리
- 풍부한 라우팅: 헤더, 쿼리 파라미터, 메서드 기반 매칭
- 멀티 프로토콜: HTTP, gRPC, TCP, UDP, TLS 네이티브 지원
- 이식성: 표준 API로 벤더 종속 최소화
핵심 리소스 이해하기
GatewayClass
인프라 제공자가 정의하는 최상위 리소스입니다. 어떤 컨트롤러가 Gateway를 관리할지 지정합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx
spec:
controllerName: gateway.nginx.org/nginx-gateway-controller
Gateway
실제 리스너(포트, 프로토콜)를 정의합니다. 클러스터 운영자가 관리합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: infra
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-cert
kind: Secret
allowedRoutes:
namespaces:
from: All
HTTPRoute
애플리케이션 개발자가 라우팅 규칙을 정의합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-route
namespace: app-ns
spec:
parentRefs:
- name: main-gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: frontend-service
port: 3000
실전 마이그레이션 단계
Step 1: Gateway API CRD 설치
# Gateway API v1.2 CRD 설치
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
# 실험적 기능 포함 (TCPRoute, TLSRoute 등)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml
# 설치 확인
kubectl get crd | grep gateway
Step 2: 기존 Ingress 분석
마이그레이션 전에 기존 Ingress를 파악합니다:
# 모든 Ingress 리소스 조회
kubectl get ingress -A -o wide
# 특정 Ingress 상세 확인 (어노테이션 포함)
kubectl get ingress my-ingress -o yaml
기존 Ingress 예시:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 3000
Step 3: ingress2gateway 도구 활용
Kubernetes SIG에서 제공하는 공식 변환 도구를 활용합니다:
# ingress2gateway 설치
go install github.com/kubernetes-sigs/ingress2gateway@latest
# 현재 클러스터의 Ingress를 Gateway API로 변환
ingress2gateway print --all-namespaces
# 특정 네임스페이스만 변환
ingress2gateway print --namespace production
# 파일로 출력
ingress2gateway print --namespace production > gateway-resources.yaml
Step 4: 변환된 리소스 검증 및 적용
# dry-run으로 검증
kubectl apply -f gateway-resources.yaml --dry-run=server
# 적용
kubectl apply -f gateway-resources.yaml
# 상태 확인
kubectl get gateway -A
kubectl get httproute -A
kubectl describe gateway main-gateway -n infra
Step 5: 트래픽 분할 (카나리 배포)
Gateway API만의 강력한 기능인 가중치 기반 트래픽 분할:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: main-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-v1
port: 8080
weight: 90
- name: app-v2
port: 8080
weight: 10
Step 6: 헤더 기반 라우팅
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-route
spec:
parentRefs:
- name: main-gateway
rules:
- matches:
- headers:
- name: X-Version
value: beta
backendRefs:
- name: app-beta
port: 8080
- backendRefs:
- name: app-stable
port: 8080
점진적 전환 전략
Ingress와 Gateway API는 동일 클러스터에서 공존할 수 있습니다:
- Phase 1: Gateway API CRD 설치 + GatewayClass/Gateway 생성
- Phase 2: 신규 서비스부터 HTTPRoute 사용
- Phase 3: 기존 Ingress를 하나씩 HTTPRoute로 전환
- Phase 4: 모든 Ingress 제거 후 Ingress Controller 정리
# 전환 진행률 모니터링
echo "Ingress: $(kubectl get ingress -A --no-headers | wc -l)"
echo "HTTPRoute: $(kubectl get httproute -A --no-headers | wc -l)"
주의사항 및 트러블슈팅
어노테이션 매핑
Ingress 어노테이션은 Gateway API에서 Policy 리소스나 Filter로 대체됩니다:
| Ingress 어노테이션 | Gateway API 대응 |
|---|---|
rewrite-target | URLRewriteFilter |
ssl-redirect | Gateway listener에서 HTTP→HTTPS 리다이렉트 |
rate-limit | BackendPolicy 또는 구현체별 Policy |
cors | HTTPRoute 필터 또는 Policy |
# URL Rewrite 예시
rules:
- matches:
- path:
type: PathPrefix
value: /old-api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /new-api
backendRefs:
- name: api-svc
port: 8080
흔한 실수
- parentRefs 네임스페이스 누락: Gateway와 HTTPRoute가 다른 네임스페이스에 있으면 반드시 namespace 지정
- allowedRoutes 미설정: Gateway에서 다른 네임스페이스의 Route를 허용하려면
from: All설정 필요 - ReferenceGrant 누락: 크로스 네임스페이스 백엔드 참조 시 ReferenceGrant 필요
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-route-from-app
namespace: backend-ns
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: app-ns
to:
- group: ""
kind: Service
마무리
Gateway API는 단순한 Ingress의 대체가 아니라, Kubernetes 네트워킹의 패러다임 전환입니다. 역할 기반 설계, 풍부한 라우팅 기능, 멀티 프로토콜 지원으로 더 안전하고 유연한 트래픽 관리가 가능합니다. 기존 Ingress와 공존할 수 있으므로, 지금부터 점진적으로 전환을 시작하는 것을 추천합니다.
퀴즈
Q1: Gateway API의 3계층 리소스 모델에서 각 계층의 이름과 담당자는?
GatewayClass (인프라 제공자/플랫폼 팀), Gateway (클러스터 운영자), HTTPRoute/TCPRoute 등 (애플리케이션 개발자)
Q2: Ingress에서 벤더 종속적인 설정을 위해 사용하던 방식은?
어노테이션(annotations). nginx, traefik 등 구현체마다 다른 비표준 어노테이션을 사용했으며, 이는 이식성을 떨어뜨리는 주요 원인이었습니다.
Q3: Gateway API CRD를 설치하는 kubectl 명령어는?
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
Q4: 기존 Ingress를 Gateway API 리소스로 자동 변환해주는 공식 도구는?
ingress2gateway. Kubernetes SIG에서 제공하며, ingress2gateway print 명령으로 변환 결과를 확인할 수 있습니다.
Q5: Gateway API에서 가중치 기반 카나리 배포를 구현하려면 어떤 필드를 사용하나?
backendRefs의 weight 필드. 예: v1에 weight: 90, v2에 weight: 10으로 설정하면 90:10 비율로 트래픽 분할.
Q6: HTTPRoute와 Gateway가 다른 네임스페이스에 있을 때 주의할 점 두 가지는?
- parentRefs에 Gateway의 namespace를 명시해야 함. 2) Gateway의 listeners에서 allowedRoutes.namespaces.from을 "All"로 설정해야 함.
Q7: 크로스 네임스페이스에서 백엔드 서비스를 참조할 때 필요한 리소스는?
ReferenceGrant. 대상 네임스페이스에 생성하여, 어떤 네임스페이스의 어떤 리소스가 자신의 Service를 참조할 수 있는지 명시합니다.
Q8: Ingress의 rewrite-target 어노테이션은 Gateway API에서 어떻게 대체되나?
HTTPRoute의 filters에서 URLRewriteFilter를 사용합니다. type: URLRewrite와 urlRewrite.path 설정으로 경로를 재작성합니다.
Kubernetes Gateway API Hands-On Migration — From Ingress to Gateway
- Introduction
- Ingress vs Gateway API: Why Migrate?
- Understanding Core Resources
- Hands-On Migration Steps
- Gradual Transition Strategy
- Considerations and Troubleshooting
- Conclusion
- Quiz

Introduction
The Kubernetes networking stack is evolving. The Ingress API, which has long been the standard, is now being replaced by the Gateway API. Gateway API reached GA (General Availability) in 2023, and since 2025, most Ingress Controllers have begun supporting Gateway API.
In this post, we walk through the practical process of migrating existing Ingress resources to Gateway API, step by step.
Ingress vs Gateway API: Why Migrate?
Limitations of Ingress
The Ingress API works well enough for simple HTTP routing, but it has the following limitations:
- No role separation: Infrastructure administrators' and application developers' concerns are mixed in a single resource
- Limited protocol support: Only HTTP/HTTPS; TCP/UDP/gRPC require non-standard annotations
- Vendor lock-in: Different annotations per implementation (nginx, traefik, etc.)
- No header-based routing: Weighted traffic splitting not possible
Advantages of Gateway API
┌─────────────────────────────────────┐
│ GatewayClass │ ← Infrastructure provider (Platform team)
├─────────────────────────────────────┤
│ Gateway │ ← Cluster operator
├─────────────────────────────────────┤
│ HTTPRoute / TCPRoute / GRPCRoute │ ← Application developer
└─────────────────────────────────────┘
- Role-based design: Separation of concerns through the GatewayClass → Gateway → Route three-tier model
- Rich routing: Matching based on headers, query parameters, and methods
- Multi-protocol: Native support for HTTP, gRPC, TCP, UDP, and TLS
- Portability: Minimized vendor lock-in through a standard API
Understanding Core Resources
GatewayClass
A top-level resource defined by the infrastructure provider. It specifies which controller manages the Gateway.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx
spec:
controllerName: gateway.nginx.org/nginx-gateway-controller
Gateway
Defines the actual listeners (ports, protocols). Managed by cluster operators.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: infra
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-cert
kind: Secret
allowedRoutes:
namespaces:
from: All
HTTPRoute
Application developers define routing rules here.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-route
namespace: app-ns
spec:
parentRefs:
- name: main-gateway
namespace: infra
hostnames:
- 'app.example.com'
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: frontend-service
port: 3000
Hands-On Migration Steps
Step 1: Install Gateway API CRDs
# Install Gateway API v1.2 CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
# Include experimental features (TCPRoute, TLSRoute, etc.)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml
# Verify installation
kubectl get crd | grep gateway
Step 2: Analyze Existing Ingress Resources
Before migrating, take stock of your existing Ingress resources:
# List all Ingress resources
kubectl get ingress -A -o wide
# Inspect a specific Ingress in detail (including annotations)
kubectl get ingress my-ingress -o yaml
Example of an existing Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 3000
Step 3: Use the ingress2gateway Tool
Leverage the official conversion tool provided by the Kubernetes SIG:
# Install ingress2gateway
go install github.com/kubernetes-sigs/ingress2gateway@latest
# Convert Ingress resources in the current cluster to Gateway API
ingress2gateway print --all-namespaces
# Convert a specific namespace only
ingress2gateway print --namespace production
# Output to a file
ingress2gateway print --namespace production > gateway-resources.yaml
Step 4: Validate and Apply Converted Resources
# Validate with dry-run
kubectl apply -f gateway-resources.yaml --dry-run=server
# Apply
kubectl apply -f gateway-resources.yaml
# Check status
kubectl get gateway -A
kubectl get httproute -A
kubectl describe gateway main-gateway -n infra
Step 5: Traffic Splitting (Canary Deployment)
Weighted traffic splitting is a powerful feature unique to Gateway API:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: main-gateway
hostnames:
- 'app.example.com'
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-v1
port: 8080
weight: 90
- name: app-v2
port: 8080
weight: 10
Step 6: Header-Based Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-route
spec:
parentRefs:
- name: main-gateway
rules:
- matches:
- headers:
- name: X-Version
value: beta
backendRefs:
- name: app-beta
port: 8080
- backendRefs:
- name: app-stable
port: 8080
Gradual Transition Strategy
Ingress and Gateway API can coexist in the same cluster:
- Phase 1: Install Gateway API CRDs + create GatewayClass/Gateway
- Phase 2: Use HTTPRoute for new services
- Phase 3: Convert existing Ingress resources to HTTPRoute one by one
- Phase 4: Remove all Ingress resources and clean up the Ingress Controller
# Monitor migration progress
echo "Ingress: $(kubectl get ingress -A --no-headers | wc -l)"
echo "HTTPRoute: $(kubectl get httproute -A --no-headers | wc -l)"
Considerations and Troubleshooting
Annotation Mapping
Ingress annotations are replaced by Policy resources or Filters in Gateway API:
| Ingress Annotation | Gateway API Equivalent |
|---|---|
rewrite-target | URLRewriteFilter |
ssl-redirect | HTTP-to-HTTPS redirect on Gateway listener |
rate-limit | BackendPolicy or implementation-specific Policy |
cors | HTTPRoute filter or Policy |
# URL Rewrite example
rules:
- matches:
- path:
type: PathPrefix
value: /old-api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /new-api
backendRefs:
- name: api-svc
port: 8080
Common Mistakes
- Missing parentRefs namespace: If the Gateway and HTTPRoute are in different namespaces, the namespace must be specified
- Missing allowedRoutes configuration: Setting
from: Allis required for the Gateway to accept Routes from other namespaces - Missing ReferenceGrant: A ReferenceGrant is required for cross-namespace backend references
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-route-from-app
namespace: backend-ns
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: app-ns
to:
- group: ''
kind: Service
Conclusion
Gateway API is not just a replacement for Ingress — it represents a paradigm shift in Kubernetes networking. With role-based design, rich routing capabilities, and multi-protocol support, it enables safer and more flexible traffic management. Since it can coexist with existing Ingress resources, we recommend starting a gradual transition now.
Quiz
Q1: What are the names and responsible parties of each tier in Gateway API's three-tier resource
model?
GatewayClass (Infrastructure provider / Platform team), Gateway (Cluster operator), HTTPRoute/TCPRoute etc. (Application developer)
Q2: What mechanism did Ingress use for vendor-specific configurations?
Annotations. Each implementation (nginx, traefik, etc.) used different non-standard annotations,
which was a major cause of reduced portability.
Q3: What is the kubectl command to install Gateway API CRDs?
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
Q4: What is the official tool that automatically converts existing Ingress to Gateway API
resources?
ingress2gateway. Provided by the Kubernetes SIG, you can check the conversion results with the
ingress2gateway print command.
Q5: Which field is used to implement weighted canary deployments in Gateway API?
The weight field in backendRefs. For example, setting weight: 90 for v1 and weight: 10 for v2
splits traffic in a 90:10 ratio.
Q6: What are two things to watch out for when HTTPRoute and Gateway are in different namespaces?
- The Gateway's namespace must be specified in parentRefs. 2) The Gateway's listeners must have allowedRoutes.namespaces.from set to "All".
Q7: What resource is needed when referencing a backend service across namespaces?
ReferenceGrant. Created in the target namespace, it specifies which resources from which namespaces are allowed to reference its Services.
Q8: How is the Ingress rewrite-target annotation replaced in Gateway API?
Using URLRewriteFilter in HTTPRoute filters. The path is rewritten using type: URLRewrite and
the urlRewrite.path configuration.