본문 바로가기
삵
Cloud Computing & MSA

Ingress를 Gateway API로 무중단 전환하기

1103동103호·2026년 11월 1일·조회 0

ingress-nginx가 읽기 전용으로 넘어간 뒤로 "그래서 뭘 깔아야 하냐"는 질문은 한 번 받고 끝났다. 요즘 받는 질문은 다르다. 이미 Ingress 리소스 수십 개가 돌아가는 클러스터를 멈추지 않고 어떻게 옮기느냐다. 손으로 HTTPRoute를 다시 쓰는 건 현실적이지 않아서, 변환 도구를 먼저 돌려 보고 남는 부분만 사람이 메우는 방식으로 접근한다.

결론부터 말하자면, ingress2gateway 1.0으로 Ingress 리소스를 Gateway API의 HTTPRoute로 기계 변환한 다음, 자동으로 넘어오지 않는 어노테이션(경로 rewrite 일부, 외부 인증, rate limit)을 선택한 Gateway 컨트롤러의 정책 리소스로 직접 옮긴다. 변환과 적용을 분리하고 두 경로를 잠시 병행 운영하면 트래픽을 끊지 않고 넘길 수 있다.

ingress-nginx가 멈춘 자리

Ingress는 쿠버네티스에서 HTTP 라우팅 규칙을 담는 표준 오브젝트다. 호스트와 경로를 백엔드 서비스로 연결하지만, 정의가 단순해서 rewrite나 인증 같은 기능은 전부 컨트롤러별 어노테이션에 의존했다. ingress-nginx는 그 어노테이션을 가장 많이 구현한 컨트롤러였다.

그 ingress-nginx 서브프로젝트가 유지보수 종료로 저장소가 읽기 전용이 됐다. 더는 보안 패치가 나오지 않으므로 운영 클러스터라면 대체 컨트롤러로 넘어가야 한다. 쿠버네티스 커뮤니티가 미는 후계는 Gateway API다.

Gateway API는 역할이 세 층으로 나뉜다. GatewayClass는 어떤 구현체(Envoy Gateway, NGINX Gateway Fabric, Istio 등)를 쓸지 고르는 설정이고, Gateway는 실제 리스너와 포트, TLS를 여는 오브젝트이며, HTTPRoute가 호스트와 경로를 백엔드로 잇는 라우팅 규칙이다. 예전 Ingress 하나가 담던 일이 이렇게 쪼개졌다.

ingress2gateway로 변환부터 돌린다

ingress2gateway는 쿠버네티스 SIG-Network가 관리하는 변환 도구다. 1.0 릴리스가 Gateway API v1.5.0을 대상으로 나왔고, 클러스터나 YAML 파일에서 Ingress를 읽어 Gateway와 HTTPRoute로 바꿔 준다. 설치는 Homebrew나 Go로 한다.

$ go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0
$ ingress2gateway version
version: v1.0.0

변환은 print 서브커맨드로 한다. --providers에 변환 규칙을 고르는데, ingress-nginx 어노테이션을 해석하려면 ingress-nginx를 지정한다. 지원 provider는 apisix, cilium, ingress-nginx, istio, gce, kong, nginx, openapi, traefik 아홉 가지다.

$ ingress2gateway print --providers=ingress-nginx -n shop -o yaml

클러스터에 직접 붙지 않고 보관해 둔 매니페스트로 먼저 돌려 보는 편이 안전하다. --input-file로 YAML을 넘기면 kubeconfig 없이 변환 결과만 확인할 수 있다.

$ kubectl get ingress -n shop -o yaml > shop-ingress.yaml
$ ingress2gateway print --providers=ingress-nginx --input-file=shop-ingress.yaml

