Skip to content
Published on

Kubernetes Gateway API 완벽 가이드 — Ingress 마이그레이션부터 트래픽 분할까지

Authors
  • Name
    Twitter
Kubernetes Gateway API

들어가며

Kubernetes에서 외부 트래픽을 클러스터 내부 서비스로 라우팅하는 방법은 오랫동안 Ingress 리소스가 사실상의 표준이었다. 그러나 Ingress는 설계 당시부터 단순한 HTTP 라우팅만을 고려했기 때문에, 프로덕션 환경에서 요구되는 고급 라우팅, 트래픽 분할, 멀티 프로토콜 지원 등의 기능은 각 컨트롤러별 **어노테이션(annotation)**에 의존할 수밖에 없었다. 이로 인해 Ingress NGINX에서 작성한 어노테이션이 Traefik이나 HAProxy에서는 동작하지 않는 이식성(portability) 문제가 만성적으로 발생해왔다.

Gateway API는 이러한 한계를 근본적으로 해결하기 위해 Kubernetes SIG-Network이 설계한 차세대 서비스 네트워킹 API이다. 2023년 10월 v1.0 GA를 시작으로, 2024년 11월 v1.2에서 WebSocket, 타임아웃, 재시도 기능이 추가되었고, 2025년 11월 v1.4에서 BackendTLSPolicy와 ReferenceGrant가 Standard Channel로 승격되며 프로덕션 레벨의 완성도를 갖추게 되었다. 특히 Kubernetes 커뮤니티는 Ingress NGINX의 공식 지원 종료(End of Life)를 2026년 3월로 선언했으며, 이후에는 보안 패치나 버그 수정이 제공되지 않는다. 이제 Gateway API로의 마이그레이션은 선택이 아닌 필수가 되었다.

이 글에서는 Gateway API의 아키텍처 원리부터 핵심 리소스 상세, Ingress에서의 마이그레이션 전략, TLS 설정, 트래픽 분할, 그리고 실무 운영에서의 트러블슈팅과 실패 복구까지 종합적으로 다룬다.

Gateway API vs Ingress: 아키텍처 비교

Ingress의 한계

Ingress 리소스는 단일 리소스에 로드 밸런서 설정과 라우팅 규칙이 혼재되어 있다. 클러스터 운영자와 애플리케이션 개발자의 관심사가 분리되지 않으며, 고급 기능은 표준화되지 않은 어노테이션으로만 구현할 수 있다.

Gateway API의 역할 기반 설계

Gateway API는 역할 기반(Role-Oriented) 설계를 채택하여 인프라 제공자, 클러스터 운영자, 애플리케이션 개발자의 관심사를 명확히 분리한다.

비교 항목IngressGateway API
리소스 구조단일 Ingress 리소스GatewayClass, Gateway, HTTPRoute 분리
역할 분리없음 (하나의 리소스에 혼재)인프라 제공자 / 클러스터 운영자 / 개발자 분리
프로토콜 지원HTTP/HTTPS만 지원HTTP, HTTPS, TCP, UDP, gRPC, TLS 지원
라우팅 기능호스트/경로 기반만 지원헤더, 쿼리 파라미터, 메서드 매칭 지원
트래픽 분할어노테이션 의존 (비표준)네이티브 가중치 기반 분할
TLS 설정기본 종료만 지원Terminate, Passthrough, BackendTLSPolicy
교차 네임스페이스불가ReferenceGrant로 안전하게 지원
이식성컨트롤러별 어노테이션 필요표준 API 스펙으로 이식 가능
멀티 테넌시취약Gateway별 네임스페이스 격리 지원
상태 관리제한적Accepted, Programmed, ResolvedRefs 조건

아키텍처 다이어그램

인프라 제공자 (Infrastructure Provider)
  └─ GatewayClass: 어떤 컨트롤러가 Gateway를 구현할지 정의
클러스터 운영자 (Cluster Operator)
  └─ Gateway: 리스너(포트, 프로토콜, TLS) 설정
