IT/Kubernets

ingress-nginx EOL 이후, ingress2gateway로 Gateway API 옮기기

gfrog 2026. 5. 1. 09:13
반응형

3월 20일자로 ingress-nginx가 공식 EOL이 됐다. 같은 날 ingress2gateway 1.0이 GA로 풀렸다. 두 이벤트가 같은 날 풀린 게 우연이 아니다 — 업스트림에서 "이제 진짜 옮길 때다"라고 못박은 거다.

EOL 전부터 우리 팀도 마이그레이션을 시도했는데, 0.x 시절 ingress2gateway는 ingress-nginx 어노테이션을 3개밖에 못 바꿨다. 그래서 일단 미뤄두고 있었는데 1.0이 나오면서 30개 이상 지원으로 확 늘었다. 마침 옮길 만한 시점이다 싶어서, 지난 2주간 스테이징 → 프로덕션 일부 클러스터까지 옮겨봤다. 그 과정 정리.

옮기기 전에 알아둘 것

Gateway API는 Ingress 리소스 하나에 다 욱여넣었던 라우팅 + 어노테이션 정글을 두 단계로 쪼갠다. Gateway는 트래픽이 어디로 어떻게 들어오는지(리스너, 인증서, 포트), HTTPRoute는 들어온 트래픽을 어떻게 라우팅할지를 다룬다. 역할이 분리되니까 클러스터 인프라 팀이 Gateway를 관리하고, 서비스 팀은 HTTPRoute만 만지면 된다. 멀티테넌트 환경에서 권한 분리가 깔끔해진다.

근데 옮기는 입장에선 이게 마냥 좋지만은 않다. 기존 Ingress 한 덩어리였던 게 여러 리소스로 흩어지니까 GitOps repo 구조도 손봐야 하고, RBAC도 재설계해야 한다. 이걸 안 하고 그냥 변환만 돌리면 카오스 된다.

1단계: 컨트롤러 선택

ingress-nginx에서 옮길 때 후보는 보통 4개다.

  • NGINX Gateway Fabric — 이름이 비슷해서 "갈아끼우기 쉽겠지" 싶지만 사실 내부 구현은 완전히 다르다. 어노테이션 호환은 거의 없다.
  • Envoy Gateway — Envoy 기반. 우리 팀 메시가 Istio라서 후보에서 빠짐.
  • Kgateway — 옛 Solo의 Gloo 계열. 기능은 강력한데 운영 도구가 살짝 무겁다.
  • Cloud Provider 구현 — AWS LBC의 Gateway API 지원, GKE Gateway 등. 매니지드 부담을 줄이려면 좋은 선택.

우리는 EKS라 처음엔 AWS Load Balancer Controller로 갈까 했는데, 결국 Envoy Gateway를 골랐다. 이유는 동일 클러스터에서 운영 중인 다른 메시(별 팀이 운영)와 데이터 플레인을 통일하고 싶었고, ALB 비용이 라우트 수가 많으면 만만찮게 올라가서다.

2단계: ingress2gateway로 변환

ingress2gateway 1.0은 컨테이너 이미지보다 그냥 바이너리로 받아서 쓰는 게 편하다.

go install sigs.k8s.io/ingress2gateway@v1.0.0

# 클러스터의 모든 Ingress를 Gateway API로 변환
ingress2gateway print \
  --providers=ingress-nginx \
  --output-format=yaml \
  --emitter=envoy-gateway \
  > converted.yaml

--emitter 옵션이 1.0에서 추가된 핵심 기능이다. vanilla Gateway API만 뱉지 않고, 컨트롤러별 확장을 반영한 매니페스트를 만들어준다. Envoy Gateway면 EnvoyProxy CRD까지 같이 나온다.

변환 결과는 무조건 사람이 한 번 읽어야 한다. 우리 케이스에서 못 옮긴 어노테이션이 두 개 있었다.

WARN: nginx.ingress.kubernetes.io/auth-url cannot be translated automatically
WARN: nginx.ingress.kubernetes.io/configuration-snippet not supported

auth-url은 Envoy Gateway의 SecurityPolicy로 다시 짜야 하고, configuration-snippet은 그냥 안티패턴이라 이번 기회에 정리했다. CVE-2025-1974 사건도 결국 이 어노테이션으로 임의 NGINX 설정을 주입할 수 있어서 터졌던 거니까.

3단계: 병렬 운영으로 검증

이게 진짜 중요하다. 한 번에 갈아끼우면 안 된다. Gateway 컨트롤러는 ingress-nginx와 다른 외부 IP(혹은 LB)를 받는다. 그래서 두 컨트롤러를 동시에 띄워놓고, DNS만 점진적으로 옮길 수 있다.

우리는 이렇게 했다:

  1. 새 클래스로 GatewayClass, Gateway 띄우고 별도 ALB/NLB 생성
  2. 일부 서비스에 대해 HTTPRoute 작성 후 적용
  3. 새 LB의 DNS 이름으로 직접 curl + 합성 모니터링 트래픽 흘려보기
  4. Route53에서 weighted record로 5% → 25% → 50% → 100% 점진 전환
  5. 일주일 안정적이면 ingress 리소스 삭제

5%에서 5xx 비율이 ingress-nginx 대비 미세하게(0.02%p) 높았는데, 알고 보니 timeout 기본값이 다른 거였다. ingress-nginx의 proxy-read-timeout 60s가 Gateway API에선 컨트롤러별 기본값이라 명시적으로 BackendTrafficPolicy로 잡아줘야 했다. 이런 미묘한 차이는 변환 도구가 못 잡아준다.

같이 챙기면 좋은 것들

GitOps 구조를 미리 정리하라. ArgoCD ApplicationSet을 쓴다면 Gateway 리소스는 인프라 팀 ApplicationSet으로, HTTPRoute는 서비스 팀 ApplicationSet으로 분리하는 게 맞다. 우리는 처음에 다 한 곳에 뒀다가 권한 모델이 꼬여서 한 번 다시 갈랐다.

대시보드도 새로 만들어야 한다. ingress-nginx의 nginx_ingress_controller_* 메트릭은 다 사라진다. Envoy Gateway는 Envoy의 stats를 그대로 노출하니까 Grafana 대시보드를 새로 짜거나 envoyproxy/gateway 리포의 샘플을 가져오면 된다.

결론은 아직 진행 중

프로덕션 일부 워크로드는 옮겼고, 결제 같은 핵심 트래픽은 아직이다. 한 달 더 운영해보고 결제까지 넘길 생각이다. 1.0 변환 도구가 생각보다 잘 만들어져 있어서 진입 장벽이 많이 내려간 건 분명한데, 운영 도구/관측성/RBAC을 같이 손보지 않으면 그냥 리소스 이름만 바뀐 셈이 된다는 점은 강조해두고 싶다.

혹시 configuration-snippet 의존이 심한 환경에서 옮긴 분 있으면 어떻게 푸셨는지 댓글 부탁드린다.

반응형