대표적인 출력은 Gateway 하나와 HTTPRoute 여러 개다. 경로 매칭, 호스트, 백엔드 서비스처럼 Ingress 스펙에 직접 들어 있던 값은 깔끔하게 넘어온다. 1.0부터 rewrite-target, use-regex, ssl-redirect, backend-protocol, CORS, 바디 크기 제한, 타임아웃 등 30개가 넘는 흔한 어노테이션도 함께 해석한다. 0.x 시절 세 개만 처리하던 것과는 범위가 다르다.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: shop
spec:
  parentRefs:
  - name: shop-gateway
  hostnames:
  - shop.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - name: shop-api
      port: 8080

여기서 한 가지 주의할 점이 있다. 도구가 해석하지 못한 어노테이션은 조용히 사라지는 게 아니라 출력에 경고나 주석으로 남는다. 그 목록이 곧 손으로 옮겨야 할 작업 목록이다. 변환 결과를 그대로 적용하기 전에 이 경고부터 읽어야 한다.

자동으로 안 넘어오는 것들

Gateway API 코어의 HTTPRoute 필터는 요청/응답 헤더 수정(RequestHeaderModifier, ResponseHeaderModifier), 리다이렉트(RequestRedirect), 경로 재작성(URLRewrite), 미러링(RequestMirror), 그리고 확장 참조(ExtensionRef)로 끝난다. 인증과 rate limit은 코어 필터에 없다. 그래서 이쪽 어노테이션은 변환 도구가 HTTPRoute 안으로 옮겨 줄 자리가 애초에 없다.

경로 rewrite는 절반만 자동이다. nginx.ingress.kubernetes.io/rewrite-target: /처럼 단순한 prefix 치환은 위 예시처럼 URLRewrite 필터로 잘 넘어온다. 하지만 정규식 캡처 그룹을 쓴 rewrite는 다르다.

nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2

이런 $2 캡처 치환은 URLRewrite의 ReplacePrefixMatch로 표현되지 않는다. 경로 설계를 바꿔 prefix 매칭으로 풀거나, 선택한 컨트롤러가 제공하는 확장 정책으로 옮겨야 한다. 막상 돌려 보면 rewrite에서 제일 자주 걸리는 지점이 이 정규식 케이스다.

외부 인증 어노테이션은 코어에 대응물이 없다.

nginx.ingress.kubernetes.io/auth-url: https://auth.example.com/verify
nginx.ingress.kubernetes.io/auth-signin: https://auth.example.com/start

이 동작은 컨트롤러별 정책 리소스로 재구현한다. Envoy Gateway라면 SecurityPolicy의 외부 인증(extAuth)을, Istio라면 AuthorizationPolicy와 RequestAuthentication을 쓴다. HTTPRoute에 그 정책을 targetRef로 붙이는 구조다. 기본 인증(auth-type basic)도 같은 방식으로 각 구현체 정책에 맡긴다.

rate limit도 마찬가지다. limit-rps, limit-connections 같은 어노테이션은 코어 밖이다. Envoy Gateway의 BackendTrafficPolicy에 rateLimit 규칙을 정의하는 식으로 컨트롤러 정책에 다시 쓴다. 즉 인증과 rate limit은 "변환"이 아니라 "재작성"이고, 어떤 Gateway 구현체를 고르느냐에 따라 문법이 전부 달라진다. 컨트롤러 선택을 먼저 확정해야 이 작업을 시작할 수 있다.

설정 스니펫 계열(configuration-snippet, server-snippet)은 변환 도구가 경고만 찍고 넘어간다. 임의의 nginx 지시문을 그대로 넣던 기능이라 정해진 대응이 없다. 스니펫 내용을 하나씩 뜯어 어느 Gateway 기능으로 대체할지 판단해야 하는데, 이 부분이 전환에서 손이 가장 많이 간다.

끊김 없이 바꿔 끼우기

Gateway API의 장점 하나는 기존 ingress-nginx와 다른 컨트롤러가 같은 클러스터에서 동시에 뜰 수 있다는 점이다. 이걸 이용해 병행 운영으로 넘긴다.

먼저 새 Gateway 컨트롤러를 설치하고 별도 로드밸런서나 노드포트로 외부 진입점을 하나 더 띄운다. 변환해 둔 HTTPRoute와 손으로 옮긴 정책을 이 새 경로에 적용한다. 이 시점에 운영 트래픽은 여전히 ingress-nginx로만 들어오므로 영향이 없다.