애플리케이션 개발자 (Application Developer)
  └─ HTTPRoute / GRPCRoute / TCPRoute: 라우팅 규칙 정의
  └─ ServicePod: 실제 트래픽 처리

핵심 리소스 상세

1. GatewayClass

GatewayClass는 클러스터 스코프 리소스로, 인프라 제공자가 정의한다. 어떤 컨트롤러가 Gateway를 관리할지 지정하며, Kubernetes의 StorageClass와 유사한 역할을 한다.

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway-class
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  description: 'Envoy Gateway 기반 프로덕션 게이트웨이 클래스'

주요 컨트롤러별 controllerName 값:

컨트롤러controllerName
Envoy Gatewaygateway.envoyproxy.io/gatewayclass-controller
NGINX Gateway Fabricgateway.nginx.org/nginx-gateway-controller
Istioistio.io/gateway-controller
Ciliumio.cilium/gateway-controller
Traefiktraefik.io/gateway-controller
Kongkonghq.com/kic-gateway-controller

2. Gateway

Gateway는 네임스페이스 스코프 리소스로, 클러스터 운영자가 관리한다. 리스너(Listener)를 통해 트래픽이 진입하는 포트, 프로토콜, 호스트명, TLS 설정을 정의한다.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: gateway-infra
  annotations:
    cert-manager.io/cluster-issuer: 'letsencrypt-prod'
spec:
  gatewayClassName: envoy-gateway-class
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All
    - name: https
      protocol: HTTPS
      port: 443
      hostname: '*.example.com'
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: wildcard-tls-cert
            namespace: gateway-infra
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: 'enabled'

allowedRoutes 설정은 멀티 테넌시 환경에서 핵심적인 보안 경계를 형성한다. from: All은 모든 네임스페이스의 Route가 이 리스너에 바인딩 가능하고, from: Selector는 특정 레이블이 있는 네임스페이스만 허용한다.

3. HTTPRoute

HTTPRoute는 애플리케이션 개발자가 관리하는 라우팅 규칙 리소스이다. 경로 매칭, 헤더 필터링, 트래픽 분할, 리다이렉트, URL 재작성 등 풍부한 기능을 표준 API로 제공한다.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
  namespace: backend-app
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-infra
      sectionName: https
  hostnames:
    - 'api.example.com'
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v2/users
          headers:
            - name: X-API-Version
              value: '2'
      filters:
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
              - name: X-Forwarded-By
                value: 'gateway-api'
      backendRefs:
        - name: users-service-v2
          port: 8080
          weight: 100
    - matches:
        - path:
            type: PathPrefix
            value: /v1
      backendRefs:
        - name: api-service-v1
          port: 8080
          weight: 90
        - name: api-service-v2
          port: 8080
          weight: 10

4. ReferenceGrant (교차 네임스페이스 참조)

Gateway API에서 교차 네임스페이스 참조는 기본적으로 차단된다. ReferenceGrant를 통해 명시적으로 허용해야 한다. v1.4에서 v1으로 승격된 이 리소스는 보안 경계를 유지하면서 유연한 구성을 가능하게 한다.

apiVersion: gateway.networking.k8s.io/v1
kind: ReferenceGrant
metadata:
  name: allow-gateway-to-backend-secrets
  namespace: gateway-infra
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: Gateway
      namespace: gateway-infra
  to:
    - group: ''
      kind: Secret

Ingress에서 Gateway API로 마이그레이션

마이그레이션 전략 개요

마이그레이션은 반드시 단계적으로 진행해야 한다. Gateway API 컨트롤러는 기존 Ingress 컨트롤러와 동일 클러스터에서 병렬로 실행할 수 있으므로, 서비스별로 점진적으로 전환하면서 검증하는 것이 안전하다.

1단계: Gateway API CRD 및 컨트롤러 설치

# Gateway API 표준 CRD 설치 (v1.4.x)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml

# 실험적 기능 포함 설치 (TCPRoute, UDPRoute, BackendTLSPolicy 포함)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/experimental-install.yaml

# CRD 설치 확인
kubectl get crd | grep gateway.networking.k8s.io

