본문으로 건너뛰기
Envoy Traffic Processing

Envoy Traffic Processing

1. Envoy HTTP Traffic Processing

[Figure 1] Envoy HTTP Traffic Processing

[Figure 1] Envoy HTTP Traffic Processing

1.1. Listener

Listener는 커널이 TCP 3-way Handshake를 완료해 Accept Queue에 올려둔 Downstream 연결을 accept() 함수로 수락하여 새로운 Socket을 얻는다. 이후에 얻은 Socket을 Dispatcher에 등록하고, TCP Connection 관련 정보를 얻기 위한 Listener Filter Chain Instance를 생성한다. 다수의 Listener가 등록되어 있는 경우에는 일반적으로 IP, Port를 통해서 어떤 Listener가 해당 Connection을 처리할지 결정하게 된다.

1.2. Listener Filter Chain

Listener Filter Chain은 Downstream과 TCP 연결이 맺어진 뒤, 들어온 Traffic의 앞부분을 소비하지 않고 recv(..., MSG_PEEK)로 엿보아 필요한 연결 정보를 얻는 데 사용된다. 일부 Filter(proxy_protocol)는 앞쪽의 PROXY Protocol Header를 실제로 읽어 원래 클라이언트 주소를 복원하고 그 헤더를 소비(제거)하기도 한다. Listener는 새로운 연결마다 Listener Filter 인스턴스를 생성하므로, 각 TCP 연결별로 별도의 Listener Filter Chain이 존재한다. Envoy가 제공하는 대표적인 Listener Filter는 다음과 같다.

  • envoy.filters.listener.original_dst : iptables REDIRECT 등으로 가려진 원래 목적지 IP, Port를 getsockopt(SO_ORIGINAL_DST) System Call로 복원한다. Istio 같은 Mesh Network 환경에서 Envoy가 실제 목적지 정보를 얻기 위해 사용된다.
  • envoy.filters.listener.original_src : 원래 Downstream의 출발지 IP, Port를 setsockopt(IP_TRANSPARENT) System Call로 보존하여 Upstream으로 전송한다.
  • envoy.filters.listener.proxy_protocol : PROXY Protocol을 사용하는 경우, 맨 앞 PROXY Header를 읽어 원래 Client IP, Port를 Envoy에서 Downstream IP, Port로 설정하고 그 Header를 제거한다.
  • envoy.filters.listener.tls_inspector : TLS를 사용하는 경우, ClientHello를 엿보아 SNI(Server Name Indication), ALPN(Application Layer Protocol Negotiation) 등 정보를 추출한다.
  • envoy.filters.listener.http_inspector : TLS를 사용하지 않아 ALPN 이용이 불가능한 경우, 앞쪽 Byte를 엿보아 HTTP 프로토콜 버전(HTTP/1.x인지 HTTP/2인지)을 감지한다.

Listener Filter Chain은 Envoy Config에 따라서 자유롭게 구성할 수 있지만, 일반적으로 목적지 IP, Port를 복원하기 위한 original_dst Filter를 첫 번째로 사용하며, Proxy Protocol을 사용하는 경우, Proxy Header를 제거하기 위한 proxy_protocol Filter를 두 번째로 구성한다. 단순히 TCP Connection의 Meta 정보를 얻기 위한 tls_inspector, http_inspector Filter는 뒤에 구성하며, 일반적으로 tls_inspector, http_inspector 순서대로 구성하여 TLS가 아닌 경우, http_inspector Filter가 HTTP 버전을 판별하도록 한다.

1
virtual FilterStatus onAccept(ListenerFilterCallbacks& cb) PURE;
[Code 1] Listener Filter Chain Interface

[Code 1]은 Listener Filter Chain이 구현해야 하는 onAccept() Interface를 나타내고 있다. Parameter로 onAccept() 함수 내부에서 현재 TCP Connection을 제어할 수 있는 ListenerFilterCallbacks Callback 함수를 전달하며, 결과로 Listener Filter Chain을 계속 진행할지 또는 잠깐 중단할지를 결정하는 FilterStatus를 반환한다.

