Envoy Configuration with xDS
1. Envoy Configuration
Envoy Configuration은 Root Configration 역할을 수행하는 Bootstrap Configuration 파일과, 외부에서 동적으로 설정을 가져오는데 사용되는 xDS (eXtensible Discovery Services) Protocol이 함께 사용되어 이루어진다.
1.1. xDS (eXtensible Discovery Services) Protocol
![[Figure 1] xDS (eXtensible Discovery Services) Resources and API](/blog-software/docs/theory-analysis/envoy-configuration-xds/images/xds-resources-api.png)
[Figure 1] xDS (eXtensible Discovery Services) Resources and API
xDS **(eXtensible Discovery Services)**는 Envoy의 동작에 필요한 설정을 외부에서 동적으로 가져오는데 사용되는 CNCF의 표준 Protocol을 의미한다. [Figure 1]은 xDS를 이루는 Resource와 API를 나타내고 있다. xDS Resource는 다음과 같은 종류가 존재한다.
- Listener : Traffic을 수신할 주소와 Port, 그리고 이를 처리할 Filter Chain(Protocol, TLS 등)을 정의한다.
- Route : 요청을 어느 Cluster로 보낼지 결정하는 라우팅 규칙을 정의한다.
- Cluster : Upstream 서비스의 논리적 그룹으로, 연결 방식과 LB 정책을 정의한다.
- Endpoint : Cluster에 속한 실제 인스턴스의 IP:Port 목록을 정의한다.
- Secret : TLS 인증서와 키 등 민감 정보를 정의한다.
- Extension Config : Listener의 Filter 자리에 이름으로만 참조해 둔 Extension(Wasm Filter 등)의 실제 설정을 정의한다.
각 xDS Resource는 대응하는 xDS API를 통해 동적으로 설정된다. xDS API의 종류는 다음과 같다.
- LDS (Listener Discovery Service) : Listener 설정을 동적으로 전달한다.
- RDS (Route Discovery Service) : Route 설정을 동적으로 전달한다.
- CDS (Cluster Discovery Service) : Cluster 설정을 동적으로 전달한다.
- EDS (Endpoint Discovery Service) : Endpoint 설정을 동적으로 전달한다.
- SDS (Secret Discovery Service) : Secret 설정을 동적으로 전달한다.
- ECDS (Extension Config Discovery Service) : HTTP Filter, Listener Filter 등 Envoy의 다양한 확장(Extension) 지점에 범용적으로 쓰이는 동적 설정 기법이다. Listener나 Route 안에 확장 설정을 통째로 넣는 대신 “이 설정은 ECDS에서 가져와"라는 참조만 남겨두면, Envoy가 필요할 때 해당 확장의 설정만 별도로 받아온다. 따라서 확장 하나의 설정을 바꾸고 싶을 때 Listener나 Route 전체를 다시 받을 필요 없이, 해당 설정만 독립적으로 갱신할 수 있다.
- ADS (Aggregated Discovery Service) : 새로운 설정을 전달하는 API가 아니라, LDS/RDS/CDS/EDS/SDS/ECDS를 별도의 연결 대신 하나의 gRPC Stream으로 묶어 전달하는 전송 기법이다. 이를 통해 Management Server가 CDS → EDS → LDS → RDS와 같이 의존성에 맞는 적용 순서를 보장할 수 있으며, 설정 갱신 과정에서 발생할 수 있는 Traffic 유실을 방지할 수 있다.
1.1.1. LDS (Listener Discovery Service)
| |
[Config 1]은 [Figure 1]의 Listener 부분에 해당하는 LDS 설정 예시를 나타내고 있다. internal-listener는 8080 Port에서 mTLS로 Traffic을 수신하며, internal-cert Secret으로 자신을 증명하고 internal-ca Secret으로 Client를 검증한 뒤 요청을 internal-routes Route Table로 넘긴다. HTTP Filter Chain에는 Wasm Filter가 internal-wasm라는 이름의 config_discovery 참조로만 들어 있으며, 실제 Filter 설정은 ECDS를 통해 별도로 전달받는다 (1.1.6에서 다룬다). external-listener는 443 Port에서 TLS Inspector로 SNI를 확인하여 Filter Chain을 선택한다. web.com Chain은 web-cert Secret으로 TLS를 종료한 뒤 external-routes Route Table로 요청을 넘기고, kafka.com Chain은 kafka-cert Secret으로 TLS를 종료한 뒤 Route Table을 거치지 않고 TCP Proxy를 통해 kafka Cluster로 바로 전달한다.
1.1.2. RDS (Route Discovery Service)
| |
[Config 2]는 [Figure 1]의 Route 부분에 해당하는 RDS 설정 예시를 나타내고 있다. internal-routes Route Table은 Host Header를 기준으로 Virtual Host를 선택한다. reviews Host의 /api 경로 요청은 reviews-v1과 reviews-v2 Cluster로 80:20 가중치로 분배되고, 나머지 경로의 요청은 모두 reviews-v1 Cluster로 전달된다.
여기서 Virtual Host 내부의 routes 목록은 위에서부터 순서대로 평가되어 처음 매칭되는 항목이 적용되는 First Match Wins 방식으로 동작한다. 따라서 /api처럼 구체적인 항목을 catch-all 항목(/) 앞에 두어야 하며, 순서가 반대가 되면 모든 요청이 catch-all 항목에 먼저 매칭되어 /api 항목은 선택되지 않는다. ratings Host의 요청은 모두 ratings Cluster로 전달된다. external-routes Route Table은 web.com Host의 요청을 모두 web Cluster로 전달한다.
1.1.3. CDS (Cluster Discovery Service)
| |
[Config 3]은 [Figure 1]의 Cluster 부분에 해당하는 CDS 설정 예시를 나타내고 있다. reviews-v1, reviews-v2, ratings, web, kafka 다섯 개의 Cluster가 정의되어 있으며, 모두 type: EDS로 설정되어 실제 인스턴스 목록은 EDS를 통해 별도로 전달받는다. 또한 모든 Cluster는 Upstream 연결 시 internal-cert Secret을 Client 인증서로 사용하고 internal-ca Secret으로 상대방을 검증하는 mTLS 설정을 공유하며, [Figure 1]의 각 Cluster에 표시된 SDS: internal-cert가 이를 의미한다.
1.1.4. EDS (Endpoint Discovery Service)
| |
[Config 4]는 [Figure 1]의 Endpoint 부분에 해당하는 EDS 설정 예시를 나타내고 있다. 각 ClusterLoadAssignment의 cluster_name은 CDS에서 정의한 Cluster 이름과 일치해야 하며, 이를 통해 Cluster와 실제 인스턴스 목록이 연결된다. reviews-v1 Cluster는 10.0.0.11:80과 10.0.0.12:80 두 개의 Endpoint로 요청이 분배되고, TCP Upstream인 kafka Cluster는 10.0.0.51:9092 Endpoint를 가진다.
1.1.5. SDS (Secret Discovery Service)
| |
[Config 5]는 [Figure 1]의 Secret 부분에 해당하는 SDS 설정 예시를 나타내고 있다. internal-cert는 internal-listener의 mTLS 종료와 모든 Cluster의 Client 인증서로 함께 사용되며, internal-ca는 상대방 인증서 검증에 사용되는 CA Bundle이다. web-cert와 kafka-cert는 external-listener에서 SNI에 따라 선택되는 Filter Chain별 Server 인증서이다.
1.1.6. ECDS (Extension Config Discovery Service)
| |
[Config 6]은 [Config 1]의 internal-listener가 config_discovery로 참조하고 있는 internal-wasm의 실제 설정을 전달하는 ECDS 예시를 나타내고 있다. Listener의 HTTP Filter 자리에는 설정 본문 대신 참조(이름과 type_url)만 두고, 실제 Filter 설정은 같은 이름의 TypedExtensionConfig Resource로 별도 전달받는다.
Filter 설정이 Listener 안에 Inline으로 들어 있으면 Filter 설정 변경도 Listener 변경이 된다. Envoy는 동작 중인 Listener의 설정을 직접 변경하지 못하므로, 변경된 설정으로 새 Listener를 만들어 교체한다. 이 과정에서 기존 Listener가 처리하던 연결들은 Drain을 거쳐 일정 시간 안에 모두 끊어진다. 즉 Filter 설정 한 줄을 바꿔도 해당 Port의 Long-lived 연결이 끊길 수 있다.
반면 ECDS를 사용하면 Filter 설정이 Listener 밖의 독립된 Resource로 분리되어 있으므로, 설정 갱신 시 Listener는 그대로 유지되고 참조된 설정만 교체된다. 기존 연결은 영향을 받지 않으며, 갱신된 Filter 설정은 이후의 새 요청부터 적용된다. RDS가 Route를 Listener에서 분리하여 Route 변경이 Listener 교체를 유발하지 않게 만든 것처럼, ECDS는 같은 분리를 Filter 설정에 대해 수행하는 것이다. Wasm Filter처럼 설정이 크거나 자주 바뀌는 Extension에 주로 사용되며, Istio의 WasmPlugin CR이 이 방식으로 반영되는 대표적인 예이다.
1.1.7. ADS (Aggregated Discovery Service)
| |
[Config 7]은 [Figure 1] 상단의 1 Stream with ADS에 해당하는, 하나의 gRPC Stream 위에서 오가는 xDS 메시지 흐름을 나타내고 있다. Envoy는 CDS와 LDS를 Wildcard로 구독하고, 응답으로 받은 Cluster와 Listener가 참조하는 이름을 기반으로 EDS, RDS, SDS, ECDS 구독이 파생된다. Envoy는 각 응답에 대해 version과 nonce를 되돌려주는 ACK를 보내며, 잘못된 설정을 받은 경우에는 NACK를 보내고 마지막 정상 버전을 유지한다.
1.2. Bootstrap Configuration
| |
Envoy Configuration은 Bootstrap Configuration 파일을 통해서 이루어진다. Bootstrap Configuration 파일은 이름에서 알 수 있듯이 Envoy의 Bootstrap 시에 사용되는 Configuration 파일을 의미하며, Envoy의 Root Configuration을 의미한다. [Shell 1]은 Envoy를 Bootstrap Configuration 파일과 함께 실행하는 예시를 나타내고 있다.
Envoy는 Bootstrap Configuration 파일에 필요한 설정을 모두 넣어서 고정적으로 이용하는 Static Configuration과, xDS (eXtensible Discovery Services) Protocol을 통해서 외부로부터 동적으로 가져와 이용하는 Dynamic Configuration으로 크게 구분할 수 있다.
1.2.1. Static Configuration
| |
[Config 8]은 Envoy의 Static Configuration 예시를 나타내고 있다. Static Configuration은 Bootstrap Configuration 파일에 Envoy 동작에 필요한 모든 설정을 고정값으로 넣어서 이용하는 방식을 의미한다. static_resources 아래에 Listener, Route, Cluster, Endpoint가 모두 Inline으로 정의되어 있으며, [Figure 1]에서 xDS API를 통해 전달되던 각 Resource가 파일 안에 그대로 들어간 형태이다.
listener_http는 10000 Port로 수신한 모든 요청을 Inline Route Table(local_route)을 거쳐 service_backend Cluster의 두 Endpoint로 전달한다. xDS Server가 필요 없어 구성이 단순하지만, 설정을 변경하려면 파일을 수정하고 Envoy를 재시작해야 한다.
1.2.2. Mostly Static with Dynamic EDS
| |
[Config 9]는 Listener, Route, Cluster는 Static으로 고정하고 Endpoint만 EDS로 동적으로 받는 예시를 나타내고 있다. service_backend Cluster가 type: EDS로 설정되어 있어, 실제 인스턴스 목록은 eds_config에 지정된 xDS Server로부터 service_name(service_backend)을 Key로 구독한다. 이때 api_config_source는 ADS가 아닌 EDS 전용 gRPC Stream을 사용한다.
xDS Server의 주소 자체는 동적으로 받아올 수 없으므로, xds_cluster는 Static Cluster로 Bootstrap 파일에 직접 정의되어야 하며 gRPC 통신을 위해 HTTP/2가 활성화되어 있다. 이 방식은 배포나 Scaling으로 인스턴스 IP만 자주 바뀌는 환경에서, 라우팅 구조는 고정한 채 Endpoint 갱신만 재시작 없이 반영하고 싶을 때 사용된다.
1.2.3. Dynamic Configuration
| |
[Config 10]은 Listener와 Cluster부터 모든 Resource를 xDS로 받아오는 Dynamic Configuration 예시를 나타내고 있다. dynamic_resources의 lds_config와 cds_config가 모두 ads로 지정되어 있어, LDS와 CDS 구독이 ads_config에 정의된 단일 gRPC Stream으로 전달되고, 응답에서 파생되는 RDS, EDS, SDS, ECDS 구독도 같은 Stream을 공유한다. 이 Stream 위에서 오가는 메시지 흐름이 [Config 7]이다.
node는 xDS Server가 어느 Envoy에게 어떤 설정을 내려줄지 구분하는 Identity이며, set_node_on_first_message_only는 Stream의 첫 메시지에만 node를 실어 이후 메시지의 크기를 줄인다. 결과적으로 Bootstrap 파일에는 xDS Server 접속 정보(xds_cluster)와 admin만 남고, 앞서 살펴본 [Config 1~6]의 모든 Resource가 이 연결을 통해 동적으로 전달된다.
2. 참조
- Envoy Life of a Request : https://www.envoyproxy.io/docs/envoy/latest/intro/life_of_a_request
- Envoy Listener Filters : https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listener_filters/listener_filters
- Envoy Network Filters : https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/network_filters/network_filters