# 출력 예시:
# gatewayclasses.gateway.networking.k8s.io
# gateways.gateway.networking.k8s.io
# httproutes.gateway.networking.k8s.io
# referencegrants.gateway.networking.k8s.io
# grpcroutes.gateway.networking.k8s.io

컨트롤러 설치는 선택한 구현체에 따라 다르다. Envoy Gateway 예시:

# Envoy Gateway 설치
helm install envoy-gateway oci://docker.io/envoyproxy/gateway-helm \
  --version v1.3.0 \
  -n envoy-gateway-system \
  --create-namespace

# 설치 확인
kubectl get pods -n envoy-gateway-system
kubectl get gatewayclass

2단계: ingress2gateway 도구를 활용한 자동 변환

ingress2gateway 도구는 기존 Ingress 리소스를 분석하여 Gateway API 리소스로 자동 변환한다. 다만, 어노테이션의 약 30~40%는 수동 변환이 필요하므로 반드시 출력 결과를 검토해야 한다.

# ingress2gateway 설치
go install github.com/kubernetes-sigs/ingress2gateway@latest

# 현재 클러스터의 Ingress 리소스를 변환
ingress2gateway print --providers ingress-nginx \
  --all-namespaces > gateway-resources.yaml

# 변환된 리소스 검토 (반드시 수동 확인 필수)
cat gateway-resources.yaml

# 변환된 리소스를 스테이징 환경에 먼저 적용
kubectl apply -f gateway-resources.yaml --dry-run=server
kubectl apply -f gateway-resources.yaml -n staging

3단계: 병렬 운영 및 트래픽 전환

기존 Ingress와 Gateway API를 동시에 운영하면서 서비스별로 점진적으로 전환한다.

# Gateway 상태 확인 - Programmed: True 확인 필수
kubectl get gateway production-gateway -n gateway-infra -o jsonpath='{.status.conditions}'

# HTTPRoute 상태 확인
kubectl get httproute -A

# DNS를 Gateway API 엔드포인트로 변경 전 테스트
GATEWAY_IP=$(kubectl get gateway production-gateway -n gateway-infra \
  -o jsonpath='{.status.addresses[0].value}')
curl -H "Host: api.example.com" https://$GATEWAY_IP/v1/health --resolve "api.example.com:443:$GATEWAY_IP"

# 정상 확인 후 DNS 변경 (CNAME 또는 A 레코드)
# 모든 서비스 전환 완료 후 기존 Ingress 리소스 삭제
kubectl delete ingress api-ingress -n backend-app

4단계: 기존 Ingress 정리

모든 서비스가 Gateway API로 전환된 후, 기존 Ingress 컨트롤러를 정리한다. 반드시 서비스별로 하나씩 삭제하면서 접근 가능 여부를 확인해야 한다.

# Ingress 리소스 하나씩 삭제하며 검증
kubectl delete ingress api-ingress -n backend-app
# 즉시 서비스 접근 테스트
curl -I https://api.example.com/v1/health

# 모든 Ingress 삭제 확인 후 컨트롤러 제거
kubectl get ingress -A  # 남은 Ingress 없는지 확인
helm uninstall ingress-nginx -n ingress-nginx
kubectl delete namespace ingress-nginx

TLS 설정 및 인증서 관리

Downstream TLS (클라이언트 - Gateway 구간)

Gateway 리스너에서 TLS 종료(Terminate)를 설정한다. cert-manager와 연동하면 인증서 발급과 갱신을 자동화할 수 있다.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tls-gateway
  namespace: gateway-infra
  annotations:
    cert-manager.io/cluster-issuer: 'letsencrypt-prod'
spec:
  gatewayClassName: envoy-gateway-class
  listeners:
    - name: https-wildcard
      protocol: HTTPS
      port: 443
      hostname: '*.example.com'
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: wildcard-example-tls
      allowedRoutes:
        namespaces:
          from: All
    - name: https-specific
      protocol: HTTPS
      port: 443
      hostname: 'admin.internal.com'
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: admin-tls-cert
      allowedRoutes:
        namespaces:
          from: Same