1.3. Filter Chain Manager

Listener Filter Chain의 tls_inspector Filter 또는 http_inspector Filter를 통해서 Server Name과 Application Protocol을 파악한 이후에, Filter Chain Manager는 해당 TCP Connection으로 전송되는 요청을 처리하기 위한 Downstream Transport Socket Instance와 Network Filter Chain Instance를 생성한다. 즉 각 TCP Connection별로 별도의 Downstream Transport Socket Instance와 Network Filter Chain Instance가 존재하게 된다.

1.4. Downstream Transport Socket

Downstream Transport Socket은 Downstream으로부터 전달받은 Traffic을 Network Filter Chain으로 전달하기 위한 다리 역할을 수행한다. TLS가 적용된 경우 TLS Transport Socket을 통해서 Downstream으로부터 전달받은 Traffic을 복호화하여 Network Filter Chain으로 전달하고, 반대로 Network Filter Chain으로부터 전달받은 Traffic을 암호화하여 Downstream으로 전송한다. 따라서 Network Filter Chain은 암호화되지 않은 Plain Text를 처리하게 된다.

TLS가 적용되지 않은 경우에는 Raw Buffer Transport Socket을 통해서 Downstream으로부터 전달받은 Traffic을 변형 없이 그대로 Network Filter Chain으로 전달하거나, 반대로 Network Filter Chain으로부터 전달받은 Traffic을 변형 없이 Downstream으로 전송하는 역할을 수행한다.

1
2
3
4
virtual void onConnected() PURE;
virtual IoResult doRead(Buffer::Instance& buffer) PURE;
virtual IoResult doWrite(Buffer::Instance& buffer, bool end_stream) PURE;
virtual void closeSocket(Network::ConnectionEvent event) PURE;
[Code 2] Downstream Transport Socket Interface

[Code 2]는 Downstream Transport Socket이 구현해야 하는 onConnected(), doRead(), doWrite(), closeSocket() Interface를 나타내고 있으며, 역할은 다음과 같다.

  • onConnected() : TCP Connection이 맺어진 이후 호출되며, TLS를 이용하는 경우 TLS Handshake를 개시한다.
  • doRead() : Downstream으로부터 전달받은 Traffic을 읽어 Network Filter Chain으로 전달한다. TLS를 이용하는 경우 복호화하여 Plain Text를 올린다.
  • doWrite() : Network Filter Chain으로부터 전달받은 Traffic을 Downstream으로 전송한다. TLS를 이용하는 경우 암호화하여 Cipher Text를 내보낸다.
  • closeSocket() : Socket이 닫히면 호출되며, TLS를 이용하는 경우 close_notify Alert을 전송하고 Session을 종료한다.

1.5. Network Filter Chain

Network Filter Chain은 Downstream으로부터 전달받은 Plain Text를 가공하여 Upstream HTTP Filter Chain으로 전달하는 역할을 수행한다. Envoy의 대부분의 핵심 기능은 Network Filter Chain에서 구현된다. Network Filter는 Chain 중간에 실행되는 Non-terminal Filter와 Chain의 마지막에 실행되는 Terminal Filter로 구분되며, Non-terminal Filter는 자유롭게 순서를 변경하여 구성할 수 있지만, Terminal Filter는 반드시 Network Filter Chain의 마지막에 위치해야 한다.

Envoy에서 제공하는 주요 Non-terminal Filter는 다음과 같다.

  • envoy.filters.network.connection_limit : Downstream 동시 커넥션 수 제한 수행.
  • envoy.filters.network.rbac : L4 기반 접근 제어 수행. (IP/SNI/mTLS Principal)
  • envoy.filters.network.ext_authz : L4 기반 외부 인가 수행. (gRPC authz 서버 호출)
  • envoy.filters.network.local_ratelimit : L4 기반 Local Rate Limit 수행. (Local Token Bucket 이용)
  • envoy.filters.network.ratelimit : L4 기반 Global Rate Limit 수행. (Rate Limit Service를 활용한 Global Token Bucket 이용)
  • envoy.filters.network.wasm : WASM(WebAssembly)을 이용한 커스텀 L4 로직 수행.
  • envoy.filters.network.mysql_proxy : MySQL Protocol 파싱 및 통계 처리 수행. Terminal Filter로 mysql_proxy Filter 이용.
  • envoy.filters.network.postgres_proxy : PostgreSQL Protocol 파싱 및 통계 처리 수행. Terminal Filter로 postgres_proxy Filter 이용.
  • envoy.filters.network.mongo_proxy : MongoDB Protocol 파싱 및 통계 처리 수행. Terminal Filter로 tcp_proxy Filter 이용.

