Envoy Gateway 핵심 개념: Gateway, Listener, Proxy, 그리고 Route 계열 완전 해설
Gateway API를 처음 보면 많은 사람이 이런 느낌을 받습니다. "용어 수가 엄청 많은 건 아닌데, 이름 비슷한 등장인물만 가득한 출연진 표를 보는 느낌이다. 누가 주인공이고 누가 조연인지 모르겠다." 이 글은 바로 그 혼란을 잘라내기 위해 존재합니다. 그리고 그 과정에서 자주 쓰는 route 타입들도 함께 정리합니다.
전체 아키텍처
공식 Envoy Gateway 개념 문서는 시스템을 세 계층으로 나눕니다:
- User Configuration: 여러분이 작성하는
Gateway API리소스와 Envoy Gateway 확장 CRD - Envoy Gateway Controller: 리소스를 읽고, 검증하고, 설정으로 번역
- Envoy Proxy: 실제로 트래픽을 처리하는 데이터 플레인
즉, 여러분이 보통 수정하는 것은 Envoy Proxy 자체가 아닙니다. 여러분은 "원하는 상태를 선언"하고 있는 겁니다. Envoy Gateway는 그 선언을 Envoy Proxy가 실행할 수 있는 설정으로 바꿔 줍니다. 그래서 프록시 설정을 직접 쓰는 것보다 훨씬 다루기 편합니다.
핵심 역할들
지금은 이것만 기억해 두면 됩니다:
GatewayClass: "이 클러스터에서 어떤 Gateway 구현체를 쓸지" 정의Gateway: "ingress가 어디에 있고 어떤 프로토콜을 말하는지" 정의Listener: 특정 port/protocol/hostname ingress 슬롯 정의Route: "어떤 요청을 어떤 Service로 보낼지" 정의Envoy Proxy: 실제로 트래픽을 받고 전달하는 Gateway Proxy
쉽게 풀어 말하면:
GatewayClass는 사양서Gateway는 건물의 정문Listener는 프런트 데스크Route는 건물 내부의 안내 표지판Envoy Proxy는 출입 통제와 안내를 맡는 보안 시스템
이들 중 하나라도 빠지면 트래픽은 원하는 목적지까지 가지 못합니다.
전체 그림이 보이는 최소 YAML
이해 가능한 가장 작은 예시는 이렇습니다:
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: eg
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend
spec:
parentRefs:
- name: eg
hostnames:
- "www.example.com"
rules:
- backendRefs:
- name: backend
port: 3000이걸 체인으로 보면 로직은 다음과 같습니다:
GatewayClass가Envoy Gateway컨트롤러가 담당한다고 선언Gateway가 포트80에서 하나의 HTTP ingress를 선언HTTPRoute가www.example.com요청을backend:3000으로 보내라고 정의Envoy Gateway가 이 전체를Envoy Proxy용 설정으로 번역
각 필드 이해하기
GatewayClass
이것은 클러스터 수준의 ingress 타입 정의입니다. 보통 가장 중요한 필드는 다음입니다:
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller뜻은 아주 단순합니다:
"이 GatewayClass는 Envoy Gateway 컨트롤러가 관리한다."
controllerName이 맞지 않으면, 잘못된 회사에 택배를 보낸 것과 같습니다. 아무도 수거하러 오지 않죠.
Gateway
이것은 "ingress가 어떤 모습인지"를 정의합니다. 가장 자주 참고하는 필드는 listeners입니다:
listeners:
- name: http
protocol: HTTP
port: 80의미는 다음과 같습니다:
http라는 이름의 listener를 연다- HTTP 트래픽을 받는다
- 외부 포트
80에서 리슨한다
HTTPS를 쓰려면 일반적으로 protocol: HTTPS인 listener를 하나 더 추가하고 TLS 설정을 붙입니다.
Gateway가 주로 관리하는 것:
- 외부 포트와 프로토콜
- listener별 hostname
- TLS termination 위치
- 어떤 네임스페이스의 route가 attach할 수 있는지
Gateway는 플랫폼 계층의 관심사에 더 가깝고, 소유권도 보통 플랫폼 팀에 있습니다.
Listener
이건 초보자가 가장 자주 놓치는 역할이지만, 실제로는 매우 중요합니다.
listener는 Gateway 바깥으로 노출된 ingress 슬롯입니다. 다음을 정의합니다:
nameportprotocolhostnametlsallowedRoutes
예시:
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: api.example.com이 말은 곧:
- 이 슬롯 이름은
https - HTTPS를 받는다
- 포트
443에서 리슨한다 api.example.com전용이다
만약 listener가 여러 개라면, 예를 들어:
- 포트 80용
http - 포트 443용
https - 포트 50051용
grpc
같은 Gateway 안에서 여러 ingress 슬롯을 여는 셈이고, 각각 다른 route가 붙을 수 있습니다.
Envoy Proxy
많은 사람이 YAML에만 집중하고 Envoy Proxy가 실제 라이브 트래픽을 처리한다는 점을 잊습니다.
역할은 다음과 같습니다:
- 클라이언트 요청 수신
- Envoy Gateway가 푸시한 설정과 매칭
- 적절한 백엔드로 트래픽 전달
- TLS, header, timeout, retry 등 데이터 플레인 동작 처리
즉 Envoy Gateway는 "컨트롤 플레인", Envoy Proxy는 "데이터 플레인 / Gateway Proxy"라고 생각하면 됩니다.
Route 계열
Route는 단순히 HTTPRoute만을 뜻하지 않습니다. 이 계열 전체가 Gateway API 설계의 핵심입니다.
HTTPRoute
여러분이 가장 자주 작성하고 수정하게 될 타입입니다. 핵심 필드 세 가지:
spec:
parentRefs:
- name: eg
hostnames:
- "www.example.com"
rules:
- backendRefs:
- name: backend
port: 3000parentRefs: 어느 Gateway에 attach할지hostnames: 어떤 Host header가 이 규칙을 트리거하는지backendRefs: 매칭된 요청을 어디로 보낼지
가장 흔한 오해는 Gateway에 세밀한 라우팅을 적는다고 생각하는 것입니다. 실제로 "트래픽을 어떻게 나눌지"를 결정하는 것은 HTTPRoute입니다.
GRPCRoute
gRPC를 다룬다면, 억지로 HTTPRoute에 밀어 넣는 것보다 GRPCRoute가 더 자연스럽습니다.
다음 기준으로 매칭할 수 있습니다:
- gRPC hostname
- gRPC service
- gRPC method
- headers
예시:
matches:
- method:
service: com.example.User
method: Login이 의미 체계는 gRPC와 자연스럽게 맞아떨어집니다. 단순히 URL path를 보는 게 아니라, gRPC service/method 모델에 직접 매칭하는 것이니까요.
공식 문서도 말하듯, GRPCRoute와 HTTPRoute가 같은 listener를 공유하고 hostname이 충돌하면 구현체는 한쪽을 거부해야 합니다. 쉽게 말해 HTTP와 gRPC가 같은 hostname을 두고 싸우게 두지 말라는 뜻입니다.
TCPRoute
TCPRoute는 raw TCP 트래픽을 처리합니다.
HTTPRoute와 달리 path나 header 같은 L7 의미를 이해하지 못합니다. L4 포워딩에 더 가깝습니다.
적합한 시나리오:
- 비 HTTP 프로토콜 서비스
- 순수 TCP 서비스
- listener 포트만 기준으로 서로 다른 백엔드에 연결을 보내는 경우
커스텀 프로토콜을 쓰는 데이터베이스나 독자 TCP 프로토콜은 전형적인 TCPRoute 사용 사례입니다.
TLSRoute
TLSRoute는 TLS 트래픽을 처리하며, 특히 TLS passthrough 시나리오에서 자주 등장합니다.
핵심 개념:
- Gateway가 반드시 TLS를 종료하는 것은 아니다
- TLS 핸드셰이크의 SNI hostname만 보고 라우팅할 수 있다
- 암호화된 트래픽은 백엔드까지 그대로 유지된다
이것은 end-to-end 암호화가 필요한 경우, 혹은 Gateway에서 TLS를 종료하고 싶지 않은 경우에 중요합니다.
Route 타입은 어떻게 고를까
| 요구사항 | 이걸 사용 |
|---|---|
| 웹사이트, REST API, 일반 웹 트래픽 | HTTPRoute |
| gRPC service/method 라우팅 | GRPCRoute |
| raw TCP 트래픽 | TCPRoute |
| TLS passthrough / SNI 기반 라우팅 | TLSRoute |
이 표가 중요한 이유는 많은 사람이 본능적으로 모든 것에 HTTPRoute를 쓰기 때문입니다. 하지만 실제 대상이 gRPC나 TCP라면, HTTPRoute에 억지로 끼워 넣을수록 YAML만 점점 어색해집니다.
Ingress보다 Route가 더 이해하기 쉬운 이유
기존 Ingress 설정은 너무 많은 책임을 하나의 리소스에 욱여넣는 경우가 많았고, 그 결과 몇 가지 지속적인 문제를 낳았습니다:
- 플랫폼 설정과 앱 라우팅이 한데 섞임
- 고급 기능이 가독성 나쁜 annotation으로 표현됨
- 비 HTTP 프로토콜에 대한 표현력이 제한적임
Gateway API는 책임을 분리하는 방식을 택합니다:
- 플랫폼 팀은
GatewayClass/Gateway관리 - 앱 팀은 각자
HTTPRoute관리
이 분리는 여러 사람이 협업할 때 특히 잘 맞습니다. 단순히 path 하나 바꾸려고 ingress 전체를 건드릴 필요가 없고, 팀끼리 서로 밟는 일도 크게 줄어듭니다.
이제 status를 보기 시작하세요
spec뿐 아니라, 점차 status를 확인하는 습관을 들이세요. 특히 HTTPRoute와 Gateway에서 중요합니다:
kubectl get gateway eg -o yaml
kubectl get httproute backend -o yaml처음에는 이것만 확인해도 좋습니다:
- route가
Accepted되었는가 - gateway listener가 Ready 인가
- address가 정상 할당되었는가
시험으로 치면 제출만 확인하지 말고 채점과 합격 여부까지 봐야 합니다.
한 줄 요약
GatewayClass, Gateway, Listener, Route, Envoy Proxy를 각각 "누가 관리하는지, ingress가 어떻게 열리는지, 각 슬롯이 어떻게 연결되는지, 트래픽이 어떻게 나뉘는지, 누가 실제로 실행하는지"로 이해하면 핵심 개념의 80%는 이미 잡은 셈입니다.
💡 초보자는 종종 곧바로 CRD 세부 필드로 뛰어듭니다. 서두르지 마세요. 먼저 각 역할을 정리하면 YAML이 더 이상 매번 낯선 사람처럼 느껴지지 않습니다.
다음 단계
이제 개념이 잡혔으니, 다음 글에서는 가장 흔한 HTTP 라우팅 시나리오인 host, path, header, 트래픽 분할을 살펴봅니다: 👉 실전 HTTP 라우팅