cert-manager는 Gateway 리소스의 어노테이션을 감지하여 자동으로 Certificate 리소스를 생성하고, 발급된 인증서를 certificateRefs에 지정된 Secret에 저장한다. 인증서 만료 30일 전에 자동 갱신이 이루어진다.

Upstream TLS (Gateway - 백엔드 구간)

v1.4에서 Standard Channel로 승격된 BackendTLSPolicy를 사용하면 Gateway에서 백엔드 Pod까지의 TLS 연결을 설정할 수 있다. 이를 통해 종단간 암호화(end-to-end encryption)를 구현한다.

apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
  name: backend-tls
  namespace: backend-app
spec:
  targetRefs:
    - group: ''
      kind: Service
      name: secure-backend-service
  validation:
    caCertificateRefs:
      - name: backend-ca-cert
        group: ''
        kind: ConfigMap
    hostname: secure-backend.backend-app.svc.cluster.local

BackendTLSPolicy와 대상 Service는 반드시 동일 네임스페이스에 있어야 한다. 교차 네임스페이스 BackendTLSPolicy는 신뢰 경계 문제로 인해 지원되지 않는다.

TLS Passthrough

Gateway가 TLS를 종료하지 않고 백엔드까지 그대로 전달하는 모드이다. 백엔드 애플리케이션이 자체적으로 TLS를 처리해야 할 때 사용한다.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: passthrough-gateway
  namespace: gateway-infra
spec:
  gatewayClassName: envoy-gateway-class
  listeners:
    - name: tls-passthrough
      protocol: TLS
      port: 443
      hostname: 'secure-app.example.com'
      tls:
        mode: Passthrough
      allowedRoutes:
        namespaces:
          from: All

트래픽 분할 (Traffic Splitting) 및 가중치 라우팅

카나리 배포 트래픽 분할

HTTPRoute의 backendRefs에 가중치(weight)를 설정하여 트래픽을 비율에 따라 분배할 수 있다. 가중치는 비율(proportion)이므로 합이 100일 필요는 없으며, 전체 가중치의 합 대비 각 백엔드의 비율로 계산된다.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
  namespace: backend-app
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-infra
  hostnames:
    - 'app.example.com'
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        # 안정 버전: 90% 트래픽
        - name: app-stable
          port: 8080
          weight: 90
        # 카나리 버전: 10% 트래픽
        - name: app-canary
          port: 8080
          weight: 10

점진적 트래픽 전환 스크립트

카나리 배포 시 트래픽 비율을 단계적으로 조절하는 자동화 스크립트 예시이다.

#!/bin/bash
# 카나리 트래픽 점진적 증가 스크립트

ROUTE_NAME="canary-route"
NAMESPACE="backend-app"
STAGES=(10 25 50 75 100)
WAIT_MINUTES=5

for canary_weight in "${STAGES[@]}"; do
  stable_weight=$((100 - canary_weight))

  echo "[$(date)] 카나리 트래픽 비율: ${canary_weight}%"

  kubectl patch httproute $ROUTE_NAME -n $NAMESPACE --type='json' \
    -p="[
      {\"op\": \"replace\", \"path\": \"/spec/rules/0/backendRefs/0/weight\", \"value\": $stable_weight},
      {\"op\": \"replace\", \"path\": \"/spec/rules/0/backendRefs/1/weight\", \"value\": $canary_weight}
    ]"

  echo "대기 ${WAIT_MINUTES}분... (에러율 모니터링)"
  sleep $((WAIT_MINUTES * 60))

  # 에러율 확인 (Prometheus 쿼리 예시)
  ERROR_RATE=$(kubectl exec -n monitoring prometheus-0 -- \
    promtool query instant \
    'rate(http_requests_total{service="app-canary",code=~"5.."}[5m]) / rate(http_requests_total{service="app-canary"}[5m]) * 100' \
    2>/dev/null | grep -oP '[0-9.]+' | head -1)

  if (( $(echo "$ERROR_RATE > 5" | bc -l 2>/dev/null) )); then
    echo "에러율 ${ERROR_RATE}% 초과! 롤백 실행"
    kubectl patch httproute $ROUTE_NAME -n $NAMESPACE --type='json' \
      -p='[
        {"op": "replace", "path": "/spec/rules/0/backendRefs/0/weight", "value": 100},
        {"op": "replace", "path": "/spec/rules/0/backendRefs/1/weight", "value": 0}
      ]'
    echo "롤백 완료. 카나리 배포 중단."
    exit 1
  fi