Envoy에서 제공하는 Terminal Filter는 다음과 같다.

  • envoy.filters.network.tcp_proxy : TCP Proxy 수행.
  • envoy.filters.network.redis_proxy : Redis Protocol 처리.
  • envoy.filters.network.kafka_broker : Kafka Protocol 처리.
  • envoy.filters.network.http_connection_manager : HTTP/1.x, HTTP/2.0, HTTP/3 Protocol 처리.
1
2
3
virtual FilterStatus onNewConnection() PURE;
virtual FilterStatus onData(Buffer::Instance& data, bool end_stream) PURE;
virtual FilterStatus onWrite(Buffer::Instance& data, bool end_stream) PURE;
[Code 3] Network Filter Chain Interface

[Code 3]는 Network Filter Chain이 구현해야 하는 onNewConnection(), onData(), onWrite() Interface를 나타내고 있으며, 역할은 다음과 같다.

  • onNewConnection() : 처음 TCP Connection이 맺어진 이후 한번 호출되며, TCP Connection의 속성만으로 초기 판단(동시 커넥션 수 제한, L4 접근 제어)을 하고, 결과로 Continue 또는 StopIteration을 반환한다.
  • onData() : Downstream에서 Upstream으로 요청 Traffic을 전송할 때마다 호출된다. 결과로 Continue 또는 StopIteration을 반환한다.
  • onWrite() : Upstream에서 Downstream으로 응답 Traffic을 전송할 때마다 호출된다. 결과로 Continue 또는 StopIteration을 반환한다.

1.5.1. HTTP Connection Manager

HTTP Connection Manager (HCM)은 HTTP Protocol을 처리하는 Terminal Network Filter로 동작한다. 앞의 Network Filter Chain에서는 L4 기반으로 동작하지만, HTTP Connection Manager는 L7 기반으로 동작한다.

1.5.1.1. HTTP Codec

HTTP Codec은 Downstream에서 Upstream으로 요청 Traffic을 전송하는 경우에는, HTTP Protocol Version에 관계없이 HTTP Filter가 일관된 형태로 처리할 수 있도록 Stream을 Decoding하여 Header와 Body를 분리한다. 반대로 Upstream에서 Downstream으로 응답 Traffic을 전송하는 경우에는, Downstream에서 이용중인 HTTP Protocol Version에 맞춰서 Stream을 Encoding하여 전송하는 역할을 수행한다.

1.5.1.2. Downstream HTTP Filter

Downstream HTTP Filter는 Router에 넘기기 전에 L7 기반으로 Traffic을 처리하는 역할을 수행한다. 인증/인가, Traffic 제한, Traffic 가공 등의 다양한 기능을 수행할 수 있다. Downstream HTTP Filter도 자유롭게 순서를 변경하여 구성할 수 있지만, Router Filter는 반드시 마지막에 위치해야 한다.

Envoy에서 제공하는 인증/인가 Filter는 다음과 같다. 일반적으로 Chain 앞쪽에 구성한다.

  • envoy.filters.http.jwt_authn : JWT 검증. 검증된 claim을 뒤 필터가 쓸 수 있게 전달.
  • envoy.filters.http.ext_authz : 외부 인가 서비스(gRPC/HTTP) 호출로 허용/차단 결정.
  • envoy.filters.http.rbac : L7 접근 제어. (경로/헤더/JWT Claim 기반)
  • envoy.filters.http.oauth2 : OAuth2 Login 처리.

