|
| 1 | +--- |
| 2 | +title: "빠르게 이해하는 Envoy 컴포넌트 구조" |
| 3 | +date: "2025-10-21" |
| 4 | +tags: ["Envoy"] |
| 5 | +summary: "A quick and practical overview of Envoy's core architecture." |
| 6 | +description: "Envoy 의 주요 컴포넌트 구조와 트래픽 처리 흐름, 로드밸런싱, Connection 관리, xDS 에 대해서 빠르게 알아보자." |
| 7 | +--- |
| 8 | + |
| 9 | +:::info |
| 10 | +envoy 는 service-mesh 를 구성할 때 핵심적인 역할을 담당하는 프록시이다. |
| 11 | +downstream/upstream 개념을 기반으로 Listener, Route, Cluster, Endpoint 를 통해 트래픽을 효율적으로 제어한다. |
| 12 | +envoy 의 주요 컴포넌트 구조와 트래픽 처리 흐름(Listener -> filter chain -> Router Filter -> Cluster), |
| 13 | +로드밸런싱 및 Connection 관리, xDS 를 통한 동적 설정 방식까지 빠르게 이해해보자. |
| 14 | +::: |
| 15 | + |
| 16 | +<Tabs> |
| 17 | + <Tab label="Basic"> |
| 18 | + <div style={{ textAlign: 'center' }}> |
| 19 | + <img src="/img/post/envoy/envoy.png" alt="envoy" style={{ display: 'inline-block' }} /> |
| 20 | + </div> |
| 21 | + * <b>Downstream</b> |
| 22 | + * Envoy 에게 요청을 보내는 호스트 |
| 23 | + * <b>Upstream</b> |
| 24 | + * Envoy 가 요청을 보내는 호스트 |
| 25 | + * <b>Listener</b> |
| 26 | + * 어떤 IP, Port 를 처리 할 지 정의한다. (=downstream 으로부터 요청을 받는 부분) |
| 27 | + * <b>Route</b> |
| 28 | + * Listener 로 들어온 요청을 어디로 보낼지 정의한다 (routing 대상은 Cluster) |
| 29 | + * <b>Cluster</b> |
| 30 | + * 실제 요청이 처리되는 IP 또는 Endpoint 들이다. (=upstream 들) |
| 31 | + * <b>Endpoint</b> |
| 32 | + * 실제로 접근 가능한 Endpoint (여러 endpoint 가 모여서 cluster 가 된다) |
| 33 | + </Tab> |
| 34 | + <Tab label="Listener"> |
| 35 | + :::success |
| 36 | + Listener 는? 어떤 IP, Port 를 처리 할 지 정의한다. (=downstream 으로 부터 요청 받은 부분) |
| 37 | + ::: |
| 38 | + * Listener 를 통해서 트래픽이 전달되면, Cluster 로 전달하기 위해서 여러 과정을 거치게 된다. |
| 39 | + * Listener 는 Listener Filters 와 Filter Chains <GreenText>(Network Filter)</GreenText> 를 지니고 있다. |
| 40 | + * Listener filters = Connection 에 대한 Metadata 를 조작하거나 추가하는데 사용된다. |
| 41 | + * Filters Chains = 이 정보를 바탕으로 실제 요청을 처리하는 부분이다. |
| 42 | + * Filters Chains 이 L3/L4 Filter 기능을 담당한다. |
| 43 | + <div style={{ textAlign: 'center' }}> |
| 44 | + <img src="/img/post/envoy/listener.png" alt="listener" style={{ display: 'inline-block' }} /> |
| 45 | + </div> |
| 46 | + </Tab> |
| 47 | + <Tab label="HTTP Connection manager"> |
| 48 | + * HTTP 를 담당하는 Network Level 의 Filter 로 Filter Chain 의 가장 마지막에 위치한다. |
| 49 | + * 주요 기능은 raw byte 를 HTTP 메시지로 Convert 를 담당하고, |
| 50 | + * HTTP Header 조작, Retry 설정, Redirect, Timeout |
| 51 | + * 내부적으로 HTTP L7 Filter 들이 존재하여 부가적인 작업을 수행한다. |
| 52 | + </Tab> |
| 53 | + <Tab label="Cluster"> |
| 54 | + :::success |
| 55 | + Cluster 는? 실제 요청이 처리되는 IP 또는 Endpoint 들이다. (=upstream 들) |
| 56 | + Endpoint 는? 실제로 접근 가능한 Endpoint |
| 57 | + ::: |
| 58 | + <BlueText>Cluster - Load Balancing</BlueText> <br/> |
| 59 | + * Cluster 컴포넌트를 통해서 Endpoint 를 그룹핑해서 관리할 수 있고, |
| 60 | + * Endpoint 중에서 어디로 트래픽을 보낼지 결정하는 <b>Load Balancing</b> 을 결정할 수 있다. |
| 61 | + <div style={{ textAlign: 'center' }}> |
| 62 | + <img src="/img/post/envoy/cluster.png" alt="cluster" style={{ display: 'inline-block' }} /> |
| 63 | + </div> |
| 64 | + |
| 65 | + ```yaml |
| 66 | + clusters: |
| 67 | + - name: some_service |
| 68 | + connection_timeout: 0.25s |
| 69 | + type: STATIC |
| 70 | + lb_policy: ROUND_ROBIN |
| 71 | + load_assignment: |
| 72 | + cluster_name: some_service |
| 73 | + endpoints: |
| 74 | + - lb_endpoints: |
| 75 | + - endpoint: |
| 76 | + address: |
| 77 | + socket_address: |
| 78 | + address: 127.0.0.1 |
| 79 | + port_value: 1234 |
| 80 | + ``` |
| 81 | + :::note |
| 82 | + <b>Service Discovery Type</b> |
| 83 | + - upstream cluster 가 정의 되면 Envoy 가 Cluster 구성원을 확인하는 방법 |
| 84 | + 1) STATIC |
| 85 | + - 가장 간단한 유형 (기본값) |
| 86 | + 2) STRICT_DNS |
| 87 | + - DNS 쿼리 결과를 엄격하게 취하여 전체 upstream cluster 를 구성 |
| 88 | + 3) LOGICAL_DNS |
| 89 | + - 비동기 확인 메커니즘이나, 첫번째 IP 주소만 사용 |
| 90 | + 4) EDS |
| 91 | + - xDS 관리 서버에서 가져온다. |
| 92 | + 5) ORIGINAL_DST |
| 93 | + - 원래 목적지 |
| 94 | + ::: |
| 95 | +
|
| 96 | + <BlueText>Cluster - Connection Pool</BlueText> <br/> |
| 97 | + * Cluster 는 Connection Pool 관리 기능도 수행한다. |
| 98 | +
|
| 99 | + :::warning |
| 100 | + <b>common_http_protocol_option</b>의 idle_timeout 을 지정하지 않으면 1시간이다. |
| 101 | + 요청이 들어왔을 때, application 에서 먼저 연결을 끊을 경우, |
| 102 | + upstream connect error or disconnect/reset before headers. reset reason: connection termination 이 발생할 수 있다. |
| 103 | + ::: |
| 104 | +
|
| 105 | + ```yaml |
| 106 | + "connect_timeout": "5s", ### Tomcat / Netty connection timeout 보다 작게 설정한다. (기본 값 5초) |
| 107 | + "typed_extension_protocol_options": { |
| 108 | + "envoy.extension_protocol_options": { |
| 109 | + "@type":"type.googleapis.com/envoy.extensions.upostreams.http.v3.HttpProtocolOptions", |
| 110 | + "common_http_protocol_options": { |
| 111 | + "idle_timeout":"60s" |
| 112 | + ### Tomcat / Netty idle timeout 설정보다 작게 설정한다. |
| 113 | + ### 지정하지 않으면 기본 값은 1시간이고, |
| 114 | + ### 0 으로 비활성화가 가능하지만, TCP FIN 패킷 손실등으로 인한 연결 누출이 발생할 수 있다. |
| 115 | + }, |
| 116 | + "explicit_http_config": { |
| 117 | + ### 여기서 http_protocol_options 나 http2_protocol_options 를 설정할 수 있다. |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + ``` |
| 122 | + </Tab> |
| 123 | + <Tab label="Flow"> |
| 124 | + :::success |
| 125 | + Downstream 은? Envoy 에게 요청을 보내는 호스트 |
| 126 | + Upstream 은? Envoy 가 요청을 보내는 호스트 |
| 127 | + Listener 는? 어떤 IP, Port 를 처리 할 지 정의한다. (=downstream 으로 부터 요청 받은 부분) |
| 128 | + Route 는? Listener 로 들어온 요청을 어디로 보낼지 정의한다 (routing 대상은 Cluster) |
| 129 | + Cluster 는? 실제 요청이 처리되는 IP 또는 Endpoint 들이다. (=upstream 들) |
| 130 | + Endpoint 는? 실제로 접근 가능한 Endpoint (여러 endpoint 가 모여서 cluster 가 된다) |
| 131 | + ::: |
| 132 | + |
| 133 | + HTTP 요청에 대한 내부 흐름은, 아래와 같다고 생각하면 된다. |
| 134 | + <div style={{ textAlign: 'center' }}> |
| 135 | + <img src="/img/post/envoy/flow.png" alt="flow" style={{ display: 'inline-block' }} /> |
| 136 | + </div> |
| 137 | + |
| 138 | + * 다른 노드나 Client 에서 <BlueText>(Downstream)</BlueText> Envoy 에게 요청을 보내고, |
| 139 | + * Envoy Listener 가 요청을 받아서 <BlueText>(Downstream 으로 부터 요청을 받음)</BlueText> |
| 140 | + * Listener 내부적으로 Listener Filters로 전달하고, Filter Chains를 순회하면서 요청을 처리하는데 적합한 Filter를 찾고 해당 Filter 가 요청을 처리한다. |
| 141 | + * Router Filter 는 요청에 적합한 Cluster 를 찾아서 해당 Cluster <BlueText>(Upstream 호스트 집단)</BlueText> 에게 트래픽을 Forwarding 한다. |
| 142 | + * Cluster 는 내부에 설정된 로드밸린싱 정책을 고려해서 적합한 Endpoint 를 선정하고, 매칭된 곳으로 트래픽을 전달한다. <BlueText>(To Upstream)</BlueText> |
| 143 | + </Tab> |
| 144 | + <Tab label="xDS"> |
| 145 | + * Envoy 컴포넌트(Listener, Cluster, Filter...) 들을 등록하는 방식에는 Static, Dynamic 이 있다. |
| 146 | + * Static 은 Envoy 기동 시점에 config 파일 정보를 기반으로 컴포넌트들을 등록하는 방식 |
| 147 | + * Dynamic 은 Envoy 가 기동 중인 상태로 config 를 변경할 수 있는 방식 |
| 148 | + * 내부 컴포넌트 설정을 변경할 수 있도록 인터페이스를 제공해주는데, 그게 xDS API 이다. |
| 149 | + <div style={{ textAlign: 'center' }}> |
| 150 | + <img src="/img/post/envoy/xds.png" alt="xds" style={{ display: 'inline-block' }} /> |
| 151 | + </div> |
| 152 | + </Tab> |
| 153 | +</Tabs> |
| 154 | + |
| 155 | +--- |
| 156 | + |
| 157 | +### 📚 Reference |
| 158 | + |
| 159 | +- [Envoy-internals 아키텍쳐 Overview by 북극 펭귄](https://cla9.tistory.com/191) |
| 160 | + |
| 161 | +<br/><br/> |
0 commit comments