새 진입점의 IP로 직접 요청을 보내 Host 헤더로 라우팅을 검증한다.

$ curl -H "Host: shop.example.com" http://<new-gateway-ip>/api/health
{"status":"ok"}

인증 거부(401), rate limit 응답(429), rewrite 후 경로까지 응답 코드로 하나씩 확인한다. 손으로 옮긴 정책이 실제로 걸리는지는 이 단계에서만 제대로 드러난다.

검증이 끝나면 DNS를 새 진입점으로 돌려 트래픽을 넘긴다. 한 번에 바꾸는 게 부담되면 가중치 기반 DNS로 일부만 먼저 흘려보내고 지표를 본 뒤 비중을 올린다. 양쪽 로그와 에러율이 안정되면 ingress-nginx로 가던 DNS 레코드를 걷고 구 컨트롤러를 내린다. TTL이 긴 레코드가 있으면 전환 며칠 전에 TTL부터 줄여 두는 게 좋다. 안 그러면 캐시된 옛 IP로 트래픽이 한참 더 들어온다.

변환 도구가 끝이 아니라 시작이라는 점이 핵심이다. 기계가 넘겨준 HTTPRoute는 라우팅 골격일 뿐이고, 인증과 rate limit, 정규식 rewrite를 어느 컨트롤러 정책으로 다시 쓸지가 실제 작업량의 대부분을 차지한다. 그러니 변환을 돌리기 전에 Gateway 구현체부터 정하고, 그 구현체의 정책 리소스 문법을 먼저 익혀 두면 뒤가 수월하다.

자주 묻는 질문

ingress2gateway가 모든 ingress-nginx 어노테이션을 변환하나.

아니다. 1.0 기준으로 rewrite-target, use-regex, ssl-redirect, CORS, 타임아웃 등 30개가 넘는 흔한 어노테이션은 해석하지만, 외부 인증과 rate limit, 설정 스니펫처럼 Gateway API 코어에 대응물이 없는 항목은 변환하지 않는다. 이런 항목은 출력에 경고나 주석으로 남겨 수동 작업 대상임을 알려 준다.

인증과 rate limit은 어디로 옮기나.

HTTPRoute 코어 필터에는 없으므로 선택한 Gateway 컨트롤러의 정책 리소스로 재작성한다. Envoy Gateway는 외부 인증에 SecurityPolicy, rate limit에 BackendTrafficPolicy를 쓰고, Istio는 AuthorizationPolicy와 RequestAuthentication을 쓴다. 문법이 구현체마다 다르므로 컨트롤러를 먼저 확정해야 한다.

정규식 rewrite-target은 자동으로 넘어오나.

단순 prefix 치환은 URLRewrite 필터의 ReplacePrefixMatch로 넘어오지만, use-regex와 캡처 그룹(예: /$2)을 쓴 재작성은 코어 필터로 표현되지 않는다. 경로 설계를 prefix 매칭으로 바꾸거나 컨트롤러 확장 정책으로 옮겨야 한다.

클러스터를 멈추지 않고 전환할 수 있나.

할 수 있다. 새 Gateway 컨트롤러를 ingress-nginx와 동시에 띄워 별도 진입점으로 검증한 뒤, DNS를 새 진입점으로 돌려 트래픽을 넘긴다. 가중치 DNS로 일부만 먼저 보내고 지표를 확인하면 위험을 더 줄일 수 있다. 전환 전에 DNS TTL을 미리 낮춰 두는 것이 좋다.

변환을 클러스터에 붙지 않고 미리 해 볼 수 있나.

있다. kubectl로 Ingress를 YAML로 덤프한 뒤 ingress2gateway print에 --input-file로 넘기면 kubeconfig 없이 변환 결과와 경고만 확인할 수 있다. 실제 적용 전에 어떤 어노테이션이 누락되는지 점검하는 용도로 쓴다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.