
마이그레이션 전에 점검할 것
ingress-nginx가 결국 작년 3월에 EOL됐다. 깃허브 저장소는 archived 처리됐고, 보안 패치도 더 이상 안 나온다. 그동안 미루고 미뤘는데 이제는 정말 도망갈 데가 없다. 우리 팀도 6월 들어서야 본격적으로 Gateway API 마이그레이션을 시작했고, 이 글은 그 과정에서 정리한 가이드다.
처음에는 막막했다. Ingress 리소스가 200개가 넘는데 이걸 일일이 손으로 옮긴다고? 다행히 SIG-Network에서 올해 3월에 ingress2gateway 1.0을 정식 릴리스했다. 이걸 쓰면 대부분의 Ingress 리소스가 자동으로 변환된다. 물론 자동 변환이 모든 걸 해결해주지는 않는다는 게 함정이지만.
본격적으로 시작하기 전에 두 가지를 먼저 확인했다.
첫 번째는 현재 사용 중인 Ingress annotation 인벤토리. ingress-nginx는 100개 가까운 annotation을 지원하는데, 이게 다 Gateway API로 1:1 매핑되지는 않는다. 우리는 이걸 먼저 정리했다.
kubectl get ingress -A -o json | \
jq -r '.items[].metadata.annotations | keys[]' | \
sort | uniq -c | sort -rn
결과를 보고 깜짝 놀랐다. 우리 클러스터에 쓰이는 nginx annotation이 37가지였다. 이 중에 nginx.ingress.kubernetes.io/rewrite-target, nginx.ingress.kubernetes.io/cors-* 계열이 압도적으로 많았다. ingress2gateway 1.0이 30개 이상의 주요 annotation을 지원한다고 하니 일단 한숨은 돌렸다.
두 번째는 Gateway API 구현체 선택. Gateway API는 표준 스펙일 뿐이고, 실제로 트래픽을 처리하는 컨트롤러는 별도다. 우리 팀은 Envoy Gateway, Cilium Gateway, Istio Gateway 셋을 두고 고민했다. 결론은 Envoy Gateway. 이유는 단순하다. 우리는 이미 사이드카로 Envoy를 쓰고 있었고, Gateway API 스펙 완성도가 가장 높았다.
ingress2gateway로 변환하기
설치는 어렵지 않다.
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0
그다음 변환은 한 줄로 끝난다.
ingress2gateway print \
--providers ingress-nginx \
--namespace production > gateway-resources.yaml
여기서 한 가지 팁. --providers ingress-nginx를 명시해야 nginx 전용 annotation까지 변환된다. 그냥 ingress만 지정하면 표준 Ingress 스펙만 변환하고 annotation은 다 무시한다. 처음에 이거 모르고 한참 헤맸다.
변환 결과를 보면 Ingress 하나가 보통 두세 개 리소스로 쪼개진다. Gateway, HTTPRoute, 그리고 annotation에 따라 BackendTLSPolicy나 HTTPRouteFilter 같은 게 추가로 생긴다.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: production
spec:
parentRefs:
- name: prod-gateway
sectionName: https
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /v1
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-svc
port: 8080
처음 본 사람은 어색할 거다. Ingress는 한 리소스에 다 우겨넣는 구조였는데, Gateway API는 책임을 쪼개놨다. Gateway는 리스너(포트, TLS), HTTPRoute는 라우팅 규칙. 익숙해지면 이게 훨씬 깔끔하다.
변환되지 않는 것들
ingress2gateway가 모든 걸 해결해주면 좋겠지만, 현실은 그렇지 않다. 자동 변환이 안 되는 케이스를 우리가 마주친 순서대로 적어본다.
1. 커스텀 nginx-snippet annotation
nginx.ingress.kubernetes.io/server-snippet이나 configuration-snippet 같은 걸로 직접 nginx 설정을 박아넣은 경우. 이건 Gateway API로 1:1 변환이 불가능하다. 우리는 이런 케이스를 따로 모아서 하나씩 검토했다. 대부분은 헤더 조작이거나 redirect 처리였는데, 이건 HTTPRoute의 filters 섹션으로 옮길 수 있다.
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set:
- name: X-Forwarded-Proto
value: https
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: Strict-Transport-Security
value: "max-age=31536000; includeSubDomains"
2. rate limiting
nginx.ingress.kubernetes.io/limit-rps 같은 rate limit annotation. Gateway API 표준 스펙에는 아직 rate limit이 없다. 구현체별로 다르다. Envoy Gateway는 BackendTrafficPolicy라는 별도 CRD로 처리한다. 이건 ingress2gateway가 손도 못 댄다. 직접 옮겨야 한다.
3. mTLS
backend로 가는 mTLS 설정. nginx.ingress.kubernetes.io/proxy-ssl-secret 같은 거. 이건 Gateway API 1.2부터 BackendTLSPolicy로 표준화됐고 ingress2gateway 1.0이 일부 변환을 지원한다. 다만 인증서 시크릿 참조 방식이 조금 달라서 변환 후 반드시 수동 검증이 필요하다.
Coexistence — 점진적 마이그레이션
좋은 소식은, 두 가지를 같이 돌릴 수 있다는 거다. 우리는 새 Gateway를 다른 LB로 띄우고 DNS를 weighted routing으로 천천히 옮겼다.
Route53 weighted records
├── api.example.com → old-ingress-lb (90%)
└── api.example.com → new-gateway-lb (10%)
10% → 30% → 50% → 100% 이런 식으로 한 주에 걸쳐 점진 전환. 트래픽이 새 Gateway로 들어가는 동안 P99 레이턴시와 에러율을 계속 모니터링했다. 실제로 마이그레이션 첫날 P99가 살짝 튀었는데, Envoy Gateway 리스너의 keepalive_timeout 기본값이 nginx와 달라서 그랬다. 이런 미세 튜닝은 가이드에서 안 알려주는 것들이라 직접 부딪혀가며 찾아야 한다.
Gateway 리소스 운영 모델
Ingress 시절에는 개발팀이 자기 namespace에 Ingress를 만들면 끝이었다. Gateway API는 책임이 분리된다. 인프라 팀이 Gateway를 관리하고, 개발팀은 HTTPRoute만 자기 namespace에 만든다. ReferenceGrant로 namespace 간 참조를 명시적으로 허가하는 모델.
이게 처음에는 번거롭게 느껴지는데, 한 달 정도 쓰다 보니 오히려 깔끔하다. 어떤 팀이 어떤 도메인을 쓰는지 한눈에 보이고, 도메인/포트/TLS 같은 공유 자원은 인프라 팀이 통제할 수 있다.
마치며
ingress-nginx EOL은 강제 이벤트지만, 마이그레이션을 미루지 말고 차라리 잘된 일이라고 보는 게 맞는 것 같다. Gateway API는 처음에는 어색하지만 일단 익숙해지면 운영 모델이 훨씬 깨끗하다.
다음에 시간이 되면 Envoy Gateway에서 BackendTrafficPolicy로 rate limit, retry, circuit breaker 설정하는 패턴을 정리해보려고 한다. 혹시 다른 구현체(Cilium, Istio Ambient) 쓰시는 분 계시면 비교 의견 댓글로 남겨주시면 감사하겠다.
'IT > 기타' 카테고리의 다른 글
| Istio sidecar에서 ambient로 옮기다 일주일을 날렸다 (0) | 2026.05.30 |
|---|---|
| Linkerd 2.18을 다시 봤지만, 결국 옮기지 않은 이유 (0) | 2026.05.25 |
| Istio Sidecar에서 Ambient로 점진적 마이그레이션, 우리 팀은 이렇게 했다 (0) | 2026.05.17 |
| Kafka 3.9를 다리 삼아 KRaft로 넘어가는 법 (0) | 2026.05.14 |
| Istio Ambient vs Cilium Service Mesh, 우리는 뭘 쓰고 있나 (0) | 2026.05.06 |