Envoy에서 제공하는 Traffic 제한 Filter는 다음과 같다.

  • envoy.filters.http.local_ratelimit : L7 기반 Local Rate Limit 수행. (Local Token Bucket 이용)
  • envoy.filters.http.ratelimit : L7 기반 Global Rate Limit 수행. (Rate Limit Service를 활용한 Global Token Bucket 이용)

Envoy에서 제공하는 Traffic 가공 Filter는 다음과 같다.

  • envoy.filters.http.cors : CORS 처리.
  • envoy.filters.http.grpc_web : gRPC-Web <-> gRPC 변환.
  • envoy.filters.http.grpc_json_transcoder : REST/JSON <-> gRPC 변환.
  • envoy.filters.http.compressor : Upstream에서 Downstream으로 나가는 응답 Traffic 압축.
  • envoy.filters.http.decompressor : Downstream에서 Upstream으로 들어오는 요청 Traffic 압축 해제.
  • envoy.filters.http.header_mutation : HTTP Header 추가/삭제/수정.
  • envoy.filters.http.grpc_stats : gRPC 통계 수집.

Envoy에서 제공하는 커스텀 로직 Filter는 다음과 같다.

  • envoy.filters.http.lua : Lua Script를 이용한 커스텀 로직.
  • envoy.filters.http.wasm : L7 WASM (WebAssembly) 기반 커스텀 로직.
  • envoy.filters.http.fault : Fault Injection
  • envoy.filters.http.buffer : 요청 전체를 Buffering.
  • envoy.filters.http.health_check : 특정 경로를 Health Check 응답으로 처리.
1.5.1.3. Router Filter

Router Filter는 Downstream HTTP Filter 의 마지막에 위치하는 Terminal Filter로, 요청을 실제 Upstream으로 보내는 역할을 담당한다. 앞의 모든 Filter를 통과한 요청에 대해, Route Config의 규칙에 따라 Target Cluster를 결정한다. 이후 Outlier Detection 정책을 통해서 Cluster에 소속되어 있는 Host 중에서 비정상 Host를 제외한다. 이후에는 남은 정상 Host 중에서 Load Balancing 정책에 따라 실제 Host를 선택하고, 해당 Host로의 연결을 통해 요청을 전달한다.

Router Filter는 다음과 같은 Load Balancing 정책을 제공한다.

  • envoy.load_balancing_policies.round_robin : Host를 순서대로 하나씩 선택이며, 가장 기본적인 정책.
  • envoy.load_balancing_policies.least_request : 활성 요청이 적은 Host 우선. 무작위 2개를 뽑아 비교하는 P2C 방식.
  • envoy.load_balancing_policies.random : 정상 Host 중 무작위 선택. 단순하고 가벼운 방식.
  • envoy.load_balancing_policies.ring_hash : 일관 해싱. 같은 키(Header/Cookie/IP 등)는 같은 Host로. 세션 어피니티용.
  • envoy.load_balancing_policies.maglev : Lookup Table 기반 일관 해싱. ring_hash보다 빠르고 균등하나 Host 변동 시 재배치가 다소 큼.
  • envoy.load_balancing_policies.client_side_weighted_round_robin : Host가 리포트한 부하 지표로 Weight를 동적 계산해 반영하는 Round Robin.
  • envoy.load_balancing_policies.wrr_locality : Locality(Zone) 간 분배를 Weight로 제어하고, Zone 내부 선택은 하위 정책에 위임하는 계층형 정책.

1.6. Upstream HTTP Filter

