Kubernetes Kubernetes Gateway API

Gateway API, Ingress를 대체하는 Kubernetes 표준

SIG-Network이 4년에 걸쳐 만든 Gateway API의 핵심 리소스 3가지와 그 관계를 이해하고, Ingress와 무엇이 달라졌는지 정리한다.

NGINX Ingress EOL 소식을 듣고 Gateway API로 옮겼다. 마이그레이션 자체는 어렵지 않았는데, 쓰다 보니 궁금해졌다. Ingress와 구조가 꽤 다르다. 리소스가 3개로 나뉘어 있고, 관계도 1:N, N:M으로 복잡하다. 왜 이렇게 설계했을까?

찾아보니 SIG-Network이 2019년부터 4년에 걸쳐 만든 프로젝트였다. Ingress의 어떤 한계가 이 설계를 이끌었는지, 히스토리부터 정리해본다.

Ingress, 뭐가 부족했나

어노테이션 복잡성

Ingress 자체 스펙은 단순하다. Host, Path, TLS 정도가 전부다. 그래서 rate limiting, 인증, 리다이렉트 같은 기능은 전부 어노테이션으로 해결해야 한다.

# NGINX Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com"

문제는 이 어노테이션이 구현체마다 다르다는 것이다. NGINX에서 Traefik으로 옮기면 어노테이션을 전부 다시 써야 한다. 표준이 아니라 각 벤더의 확장이기 때문이다.

Gateway API 공식 마이그레이션 가이드에서는 이렇게 표현한다:

“The annotations approach to extensibility leads to limited portability as every implementation has its own supported extensions that may not translate to any other implementation.”

같은 Kubernetes인데 구현체를 바꾸면 YAML을 다 고쳐야 하는 건 표준이 부족하다는 뜻이다. 어노테이션이 사실상 벤더 락인 도구가 된 셈이다.

실제로 ingress-nginx에서는 어노테이션 자체가 보안 취약점이 되기도 했다. configuration-snippet 어노테이션으로 임의 NGINX 설정을 주입할 수 있었고, 이게 CVE-2023-5043(어노테이션 인젝션을 통한 임의 명령 실행) 같은 취약점으로 이어졌다. NGINX Ingress 퇴역 공지에서도 “Yesterday’s flexibility has become today’s insurmountable technical debt”라고 언급했다.

역할 분리가 없다

Ingress는 하나의 리소스에 모든 설정이 들어간다. 포트, TLS, 라우팅 규칙이 전부 한 YAML에 있다.

같은 마이그레이션 가이드에서:

“The Ingress API is not well-suited for multi-team clusters with shared load-balancing infrastructure.”

실무에서는 인프라 팀이 LB와 TLS를 관리하고, 개발 팀이 라우팅 규칙을 정의하는 게 자연스럽다. 그런데 Ingress는 이 구분이 없다. 개발자가 Ingress를 수정하면서 TLS 설정을 건드리거나, 인프라 팀이 라우팅 규칙을 건드리면서 충돌이 생긴다.

Gateway API는 이를 해결하기 위해 역할 기반 설계(Roles and Personas)를 핵심 철학으로 삼았다.

HTTP만 된다

Ingress는 L7 HTTP/HTTPS만 지원한다. gRPC, TCP, UDP 같은 프로토콜은 Ingress 스펙에 없다.

이건 오래된 이슈다. 2016년에 이미 kubernetes/kubernetes#23291에서 “Ingress에 TCP 지원을 추가해야 한다”는 논의가 있었지만, 모든 클라우드 프로바이더에서 일관되게 구현하기 어렵다는 이유로 lifecycle/frozen 처리되었다. 결국 각 구현체가 별도 CRD나 어노테이션으로 해결했고, 이것 역시 구현체마다 방법이 다르다.


SIG-Network의 답: Gateway API

Ingress의 한계는 커뮤니티에서 오래 인식되고 있었다. 각 프로젝트가 독자적으로 해결책을 만들기 시작했는데, Istio는 VirtualService를, Contour는 IngressRoute를 개발했다. Google의 회고에서는 이를 “The community had started to develop alternative APIs”라고 표현한다. 표준의 부재가 생태계 파편화로 이어진 것이다.

2019년 KubeCon EU에서 Google의 Bowei Du와 Rohit Ramkumar가 “Ingress v2”를 제안했다. Ingress의 한계를 근본적으로 해결하는 새 API의 필요성을 발표한 것이 시작이었다. 같은 해 KubeCon San Diego에서 SIG-Network 멤버들이 모여 구체적인 설계를 논의했고, Google, Kong, Red Hat, VMware 등이 참여했다.

이 프로젝트는 처음에 “Service APIs”라 불렸다가 Gateway API로 이름이 바뀌었고, KEP 대신 자체 GEP(Gateway Enhancement Proposals) 체계로 운영된다.

