Ingress에서 Gateway API로 마이그레이션
기존 인그레스(Ingress) 리소스를 Gateway API로 전환하는 방법을 안내합니다.
Ingress와 Gateway API의 개념 비교는 Gateway API 배포 > Ingress와 Gateway API 비교를 참고해 주세요.
본 가이드는 커뮤니티 NGINX Ingress Controller(kubernetes/ingress-nginx)의 프로젝트 퇴역(retirement)(2026년 3월 유지보수 종료)에 따라, 안전한 인프라 운영을 위한 Gateway API 전환 방법을 안내합니다.
- 전환 권장 배경: 기존 환경이 즉시 중단되지는 않으나, 향후 보안 패치 및 버그 수정이 불가능해집니다. (단, Traefik, HAProxy, Kong 등 다른 컨트롤러는 개별 유지보수 중이므로 기존 환경 유지 가능)
- Gateway API 구현체 선택: 본 가이드는 NGINX Gateway Fabric을 예시로 설명하나, Envoy, Istio, Cilium 등 선호하는 구현체를 자유롭게 선택할 수 있습니다.
- 공식 참고 자료: Kubernetes 공식 성명 및 Gateway API 공식 구현체 목록
사전 검증 필수: 본 문서는 마이그레이션을 돕기 위한 참고용 가이드입니다. 전환 과정에서 발생할 수 있는 서비스 중단이나 트래픽 유실에 대해 Kubernetes Engine은 책임을 지지 않으므로, 반드시 스테이징 환경에서 충분한 검증을 거친 후 운영 환경에 적용해 주시기 바랍니다.
어노테이션 호환성: 기존 Ingress 리소스의 커스텀 어노테이션은 Gateway API와 1:1로 대응되지 않습니다. (예: rewrite-target, proxy-body-size, ssl-redirect 등). 어노테이션 전환 방법은 선택한 Gateway API 컨트롤러마다 상이하므로, 사전에 사용 중인 설정을 확인하고 해당 컨트롤러의 공식 문서를 참고해 개별 적용해야 합니다. 상세 내용은 Kubernetes Gateway API 공식 마이그레이션 가이드와 Ingress-NGINX 마이그레이션 가이드를 참고해 주세요.
Admission Webhook 설정: Kubernetes Engine 환경에서는 기본적으로 Admission Webhook 통신이 제한됩니다. 도입하려는 Gateway API 컨트롤러에 Admission Webhook을 사용하는 컴포넌트가 포함되어 있다면, 해당 파드에 반드시 hostNetwork: true 설정을 추가해야 합니다.
로드 밸런서 IP 설정: Kubernetes Engine에서 Gateway를 통해 생성되는 로드 밸런서는 기본적으로 프라이빗 IP를 할당받습니다. 외부 통신을 위한 퍼블릭 IP가 필요하다면 별도의 로드 밸런서 어노테이션 설정이 요구됩니다. 자세한 방법은 로드 밸런서 생성 및 삭제 > 부록. 로드 밸런서 상세 옵션 설정하기를 참고해 주세요.
Step 1. ingress2gateway를 활용한 자동 변환
Kubernetes SIG-Network에서 제공하는 ingress2gateway 도구를 사용하면, 기존 Ingress 리소스를 Gateway API 리소스(Gateway + HTTPRoute)로 자동 변환할 수 있습니다.
ingress2gateway 설치
go install github.com/kubernetes-sigs/ingress2gateway@latest
클러스터에서 직접 변환
현재 클러스터에 배포된 Ingress 리소스를 읽어 Gateway API 리소스로 변환합니다.
ingress2gateway print --kubeconfig=$KUBE_CONFIG --providers=ingress-nginx --all-namespaces
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: nginx
namespace: demo-apps
spec:
gatewayClassName: nginx
listeners:
- name: http
port: 80
protocol: HTTP
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: path-routing-all-hosts
namespace: demo-apps
spec:
parentRefs:
- name: nginx
rules:
- backendRefs:
- name: app-v1
port: 80
matches:
- path:
type: PathPrefix
value: /api
- backendRefs:
- name: app-v2
port: 80
matches:
- path:
type: PathPrefix
value: /web
파일 기반 변환
클러스터에 접속하지 않고, Ingress YAML 파일을 직접 변환할 수도 있습니다.
ingress2gateway print --providers=ingress-nginx --input-file=ingress-backup.yaml
ingress2gateway print --providers=ingress-nginx --input-file=ingress-backup.yaml > gateway-api-resources.yaml
ingress2gateway는 호스트, 경로, 백엔드, TLS 같은 핵심 Ingress 필드를 변환하며, ingress-nginx에서 자주 사용하는 일부 어노테이션도 함께 변환할 수 있습니다.
다만 모든 어노테이션과 구현체별 확장 설정이 1:1로 자동 변환되는 것은 아니므로, 자동 생성된 결과물의 gatewayClassName, namespace, hostnames, filters, backendRefs를 반드시 검토해 주시기 바랍니다.
Step 2. 필드 매핑 가이드
자동 변환 도구를 사용하지 않고 직접 변환하는 경우, 아래 Before/After 예제를 참고해 주세요. 아래 예제들은 Gateway 리소스가 이미 생성되어 있다고 가정합니다.
예제 1. 경로 기반 라우팅
Before — Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-routing
namespace: demo-apps
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 80
- path: /web
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
After — HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: path-routing
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: app-v1
port: 80
- matches:
- path:
type: PathPrefix
value: /web
backendRefs:
- name: app-v2
port: 80
주요 변경점:
ingressClassName→parentRefs로 Gateway를 직접 참조spec.rules[].http.paths→spec.rules[].matches[].pathbackend.service.name/port.number→backendRefs[].name/port
예제 2. 호스트 기반 라우팅
Before — Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: host-routing
namespace: demo-apps
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
After — HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
hostnames:
- api.example.com
rules:
- backendRefs:
- name: api-svc
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
hostnames:
- web.example.com
rules:
- backendRefs:
- name: web-svc
port: 80
주요 변경점:
spec.rules[].host→ HTTPRoutespec.hostnames- 호스트별로 별도의 HTTPRoute를 생성하여 독립적으로 관리 가능 (하나의 HTTPRoute에 여러 hostnames를 정의하는 것도 가능)
예제 3. TLS 종료
Before — Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
namespace: demo-apps
spec:
ingressClassName: nginx
tls:
- hosts:
- secure.example.com
secretName: tls-secret
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-svc
port:
number: 80
After — Gateway + HTTPRoute
Gateway API에서는 TLS 설정이 Gateway 리소스의 리스너에 정의됩니다. HTTPRoute는 라우팅 규칙만 담당합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: nginx-gateway
namespace: nginx-gateway
spec:
gatewayClassName: nginx
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: secure.example.com
tls:
mode: Terminate
certificateRefs:
- name: tls-secret
allowedRoutes:
namespaces:
from: All
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: secure-route
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
sectionName: https
hostnames:
- secure.example.com
rules:
- backendRefs:
- name: secure-svc
port: 80
주요 변경점:
- Ingress
spec.tls→ Gatewaylisteners[].tls에서 TLS 종료 설정 - TLS 인증서(Secret)는 Gateway가 관리하며, HTTPRoute는 라우팅만 정의
parentRefs.sectionName으로 특정 리스너를 참조
Step 3. 사전 작업
마이그레이션을 시작하기 전에 다음 사전 작업을 완료합니다.
1. 기존 Ingress 설정 백업
kubectl --kubeconfig=$KUBE_CONFIG get ingress -A -o yaml > ingress-backup.yaml
2. Gateway API CRD 설치
설치 방법은 Gateway API 배포 > Step 1. 사전 작업을 참고해 주세요.
kubectl --kubeconfig=$KUBE_CONFIG get crd | grep gateway
3. Gateway API 컨트롤러 배포
NGINX Gateway Fabric 기준 배포 방법은 Gateway API 배포 > Step 2. Gateway API 컨트롤러 배포를 참고해 주세요. 다른 구현체를 사용하는 경우, 해당 컨트롤러의 공식 문서를 참고하여 배포합니다.
kubectl --kubeconfig=$KUBE_CONFIG get pods -n nginx-gateway
4. Gateway 리소스 생성
생성 방법은 Gateway API 배포 > Step 4. Gateway 리소스 생성을 참고해 주세요.
kubectl --kubeconfig=$KUBE_CONFIG get gateway -n nginx-gateway
NAME CLASS ADDRESS PROGRAMMED AGE
nginx-gateway nginx k8s-nginxga-nginxga-xxxxxxxxxx-xxxxxx.ke.kr-central-2.kakaocloud.com True 3m
Step 4. 마이그레이션 수행
기존 Ingress 리소스를 Gateway API로 전환합니다. 병렬 운영 → 검증 → 전환 순서로 안전하게 진행합니다.
1. HTTPRoute 리소스 생성
Step 1의 자동 변환 결과 또는 Step 2의 예제를 참고하여, 기존 Ingress 리소스에 대응하는 HTTPRoute를 생성합니다.
kubectl --kubeconfig=$KUBE_CONFIG apply -f httproute-path.yaml
kubectl --kubeconfig=$KUBE_CONFIG get httproute -n demo-apps
2. 병렬 운영
기존 Ingress와 새 HTTPRoute를 동시에 운영하여, 트래픽이 양쪽 모두에서 정상적으로 처리되는지 확인합니다.
Ingress Controller와 Gateway API Controller는 각각 별도의 로드 밸런서를 생성하므로 서로 간섭 없이 동시에 운영할 수 있습니다. 단, 기존 Ingress에서 사용하던 로드 밸런서의 IP 주소를 Gateway API 측에서 그대로 사용할 수는 없습니다. 새로운 로드 밸런서 주소가 할당되므로, DNS 레코드를 새 주소로 변경하여 트래픽을 전환해 주시기 바랍니다.
kubectl --kubeconfig=$KUBE_CONFIG get ingress -n demo-apps
kubectl --kubeconfig=$KUBE_CONFIG get gateway -n nginx-gateway -o jsonpath='{.items[0].status.addresses[0].value}'
3. 트래픽 전환 및 기존 Ingress 삭제
Gateway API 측에서 트래픽이 정상적으로 처리되는 것을 확인한 후, DNS를 Gateway의 로드 밸런서 주소로 전환하고 기존 Ingress 리소스를 삭제합니다.
kubectl --kubeconfig=$KUBE_CONFIG delete ingress path-routing -n demo-apps
Ingress 리소스를 삭제하면 해당 Ingress Controller의 로드 밸런서를 통한 트래픽이 중단됩니다. 반드시 Gateway API를 통한 트래픽이 정상적으로 동작하는 것을 확인한 후에 삭제하시기 바랍니다.
4. 롤백 방안
마이그레이션 후 문제가 발생하는 경우, Step 3에서 백업해 둔 Ingress 리소스를 복원하여 원래 상태로 되돌릴 수 있습니다.
kubectl --kubeconfig=$KUBE_CONFIG apply -f ingress-backup.yaml
kubectl --kubeconfig=$KUBE_CONFIG delete httproute path-routing -n demo-apps
DNS를 원래 Ingress 로드 밸런서 주소로 되돌려 주시기 바랍니다.
롤백 가능성을 고려하여, 기존 Ingress Controller는 Gateway API를 통한 트래픽이 충분히 안정화될 때까지 삭제하지 않고 유지하는 것을 권장합니다.
Step 5. 마이그레이션 확인
마이그레이션이 완료된 후, 다음 항목을 확인하여 정상 동작을 검증합니다.
1. Gateway 및 HTTPRoute 상태 확인
Gateway에 ADDRESS가 할당되어 있고 PROGRAMMED: True인지, HTTPRoute가 Accepted: True인지 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG get gateway -n nginx-gateway
kubectl --kubeconfig=$KUBE_CONFIG describe httproute path-routing -n demo-apps
Status:
Parents:
Conditions:
Message: The route is accepted
Reason: Accepted
Status: True
Type: Accepted
Message: All references are resolved
Reason: ResolvedRefs
Status: True
Type: ResolvedRefs
2. 라우팅 동작 검증
Gateway의 ADDRESS를 통해 라우팅이 정상적으로 동작하는지 확인합니다.
export GW_ADDRESS=$(kubectl --kubeconfig=$KUBE_CONFIG get gateway nginx-gateway -n nginx-gateway -o jsonpath='{.status.addresses[0].value}')
echo $GW_ADDRESS
curl -v http://$GW_ADDRESS/api
curl -v http://$GW_ADDRESS/web
curl -v http://$GW_ADDRESS/ -H "Host: api.example.com"
curl -v http://$GW_ADDRESS/ -H "Host: web.example.com"
curl -v https://$GW_ADDRESS/ -H "Host: secure.example.com" --insecure
각 요청이 올바른 백엔드 서비스로 라우팅되면 마이그레이션이 성공적으로 완료된 것입니다.
마이그레이션 완료 후, 더 이상 사용하지 않는 Ingress Controller 리소스(Deployment, Service, IngressClass 등)를 정리하여 불필요한 리소스 사용을 방지할 수 있습니다.
트러블슈팅
마이그레이션 과정에서 자주 발생하는 문제와 해결 방법입니다.
HTTPRoute의 Accepted 상태가 False인 경우
kubectl --kubeconfig=$KUBE_CONFIG describe httproute <route-name> -n <namespace>
| Reason | 원인 | 해결 방법 |
|---|---|---|
InvalidListener | Gateway의 리스너가 유효하지 않음 (예: TLS Secret 누락) | Gateway의 리스너 Conditions를 확인하고, TLS Secret이 올바르게 생성되어 있는지 확인합니다. |
NotAllowedByListeners | HTTPRoute의 호스트명이 Gateway 리스너의 hostname과 불일치 | HTTPRoute의 hostnames와 Gateway 리스너의 hostname을 비교하여 일치시킵니다. |
NoMatchingParent | parentRefs에 지정한 Gateway를 찾을 수 없음 | Gateway의 이름과 네임스페이스가 올바른지 확인합니다. |
RefNotPermitted | 다른 네임스페이스의 Gateway를 참조할 권한이 없음 | Gateway의 allowedRoutes.namespaces.from이 All로 설정되어 있거나, ReferenceGrant가 생성되어 있는지 확인합니다. |
Gateway의 PROGRAMMED 상태가 False인 경우
kubectl --kubeconfig=$KUBE_CONFIG describe gateway <gateway-name> -n <namespace>
| Reason | 원인 | 해결 방법 |
|---|---|---|
AddressNotAssigned | 로드 밸런서 프로비저닝 실패 | kubectl get svc -n <gateway-namespace>로 EXTERNAL-IP 상태를 확인합니다. <pending> 상태가 지속되면 로드 밸런서 프로비저닝에 문제가 있을 수 있습니다. |
InvalidCertificateRef | TLS Secret이 존재하지 않거나 형식이 올바르지 않음 | kubectl get secret <name> -n <namespace>로 존재 여부를 확인하고, kubernetes.io/tls 타입인지 확인합니다. |
InvalidGatewayClass | GatewayClass가 존재하지 않거나 Accepted 상태가 아님 | kubectl get gatewayclass로 상태를 확인합니다. |
백엔드 서비스로 트래픽이 전달되지 않는 경우
-
HTTPRoute의
backendRefs에 지정한 서비스 이름과 포트가 올바른지 확인합니다.백엔드 서비스 확인kubectl --kubeconfig=$KUBE_CONFIG get svc -n <namespace> -
HTTPRoute와 백엔드 서비스가 동일한 네임스페이스에 있는지 확인합니다. 다른 네임스페이스의 서비스를 참조하려면 ReferenceGrant가 필요합니다.
-
HTTPRoute의 Conditions에서
ResolvedRefs상태를 확인합니다.ResolvedRefs 상태 확인kubectl --kubeconfig=$KUBE_CONFIG get httproute <route-name> -n <namespace> -o jsonpath='{.status.parents[0].conditions[?(@.type=="ResolvedRefs")]}'
ingress2gateway 변환 결과가 예상과 다른 경우
--providers플래그에 올바른 프로바이더를 지정했는지 확인합니다 (예:ingress-nginx,istio,kong등).- 자동 생성된 Gateway 리소스의
gatewayClassName과namespace가 실제 환경과 일치하는지 확인하고, 필요에 따라 수정합니다.