done

echo "카나리 배포 완료. 트래픽 100% 전환 성공."

헤더 기반 트래픽 분할

특정 헤더를 가진 요청만 새 버전으로 라우팅하여, QA 팀이나 내부 사용자가 먼저 검증할 수 있다.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-based-route
  namespace: backend-app
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-infra
  hostnames:
    - 'app.example.com'
  rules:
    # 규칙 1: X-Canary 헤더가 있으면 v2로 라우팅
    - matches:
        - headers:
            - name: X-Canary
              value: 'true'
      backendRefs:
        - name: app-v2
          port: 8080
    # 규칙 2: 기본 트래픽은 v1으로 라우팅
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: app-v1
          port: 8080

실무 운영 시 주의사항

Gateway 상태 모니터링

Gateway API 리소스의 상태 조건(Conditions)을 반드시 모니터링해야 한다. 핵심 조건은 세 가지이다.

조건의미확인 시점
Accepted리소스가 구문/의미적으로 유효하고 컨트롤러에 의해 수락됨리소스 생성 직후
Programmed설정이 데이터 플레인에 완전히 반영됨트래픽 전환 전 필수 확인
ResolvedRefs참조하는 모든 외부 객체(Secret, Service 등)가 유효함TLS 설정, 교차 네임스페이스 참조 시
# Gateway 상태 조건 확인
kubectl describe gateway production-gateway -n gateway-infra

# HTTPRoute 상태 확인
kubectl get httproute -A -o custom-columns=\
'NAME:.metadata.name,HOSTNAMES:.spec.hostnames[*],PARENT:.spec.parentRefs[0].name,ACCEPTED:.status.parents[0].conditions[?(@.type=="Accepted")].status'

allowedRoutes 설정 관리

프로덕션 환경에서는 from: All 대신 반드시 from: Selector 또는 from: Same을 사용하여 어떤 네임스페이스의 Route가 Gateway에 바인딩할 수 있는지 명시적으로 제한해야 한다. 이를 무시하면 악의적인 또는 잘못된 HTTPRoute가 프로덕션 Gateway에 바인딩되어 트래픽 하이재킹이 발생할 수 있다.

리스너 호스트명 충돌 방지

동일한 Gateway에 같은 호스트명을 가진 리스너를 중복 정의하면 예측할 수 없는 동작이 발생한다. 리스너 이름은 고유해야 하며, 와일드카드 호스트명(*.example.com)과 구체적 호스트명(api.example.com)이 공존할 때 우선순위 규칙을 이해해야 한다. 구체적 호스트명이 와일드카드보다 우선한다.

리소스 정리 순서

마이그레이션 완료 후 리소스를 정리할 때 순서가 중요하다. HTTPRoute를 먼저 삭제하면 트래픽이 즉시 끊긴다. 반드시 DNS 변경 확인 후 기존 Ingress 리소스부터 삭제해야 한다.

트러블슈팅

문제 1: Gateway가 Programmed: False 상태에서 멈춤

원인: GatewayClass 컨트롤러가 실행되지 않거나, TLS 인증서 참조가 유효하지 않은 경우.

# 컨트롤러 Pod 상태 확인
kubectl get pods -n envoy-gateway-system

# Gateway 이벤트 확인
kubectl describe gateway production-gateway -n gateway-infra | tail -20

# TLS Secret 존재 여부 확인
kubectl get secret wildcard-tls-cert -n gateway-infra

# GatewayClass 상태 확인
kubectl get gatewayclass envoy-gateway-class -o yaml

해결: 컨트롤러 Pod가 CrashLoopBackOff 상태라면 로그를 확인한다. TLS Secret이 없으면 cert-manager 로그를 확인하고 Certificate 리소스 상태를 점검한다.