Upstream HTTP Filter는 Router Filter에 의해서 어느 Host로 Traffic을 전달할지 결정된 이후에 실행되는 Filter이다. Upstream HTTP Filter Instance는 매 재시도마다 Router Filter에 의해서 새로운 Instance가 생성되는 특징을 갖는다. Envoy에서 제공하는 Upstream HTTP Filter는 다음과 같다.

  • envoy.filters.http.header_mutation : 선택된 Upstream Host를 기준으로 Header를 추가/삭제/수정. 어느 Host로 Traffic이 전달되는지 정해진 뒤에 헤더를 조작해야 할 때 이용.
  • envoy.filters.http.lua : Upstream Context에서 Lua Script로 Custom Logic 수행.
  • envoy.filters.http.wasm : Upstream Context에서 WASM (WebAssembly) 기반 Custom Logic 수행.
  • envoy.filters.http.upstream_codec : Upstream HTTP Filter Chain의 마지막에 위치하는 Terminal Filter로, HTTP Connection Manager 내부의 HTTP Codec과 대칭적으로 Upstream 방향의 Encoding/Decoding을 담당한다. 생략시 자동으로 추가되는 특징을 갖는다.

1.7. Upstream Transport Socket

Upstream Transport Socket은 Downstream Transport Socket과 대칭적으로 Upstream 방향의 Transport Socket을 제공한다. TLS가 적용된 경우, Upstream Codec Filter로부터 전달받은 요청 Traffic을 암호화하여 Upstream으로 전송하고, 반대로 Upstream으로부터 받은 응답 Traffic을 복호화하여 Upstream Codec Filter로 올린다. TLS가 적용되지 않은 경우에는 Downstream과 동일하게 Raw Buffer Transport Socket이 Traffic을 변형 없이 그대로 전달한다.

2. Envoy Configuration

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
static_resources:

  listeners:
  # ── 1. Listener ──────────────────────────────────────────────
  - name: main_listener
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }

    # ── 2. Listener Filter Chain (names only) ───────────────────
    listener_filters:
    - name: envoy.filters.listener.original_dst         # Restores original destination (iptables REDIRECT)
    - name: envoy.filters.listener.proxy_protocol       # Parses the leading PROXY header
    - name: envoy.filters.listener.tls_inspector        # Peeks at ClientHello to extract SNI/ALPN
    - name: envoy.filters.listener.http_inspector       # Detects HTTP version (h1/h2)

    # ── 3. Filter Chain Manager ──────────────────────────────────
    # Selects one of the filter_chains below based on info extracted
    # by listener_filters (SNI, ALPN, etc.)
    filter_chains:

    # (a) Match by SNI (server_names) — most common case
    - filter_chain_match:
        server_names: ["example.com", "*.example.com"]

      # ── 4. Downstream Transport Socket (TLS termination) ──────
      transport_socket:
        name: envoy.transport_sockets.tls
        common_tls_context:
          tls_certificates:
          - certificate_chain: { filename: "/etc/envoy/cert.pem" }
            private_key:       { filename: "/etc/envoy/key.pem" }

      # ── 5. Network Filter Chain ──────────────────────────────
      filters:
      - name: envoy.filters.network.connection_limit
      - name: envoy.filters.network.rbac
      - name: envoy.filters.network.local_ratelimit

      # ── 6. HTTP Connection Manager (terminal filter of the network chain) ──
      - name: envoy.filters.network.http_connection_manager
        stat_prefix: ingress_http
        codec_type: AUTO                              # ── 7. HTTP Codec ──

        route_config:
          name: local_route
          virtual_hosts:
          - name: backend_vh
            domains: ["*"]
            routes:
            - match: { prefix: "/" }
              route: { cluster: backend }

        # ── 8. Downstream HTTP Filter ──────────────────────────
        http_filters:
        - name: envoy.filters.http.cors
        - name: envoy.filters.http.jwt_authn
        - name: envoy.filters.http.local_ratelimit
        - name: envoy.filters.http.fault
        - name: envoy.filters.http.compressor
        - name: envoy.filters.http.lua

        # ── 9. Router Filter (terminal filter of the downstream chain) ──
        - name: envoy.filters.http.router

    # (b) Match by ALPN — route h2 traffic to this chain
    - filter_chain_match:
        application_protocols: ["h2"]
      # (filters omitted — in practice, build a full network/http filter chain like (a))

  # ── 1b. Listener (second listener — plain HTTP, e.g. internal/health traffic) ──
  - name: internal_listener
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    # (filter_chains omitted — in practice, build a full network/http filter chain like (a))

  clusters:
  - name: backend
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: backend
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: httpbin.org, port_value: 443 }

    typed_extension_protocol_options:
      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
        explicit_http_config:
          http_protocol_options: {}

        # ── 10. Upstream HTTP Filter ──────────────────────────
        http_filters:
        - name: envoy.filters.http.header_mutation
        - name: envoy.filters.http.lua
        - name: envoy.filters.http.upstream_codec        # terminal

    # ── 11. Upstream Transport Socket (TLS origination) ────────
    transport_socket:
      name: envoy.transport_sockets.tls
      sni: httpbin.org