시점이벤트
2019.05KubeCon EU, Bowei Du “Ingress v2” 제안
2019.11KubeCon San Diego, SIG-Network 설계 논의 시작
2020”Service APIs” → Gateway API로 개명
2022.07v0.5.0, Beta 승격 (Standard Channel)
2023.10v1.0 GA — GatewayClass, Gateway, HTTPRoute
2024.05v1.1, Service mesh + GRPCRoute Standard 승격
2025.04v1.3, Request mirroring, CORS, Gateway merging

4년의 개발 끝에 2023년 10월 v1.0 GA에 도달했다. 현재 주요 클라우드 벤더(Google, AWS, Azure)와 Envoy Gateway, Istio, Cilium, Kong, Traefik 등이 구현체로 참여하고 있으며, 4개월 주기로 릴리스되고 있다.


핵심 리소스 3가지

Gateway API는 Ingress가 하나의 리소스로 처리하던 것을 3개 리소스로 분리했다. 각각 다른 역할을 담당한다.

GatewayClass — “어떤 컨트롤러를 쓸 것인가”

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller

클러스터에 설치된 Gateway 구현체(Envoy Gateway, Istio 등)를 정의한다. Ingress의 IngressClass와 비슷한 역할이지만, parametersRef구현체별 세부 설정을 연결할 수 있다는 점이 다르다. 이 부분은 뒤에서 자세히 다룬다.

클러스터 스코프 리소스이며, 보통 인프라 팀이 관리한다.

Gateway — “어떤 포트와 TLS를 열 것인가”

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: infra
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
tls:
certificateRefs:
- name: wildcard-cert

리스너(포트, 프로토콜, TLS)를 정의한다. “이 Gateway로 들어오는 트래픽은 80번과 443번 포트를 사용한다”는 선언이다.

네임스페이스 스코프이며, 마찬가지로 인프라 팀이 관리한다. 개발자가 건드릴 이유가 없는 영역이다.

HTTPRoute — “어떤 경로를 어디로 보낼 것인가”

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
namespace: app-team
spec:
parentRefs:
- name: main-gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
value: /api
backendRefs:
- name: api-service
port: 8080

실제 라우팅 규칙을 정의한다. “app.example.com/api로 들어오는 요청은 api-service:8080으로 보내라”는 선언이다.

네임스페이스 스코프이며, 개발 팀이 관리한다. 인프라를 건드리지 않고 자기 서비스의 라우팅만 정의할 수 있다.

역할 분리의 핵심

Ingress에서는 이 모든 것이 하나의 YAML에 섞여 있었다. Gateway API는 이렇게 나눈다:

역할리소스담당
컨트롤러 선택GatewayClass인프라 팀
포트/TLS 관리Gateway인프라 팀
라우팅 규칙HTTPRoute개발 팀

개발자는 HTTPRoute만 건드리면 되고, 인프라 팀은 Gateway와 GatewayClass를 관리한다. 서로의 영역을 침범하지 않는다.


리소스 간 관계

3개 리소스가 어떻게 연결되는지, 카디널리티까지 포함해서 정리한다.

flowchart TD
GC[GatewayClass] -->|1:N| GW[Gateway]
GW -->|N:M| HR[HTTPRoute]
HR -->|N:M| SVC[Service]

GatewayClass → Gateway: 1:N

하나의 GatewayClass를 여러 Gateway가 참조할 수 있다. 예를 들어 같은 Envoy Gateway 컨트롤러를 사용하면서, 내부용 Gateway와 외부용 Gateway를 따로 만들 수 있다.

# 외부 트래픽용
kind: Gateway
metadata:
name: external
spec:
gatewayClassName: eg # 같은 GatewayClass
listeners:
- port: 443
protocol: HTTPS
# 내부 트래픽용
kind: Gateway
metadata:
name: internal
spec:
gatewayClassName: eg # 같은 GatewayClass
listeners:
- port: 8080
protocol: HTTP

Gateway ↔ HTTPRoute: N:M

이 관계가 Ingress와 가장 큰 차이다.

하나의 Gateway에 여러 HTTPRoute를 연결할 수 있다. 각 팀이 자기 HTTPRoute를 만들어서 공유 Gateway에 붙이는 구조다.

# 팀 A의 라우팅
kind: HTTPRoute
metadata:
name: team-a-route
namespace: team-a
spec:
parentRefs:
- name: main-gateway
namespace: infra
# 팀 B의 라우팅
kind: HTTPRoute
metadata:
name: team-b-route
namespace: team-b
spec:
parentRefs:
- name: main-gateway
namespace: infra

하나의 HTTPRoute가 여러 Gateway에 연결되는 것도 가능하다. parentRefs에 여러 Gateway를 넣으면 된다. 외부와 내부 모두에서 접근 가능한 서비스를 만들 때 유용하다.

kind: HTTPRoute
spec:
parentRefs:
- name: external-gateway
- name: internal-gateway

HTTPRoute → Service: N:M

하나의 HTTPRoute가 weight 기반으로 여러 Service에 트래픽을 분배할 수 있다. 카나리 배포나 A/B 테스트에 쓰인다.