문제 2: HTTPRoute가 Accepted되었지만 트래픽이 라우팅되지 않음

원인: backendRef에 지정된 Service가 존재하지 않거나, 포트 번호가 일치하지 않거나, Pod가 건강하지 않은 경우.

# HTTPRoute 상태의 ResolvedRefs 조건 확인
kubectl get httproute api-route -n backend-app -o yaml | grep -A5 "ResolvedRefs"

# 백엔드 Service 존재 여부 확인
kubectl get svc users-service-v2 -n backend-app

# Endpoints 확인 (건강한 Pod가 있는지)
kubectl get endpoints users-service-v2 -n backend-app

# Pod 상태 및 로그 확인
kubectl get pods -n backend-app -l app=users-service-v2
kubectl logs -n backend-app -l app=users-service-v2 --tail=50

문제 3: 교차 네임스페이스 참조 실패

원인: ReferenceGrant가 올바르게 설정되지 않은 경우.

# ReferenceGrant 목록 확인
kubectl get referencegrant -A

# 특정 네임스페이스의 ReferenceGrant 상세 확인
kubectl describe referencegrant -n gateway-infra

해결: from 필드의 namespace, group, kind가 참조하는 리소스와 정확히 일치하는지 확인한다. ReferenceGrant는 참조 대상(to)이 위치한 네임스페이스에 생성해야 한다.

문제 4: cert-manager 연동 시 인증서가 발급되지 않음

# Certificate 리소스 상태 확인
kubectl get certificate -n gateway-infra

# cert-manager 로그 확인
kubectl logs -n cert-manager deploy/cert-manager --tail=100

# Challenge 상태 확인 (ACME HTTP-01)
kubectl get challenge -A

# Order 상태 확인
kubectl get order -A

해결: ClusterIssuer가 올바르게 설정되었는지, ACME 서버에 접근 가능한지, HTTP-01 challenge를 위한 포트 80 리스너가 열려 있는지 확인한다.

실패 사례와 복구 절차

사례 1: 마이그레이션 중 DNS 전환 시 트래픽 유실

상황: DNS TTL이 높은 상태에서 Ingress를 삭제하여 일부 클라이언트의 트래픽이 유실되었다.

복구 절차:

  1. 삭제된 Ingress 리소스를 즉시 재생성하여 기존 경로를 복원한다.
  2. DNS TTL을 300초(5분) 이하로 낮춘 후 최소 이전 TTL의 2배 시간을 대기한다.
  3. Gateway의 Programmed 상태가 True인지 재확인한다.
  4. DNS를 Gateway API 엔드포인트로 변경한다.
  5. 최소 24시간 모니터링 후 이전 Ingress를 삭제한다.

예방: 마이그레이션 전 DNS TTL을 60~300초로 낮추고, 충분한 전파 시간을 확보한 후 전환한다.

사례 2: 가중치 설정 오류로 인한 전체 트래픽 다운

상황: 카나리 배포 시 모든 backendRefs의 weight를 0으로 설정하여 503 에러가 발생했다.

복구 절차:

# 즉시 안정 버전으로 100% 트래픽 복원
kubectl patch httproute canary-route -n backend-app --type='json' \
  -p='[
    {"op": "replace", "path": "/spec/rules/0/backendRefs/0/weight", "value": 100},
    {"op": "replace", "path": "/spec/rules/0/backendRefs/1/weight", "value": 0}
  ]'

# 적용 확인
kubectl get httproute canary-route -n backend-app -o yaml

예방: 가중치 변경 시 최소 하나의 백엔드는 0보다 큰 가중치를 유지하도록 자동화 스크립트에 검증 로직을 포함한다.

사례 3: ReferenceGrant 누락으로 인한 TLS 인증서 참조 실패

상황: Gateway와 TLS Secret이 다른 네임스페이스에 있는데 ReferenceGrant 없이 배포하여 Gateway가 Programmed 상태가 되지 않았다.