[Config 1] Envoy Configuration Example

[Config 1]은 1장에서 살펴본 구성 요소들이 실제 Envoy 설정의 어느 위치에 정의되는지 나타내는 예시이며, 주석의 번호는 [Figure 1]의 각 구성 요소에 대응한다. 분량상 각 Filter의 상세 설정(typed_config)은 생략하고 Filter 이름만 표기했다. Downstream 방향의 구성 요소(1-9)는 listeners 아래에, Upstream 방향의 구성 요소(10-11)는 clusters 아래에 정의된다는 것을 확인할 수 있다.

main_listener는 10000 Port에서 Downstream 연결을 수락하는 Listener이며 (1), listener_filters에는 1.2에서 설명한 권장 순서대로 Listener Filter Chain이 구성되어 있다 (2). Listener Filter가 추출한 SNI, ALPN 정보는 filter_chains에 정의된 각 Filter Chain의 filter_chain_match 조건과 비교되어 해당 연결을 처리할 Filter Chain 하나를 선택하는 데 사용되며, 이 선택이 Filter Chain Manager의 역할이다 (3). 예시에서는 (a) Chain이 SNI 기준으로, (b) Chain이 ALPN 기준으로 매칭된다.

선택된 Filter Chain의 transport_socket은 Downstream Transport Socket에 해당하며, TLS Transport Socket이 인증서와 함께 지정되어 있어 TLS Termination을 수행한 뒤 Plain Text를 Network Filter Chain으로 올린다 (4). filters에는 Network Filter Chain이 구성되며 (5), connection_limit, rbac, local_ratelimit 세 Non-terminal Filter가 L4 수준에서 연결을 검사한 뒤 Terminal Filter인 HTTP Connection Manager가 HTTP 처리를 담당한다 (6). HTTP Connection Manager의 codec_type은 HTTP Codec을 지정하는 설정으로, AUTO로 설정되어 있어 Downstream이 이용하는 HTTP Version에 맞는 Codec이 자동으로 선택된다 (7).

HTTP Connection Manager의 http_filters에는 Downstream HTTP Filter Chain이 구성되며 (8), 마지막에 위치하는 Router Filter가 route_config의 규칙에 따라 Target Cluster를 결정한다 (9). 예시의 route_config는 모든 Domain (*)의 모든 경로 (/) 요청을 backend Cluster로 라우팅하는 단순한 구성이며, backend Cluster의 lb_policy에 지정된 Load Balancing 정책에 따라 실제 Host가 선택된다.

Upstream 방향의 구성 요소는 Router Filter가 선택하는 Cluster의 정의에 위치한다. Upstream HTTP Filter Chain은 Cluster의 typed_extension_protocol_options 아래에 http_filters로 구성되며, 마지막의 upstream_codec Terminal Filter가 Upstream 방향의 Encoding/Decoding을 담당한다 (10). Cluster의 transport_socket은 Upstream Transport Socket에 해당하며, TLS Transport Socket이 지정되어 있어 Upstream으로 나가는 Traffic을 암호화하는 TLS Origination을 수행한다 (11). 이처럼 Downstream 방향은 Listener의 Transport Socket이 TLS를 종료하고 Upstream 방향은 Cluster의 Transport Socket이 TLS를 새로 시작하므로, 두 Transport Socket 사이의 Filter Chain들은 항상 Plain Text를 처리한다.

3. 참조