rules:
- backendRefs:
- name: api-v1
port: 8080
weight: 90
- name: api-v2
port: 8080
weight: 10

반대로 여러 HTTPRoute가 같은 Service를 참조하는 것도 자연스럽다. 경로별로 다른 HTTPRoute를 쓰지만 같은 백엔드를 가리킬 수 있다.

이런 N:M 관계는 Ingress에서는 불가능했다. Ingress는 리소스 하나에 라우팅 규칙이 묶여 있어서, 여러 팀이 같은 도메인의 라우팅을 나눠 관리하기 어려웠다.


구현체 확장: 표준과 벤더 설정의 공존

Gateway API의 3가지 리소스는 표준이다. 어떤 구현체를 쓰든 동일하게 동작한다. 그런데 실무에서는 구현체별 세부 설정이 필요하다.

  • OCI에서 Load Balancer Shape을 Flexible 10Mbps로 만들어라
  • Envoy Proxy를 노드당 1개씩 배치해라
  • Istio에서 mTLS를 활성화해라

이런 설정은 표준 스펙에 넣을 수 없다. OCI만의 요구사항이고, Istio만의 기능이기 때문이다.

parametersRef: 표준과 확장의 연결 고리

GatewayClass에는 parametersRef라는 필드가 있다. 구현체가 제공하는 커스텀 리소스를 참조할 수 있다.

flowchart LR
EP[EnvoyProxy<br>구현체 확장] -->|parametersRef| GC[GatewayClass<br>표준]
GC --> GW[Gateway<br>표준]
GW --> HR[HTTPRoute<br>표준]

Envoy Gateway는 EnvoyProxy라는 커스텀 리소스를 통해 확장 설정을 제공한다.

# EnvoyProxy — Envoy Gateway 전용 확장 리소스
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: custom-config
spec:
provider:
type: Kubernetes
kubernetes:
envoyDeployment:
replicas: 2
envoyService:
annotations:
service.beta.kubernetes.io/oci-load-balancer-shape: "flexible"
# GatewayClass — parametersRef로 확장 리소스 연결
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: custom-config

이 구조의 장점은 표준 리소스(Gateway, HTTPRoute)는 건드리지 않는다는 것이다. 구현체를 바꿔도 Gateway와 HTTPRoute는 그대로 유지되고, GatewayClass의 parametersRef만 새 구현체의 설정으로 교체하면 된다.

모든 구현체가 parametersRef를 쓰는 건 아니다

Envoy Gateway는 EnvoyProxy라는 확장 리소스를 parametersRef로 연결하지만, 모든 구현체가 이 방식을 쓰는 건 아니다.

예를 들어 Cilium은 parametersRef 없이 GatewayClass만 선언한다:

# Cilium GatewayClass — parametersRef 없음
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cilium
spec:
controllerName: io.cilium/gateway-controller

Cilium은 Helm values에서 직접 Gateway 관련 설정을 관리한다. 별도 확장 리소스를 만들지 않고도 충분히 동작한다.

parametersRef는 Gateway API가 제공하는 확장 메커니즘이지, 필수가 아니다. 구현체가 세부 설정을 어떻게 전달할지는 구현체의 설계 철학에 따라 다르다.


Ingress와 비교

항목IngressGateway API
역할 분리없음 (하나의 리소스)GatewayClass/Gateway(인프라) + HTTPRoute(개발)
벤더 설정어노테이션 (비표준)parametersRef (표준 확장 구조)
프로토콜HTTP/HTTPS만HTTP, gRPC, TCP, UDP, TLS
크로스 네임스페이스불가parentRefs + ReferenceGrant
트래픽 분배불가weight 기반 분배 내장
헤더 매칭불가 (어노테이션 의존)표준 스펙에 포함
여러 팀 공유충돌 위험N:M 관계로 자연스럽게 분리

Ingress가 하나의 칼로 모든 걸 하려 했다면, Gateway API는 역할에 맞는 도구를 나눠 쓰는 방식이다.


핵심 정리

Gateway API는 SIG-Network이 Ingress의 한계를 해결하기 위해 설계한 Kubernetes 표준이다.

  • GatewayClass: 어떤 컨트롤러를 쓸지 선언. parametersRef로 구현체 확장과 연결
  • Gateway: 어떤 포트/TLS를 열지 정의. 인프라 팀 영역
  • HTTPRoute: 어떤 경로를 어디로 보낼지 정의. 개발 팀 영역

이 3개가 1:N, N:M 관계로 연결되면서, Ingress에서 불가능했던 역할 분리, 크로스 네임스페이스 라우팅, 트래픽 분배가 표준 스펙 안에서 가능해졌다. 구현체별 설정은 parametersRef를 통해 표준과 분리되므로, 구현체를 바꿔도 Gateway와 HTTPRoute는 그대로 쓸 수 있다.

관련 콘텐츠

댓글