복구 절차:

  1. Gateway의 status.conditions에서 ResolvedRefs 조건을 확인한다.
  2. 누락된 ReferenceGrant를 Secret이 위치한 네임스페이스에 생성한다.
  3. Gateway가 Programmed: True로 전환되는지 확인한다.

사례 4: 컨트롤러 업그레이드 중 데이터 플레인 중단

상황: Gateway API 컨트롤러를 Helm으로 업그레이드하는 동안 데이터 플레인 Pod가 재시작되며 트래픽이 일시 중단되었다.

복구 절차:

  1. 업그레이드 전 반드시 Gateway 리소스의 YAML 백업을 수행한다.
  2. 컨트롤러의 RollingUpdate 전략을 확인한다.
  3. 중단 발생 시 Helm rollback을 실행한다.
# 업그레이드 전 백업
kubectl get gateway,httproute,referencegrant -A -o yaml > gateway-backup.yaml

# Helm 롤백
helm rollback envoy-gateway -n envoy-gateway-system

# 데이터 플레인 Pod 상태 확인
kubectl get pods -n envoy-gateway-system -w

예방: 컨트롤러 업그레이드 시 PodDisruptionBudget을 설정하고, 스테이징 환경에서 먼저 검증한다.

마이그레이션 체크리스트

마이그레이션 전, 중, 후에 확인해야 할 항목을 체크리스트로 정리한다.

사전 준비

  • Gateway API CRD가 클러스터에 설치되었는가 (kubectl get crd | grep gateway)
  • 선택한 Gateway API 컨트롤러가 정상 실행 중인가
  • GatewayClass가 Accepted 상태인가
  • cert-manager가 Gateway API 연동을 지원하는 버전(1.15+)인가
  • DNS TTL이 300초 이하로 낮춰져 있는가
  • 기존 Ingress 리소스 전체 목록이 문서화되었는가
  • ingress2gateway로 변환된 YAML을 수동 검토했는가

마이그레이션 진행

  • Gateway 리소스가 Programmed: True 상태인가
  • 모든 HTTPRoute가 Accepted: True 상태인가
  • 모든 HTTPRoute의 ResolvedRefs가 True인가
  • TLS 인증서가 정상 발급되었는가 (kubectl get certificate)
  • 교차 네임스페이스 참조에 ReferenceGrant가 설정되었는가
  • curl 또는 외부 모니터링으로 엔드포인트 접근이 확인되었는가
  • Gateway API 엔드포인트로의 DNS 전환이 완료되었는가

마이그레이션 완료 후

  • 기존 Ingress 리소스가 모두 삭제되었는가
  • 기존 Ingress 컨트롤러가 정리되었는가
  • 모니터링 대시보드가 Gateway API 메트릭을 수집하고 있는가
  • 알림(Alert) 규칙이 Gateway 상태 조건을 포함하는가
  • 장애 대응 런북이 Gateway API 기준으로 업데이트되었는가
  • Gateway 리소스 YAML 백업이 Git 등 형상관리에 저장되었는가

마치며

Gateway API는 Kubernetes 네트워킹의 미래이다. 역할 기반 설계로 인프라 운영자와 애플리케이션 개발자의 관심사를 깔끔하게 분리하고, 표준화된 API로 컨트롤러 간 이식성을 보장하며, 네이티브 트래픽 분할과 고급 라우팅 기능을 제공한다. v1.4에서 BackendTLSPolicy와 ReferenceGrant가 GA로 승격되면서 프로덕션 환경에서 필요한 거의 모든 기능이 Standard Channel에 포함되었다.

Ingress NGINX의 공식 지원 종료가 2026년 3월로 예정된 만큼, 아직 마이그레이션을 시작하지 않았다면 지금이 적기이다. 이 글에서 다룬 단계적 마이그레이션 전략, ingress2gateway 도구 활용, 병렬 운영 방식을 참고하여 안전하게 전환하기를 권한다. 무엇보다 중요한 것은 Gateway가 Programmed: True 상태인지 반드시 확인한 후에만 트래픽을 전환하는 것이다. 서두르지 않고 서비스별로 점진적으로 전환하면서 검증하는 것이 가장 안전한 방법이다.

참고 자료