Envoy 部署類型

Envoy 網絡拓撲及請求流程

首先描述Envoy如何適合請求的請求路徑,而後描述內部請求從下游到達Envoy代理以後發生的內部事件c++

1. 術語

Envoy在其代碼庫和文檔中使用如下術語:算法

  • Cluster/(集羣):,集羣是指 Envoy 鏈接到的邏輯上相同的一組上游主機。 Envoy 經過服務發現 來發現集羣的成員。能夠經過 主動健康檢查來肯定集羣成員的健康狀態。 Envoy 經過負載均衡策略 決定將請求路由到哪一個集羣成員
  • Downstream/(下游): 下游主機鏈接到Envoy, 發送請求並接收響應。多是本地應用程序(在Sidecar模型中)或網絡節點 ,在非Sidecar模型中,這是一個遠程客戶端, 對於Envoy(服務端),至關於client .
  • Endpoints(端點):實現邏輯服務的網絡節點。它們在集羣中被分組。集羣中的端點位於Envoy代理的上游。
  • Filter(過濾器):鏈接或請求處理管道中的模塊,用於提供某些請求處理時的鏈接或請求處理的管道模塊。 相似與Unix系統中的管道符。
  • Filter chain: 相似 過濾器
  • Listeners/(監聽器): Envoy模塊,負責綁定到IP /端口, 能夠被下游客戶端鏈接。Envoy 暴露一個或者多個監聽器給下游主機鏈接。
  • Upstream/(上游):上游主機接收來自 Envoy 的鏈接和請求,並返回響應。這多是本地應用程序(在Sidecar模型中)或網絡節點。在非Sidecar模型中,這對應於Envoy來講,至關於服務端(server)。

2. 網絡拓撲

請求如何流經網絡(包括Envoy)中的組件取決於網絡的拓撲。Envoy可用於多種網絡拓撲中。數據庫

  1. Envoy最初是做爲Service Mesh Sidecar代理,從應用程序中排除了負載平衡,路由,可觀察性,安全性和發現服務。在Service Mesh模型中,請求數據流經Envoy做爲網絡的網關。請求經過入口或出口偵聽器到達Envoy:api

    • Ingress listeners 從Service Mesh中的其餘節點獲取請求,並將其轉發到本地應用程序。本地應用程序的響應經過Envoy流回到下游。安全

    • Egress listeners 從本地應用程序獲取請求,並將其轉發到網絡中的其餘節點。這些接收節點一般還將運行Envoy,並經過其入口偵聽器接受請求。服務器

  2. Service mesh外,Envoy還用於各類配置中。例如,它還能夠充當內部負載平衡器:網絡

  3. 做爲網絡邊緣上的入口/出口(ingress/egress proxy)代理:架構

  4. 在實際應用中,會混合使用上面的方法, 其中Envoy在service mesh(服務網格)中, 在邊緣網絡上做爲 內部負載均衡 使用。 請求路徑可能會遍歷多個Envoy併發

  5. Envoy能夠在多層拓撲中進行配置以實現可伸縮性可靠性,其中請求首先經過邊緣Envoy,而後再經過第二個Envoy層:負載均衡

在上述全部的網絡拓撲模型中,請求將從下游經過TCPUDPUnix域套接字到達特定的Envoy。 Envoy將經過TCPUDPUnix域套接字向上遊轉發請求。

下面重點介紹一個Envoy代理模式

3. 配置

Envoy是擴展性很強的平臺。 這致使可能的請求路徑組合不少,具體取決於:

  • L3 / 4層協議例如TCP,UDP,Unix域套接字
  • L7層協議,例如HTTP / 1,HTTP / 2,HTTP / 3,gRPC,Thrift,Dubbo,Kafka,Redis和各類數據庫。
  • 傳輸socket套接字,例如純文本,TLS,ALTS。
  • 鏈接路由,例如PROXY協議,原始目的地,動態轉發。
  • 身份驗證和受權
  • 斷路器和異常值檢測配置以及激活狀態。
  • 許多其餘配置,如用於網絡,HTTP,偵聽器,訪問日誌記錄,運行情況檢查,跟蹤和統計信息擴展等配置

如下示例主要使用到以下配置相關

  1. 一次具備TLS協議的鏈接上游和下游的HTTP/2的請求
  2. HTTP鏈接管理器 做爲惟一的網絡過濾器
  3. 假定一個客戶自定義過濾器和一個路由做爲 HTTP過濾器
  4. 文件系統訪問日誌記錄
  5. 具備靜態端點的單個羣集
  6. 統計接收器

爲了簡單起見,假定使用靜態引導程序配置文件:example.yml

# 靜態資源
static_resources:
  listeners:
  # 簡單的監聽器,綁定端口443
  - name: listener_https
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 443
    # A single listener filter exists for TLS inspector.
    listener_filters:
    - name: "envoy.filters.listener.tls_inspector"
      typed_config: {}
    # On the listener, there is a single filter chain that matches SNI for acme.com.
    filter_chains:
    - filter_chain_match:
        # This will match the SNI extracted by the TLS Inspector filter.
        server_names: ["acme.com"]
      # 下游 TLS 配置.
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificates:
            - certificate_chain: { filename: "certs/servercert.pem" }
              private_key: { filename: "certs/serverkey.pem" }
      filters:
      # HTTP鏈接管理器
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          use_remote_address: true
          http2_protocol_options:
            max_concurrent_streams: 100
          # 基於系統文件的 日誌
          access_log:
            - name: envoy.access_loggers.file
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                path: "/var/log/envoy/access.log"
          # 路由表,匹配本地服務的 /foo請求
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["acme.com"]
              routes:
              - match:
                  path: "/foo"
                route:
                  cluster: some_service
          # CustomFilter and the HTTP router filter are the HTTP filter chain.
          http_filters:
            # - name: some.customer.filter
            - name: envoy.filters.http.router
  clusters:
  - name: some_service
    connect_timeout: 5s
    # 上游 TLS 認證.
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
    load_assignment:
      cluster_name: some_service
      # 靜態端點分配(邏輯主機節點)
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 10.1.2.10
                port_value: 10002
        - endpoint:
            address:
              socket_address:
                address: 10.1.2.11
                port_value: 10002
    typed_extension_protocol_options:
      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
        "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
        explicit_http_config:
          http2_protocol_options:
            max_concurrent_streams: 100
  - name: some_statsd_sink
    connect_timeout: 5s
    # The rest of the configuration for statsd sink cluster.
# statsd sink.
stats_sinks:
  - name: envoy.stat_sinks.statsd
    typed_config:
      "@type": type.googleapis.com/envoy.config.metrics.v3.StatsdSink
      tcp_cluster_name: some_statsd_sink

4. 更高層的架構

Envoy中的請求處理路徑包括兩個主要部分:

  1. Listener子系統,用於處理下游請求。它還負責管理下游請求生命週期以及到客戶端的響應路徑。下游的HTTP/2請求位於此模塊中
  2. Cluster子系統,負責選擇和配置與端點的上游鏈接。能夠今後處瞭解羣集和端點健康情況負載均衡鏈接池的地方。上游HTTP / 2請求處理位於此模塊

這兩個子系統與HTTP路由器過濾器橋接,該過濾器將HTTP請求從下游轉發到上游。

Envoy還具備基於事件的線程模型。主線程負責服務器的生命週期,配置處理,狀態等,而且一些工做線程負責處理請求。全部線程都圍繞事件循環運行,當監聽器接收到一個鏈接請求時,該鏈接將其生命週期綁定到一個單獨的 worker 線程。worker 線程則負責執行監聽、過濾和轉發。每一個工做線程都維護本身的與上游端點的TCP鏈接池。UDP過濾器狀態爲給定的工做線程共享,過濾器負責根據須要提供會話語義。

worker線程不多共享狀態,而且不多並行運行。該線程模型能夠擴展到核心數量很是多的CPU。

5. 請求流程

使用上面的示例配置簡要概述了請求和響應的生命週期:

  1. workder線程上運行的Envoy Listener接受來自下游的一個TCP鏈接。
  2. 監聽過濾器建立並運行,將會提供 SNI 和其餘 TLS欲配置項。一旦完成後,偵聽器將匹配網絡過濾器鏈。每一個偵聽器可能具備多個過濾器鏈,這些過濾器鏈在目標IP CIDR範圍,SNI,ALPN,源端口等的某種組合上匹配。傳輸套接字(在咱們的示例中爲TLS傳輸套接字)與此過濾器鏈相關聯。
  3. 在網絡讀取時,TLS傳輸套接字將從TCP鏈接讀取的數據流並進行解密,以進行進一步處理。
  4. 網絡過濾器鏈已建立並運行。 HTTP最重要的過濾器是HTTP鏈接管理器,它是鏈中的最後一個網絡過濾器。
  5. **HTTP connection manager **中的HTTP / 2編解碼器 從TLS鏈接獲取到多個獨立的數據流進行解密。每一個流只處理一個請求和響應。
  6. 對於每一個HTTP流,會有一個HTTP過濾器建立和運行。該請求首先經過CustomFilter(能夠讀取和修改該請求),最重要的HTTP過濾器是位於HTTP過濾器尾部的路由器過濾器。在路由器過濾器上調用解碼時,將選擇路由並選擇集羣。數據中請求頭將轉發到該羣集中的上游端點。路由器過濾器從羣集管理器中爲匹配的羣集獲取HTTP鏈接池,以執行此操做。
  7. 集羣中具體的負載均衡 被執行用以查找一個端點。羣集的斷路器被檢查,以肯定是否容許新的數據流。若是端點的鏈接池爲空或容量不足,一個新的鏈接(端點)被建立。
  8. 上游端點鏈接器HTTP/2的解碼器將請求的流與經過單個TCP鏈接流向上游的任何其餘流進行多路複用和幀化。
  9. 上游端點鏈接的TLS傳輸套接字加密這些字節並將它們寫入上游鏈接的TCP套接字。
  10. 包含請求頭,請求正文和尾部組成的 Request 在上游被代理,Response在下游被代理。響應以與請求相反的順序經過HTTP過濾器,從路由器過濾器開始並經過CustomFilter,而後再發送到下游。
1. Listener TCP 接收

如圖

ListenerManager負責獲取偵聽器的配置,並實例化綁定到其各自IP /端口的多個偵聽器實例上。偵聽器可能處於如下三種狀態之一:

  • Warming: 偵聽器正在等待配置依賴項(如外部配置,動態機密)。偵聽器還沒有準備好接受TCP鏈接。
  • Active: 偵聽器已綁定到其IP /端口並接受TCP鏈接。
  • Draining: 監聽已存在的 TCP連接持續一段時間, 再也不接受新的TCP連接

每一個工做線程爲每一個配置的偵聽器維護本身的偵聽器實例。每一個偵聽器均可以經過SO_REUSEPORT綁定到同一端口,或者共享一個綁定到該端口的套接字。當新的TCP鏈接到達時,內核將決定哪一個工做線程將接受該鏈接,而且該工做線程的偵聽器將調用其Server :: ConnectionHandlerImpl :: ActiveTcpListener :: onAccept()回調。

2. 偵聽器過濾器鏈和網絡過濾器鏈匹配

而後,工做線程的偵聽器將建立並運行偵聽器過濾器鏈。過濾器鏈是經過應用每一個過濾器的過濾器工廠而建立的。工廠知道過濾器的配置,併爲每一個鏈接或流建立一個新的過濾器實例。

3.TLS傳輸套接字解密

Envoy經過TransportSocket擴展接口提供可插拔的運輸插座。傳輸套接字遵循TCP鏈接的生命週期事件,並讀寫網絡緩衝區。運輸套接字必須實現的一些關鍵方法是:

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;

當TCP鏈接上有可用數據時,Network :: ConnectionImpl :: onReadReady()會經過SslSocket :: doRead()調用TLS傳輸套接字。而後,傳輸套接字在TCP鏈接上執行TLS握手。握手完成後,SslSocket :: doRead()將解密的字節流提供給Network :: FilterManagerImpl的實例,該實例負責管理網絡過濾器鏈。

須要特別注意的是,不管是TLS握手仍是過濾器管道暫停,都沒法阻止任何操做。因爲Envoy是基於事件的,所以任何須要額外數據處理的狀況都將致使事件的儘早完成,並使CPU產生另外一個事件。當網絡使更多數據可供讀取時,讀取事件將觸發TLS握手的恢復。

4.網絡過濾器鏈處理

與偵聽器過濾器鏈同樣,Envoy將經過Network :: FilterManagerImpl實例化其過濾器工廠中的一系列網絡過濾器。該實例對於每一個新鏈接都是新鮮的。網絡過濾器(如傳輸套接字)跟隨TCP生命週期事件,並隨着數據從傳輸套接字可用而被調用。

網絡過濾器是做爲管道組成的,與每一個鏈接一個的傳輸套接字不一樣。網絡過濾器分爲三種:

  • ReadFilter 實現onData(),當鏈接中有數據可用時(因爲某些請求)而調用。
  • **WriteFilter **實現onWrite(),在即將將數據寫入鏈接時(因爲某些響應)而調用。
  • Filter 同時實現ReadFilter和WriteFilter。
5. HTTP/2 codec 解碼

Envoy中的HTTP / 2編解碼器基於nghttp2。

6. HTTP filter 鏈處理

對於每一個HTTP流,HCM都按照上面爲偵聽器和網絡過濾器鏈創建的模式實例化HTTP過濾器鏈。

HTTP過濾器接口共有三種:

  1. StreamDecoderFilter 帶有用於請求處理的回調。
  2. StreamEncoderFilter 帶有用於響應處理的回調。
  3. StreamFilter 同時實現 StreamDecoderFilter 和StreamEncoderFilter.

請求路徑將以下所示:

響應路徑以下所示:

調用路由器過濾器時,路由將最終肯定。所選路由的配置將指向上游羣集名稱。而後,路由器過濾器向ClusterManager詢問羣集的HTTP鏈接池。

7. 負載均衡

負載平衡是一種在單個上游羣集內的多個主機之間分配流量以有效利用可用資源的方法。有許多不一樣的方法能夠完成此任務,所以Envoy提供了幾種不一樣的負載平衡策略。從高層次上講,咱們能夠將這些策略分爲兩類:全局負載平衡和分佈式負載平衡。

分佈式負載平衡是指 讓Envoy本身基於瞭解上游主機的位置來肯定應如何將負載分配到端點。

例子:

  • Active health checking: 經過對上游主機進行情況檢查,Envoy能夠調整優先級和位置的權重以解決不可用主機的問題。
  • Zone aware routing: 可以使Envoy選擇使用更近的端點,而沒必要在控制平面中顯式配置優先級。
  • Load balancing algorithms:Envoy可使用幾種不一樣的算法來使用提供的權重來肯定要選擇的主機

每一個集羣都有一個負載均衡器,當新請求到達時,該負載均衡器會選擇一個端點。Envoy支持多種負載平衡算法,例如加權輪循,磁懸浮列車,負荷最小,隨機。

負載平衡器從靜態引導程序配置,DNS,動態xDS(CDS和EDS發現服務)以及主動/被動運行情況檢查的組合中得到有效分配。

選擇端點後,將使用該端點的鏈接池來查找用於轉發請求的鏈接。若是不存在與主機的鏈接,或者全部鏈接均處於其最大併發流限制,則除非鏈接集羣最大鏈接的斷路器跳閘,不然將創建新鏈接並將其放置在鏈接池中。若是配置並達到了鏈接的最大生存期流限制,則會在池中分配一個新的鏈接,而且耗盡受影響的HTTP / 2鏈接。

8. HTTP/2 codec 編碼

所選鏈接的HTTP / 2編解器將請求流與單個TCP鏈接流向同一上游的任何其餘流進行多路複用。這與HTTP / 2編解碼器解碼相反。

與下游HTTP / 2解碼器同樣,上游編解碼器負責獲取Envoy對HTTP的標準抽象,即在單個鏈接上與請求/響應標頭/正文/尾部複用的多個流,並經過生成一系列HTTP / 2幀將其映射到HTTP / 2

9. TLS transport socket 加密

上游端點鏈接的TLS傳輸套接字對HTTP / 2編解碼器輸出中的字節進行加密,並將其寫入用於上游鏈接的TCP套接字。與TLS傳輸套接字解密同樣,示例中,羣集配置了提供TLS傳輸安全性的傳輸套接字。上游和下游傳輸套接字擴展存在相同的接口。

10. 響應路徑和HTTP生命週期

Reuest 在上游被代理,Response在下游被代理。響應以相反的順序經過HTTP和網絡過濾器。從請求。

解碼器/編碼器請求生命週期事件的各類回調將在HTTP過濾器中調用,例如當響應預告片被轉發或請求主體被流式傳輸時。一樣,當請求期間數據繼續在兩個方向上流動時,讀/寫網絡過濾器也將調用其各自的回調。

請求有可能提早終止。這多是因爲(但不限於):

  1. 請求超時。
  2. 上游端點重啓。
  3. HTTP篩選器流重置。
  4. 熔斷
  5. 上游資源不可用,例如缺乏路線的羣集。
  6. 沒有健康的端點
  7. DoS保護。
  8. HTTP協議違規。
  9. 來自HCM或HTTP過濾器的本地回覆。例如。速率限制HTTP過濾器返回429響應。

若是發生這些狀況中的任何一個,Envoy內部可能會發送生成的響應(若是還沒有發送上游響應頭),或者將流重置(若是響應頭已經轉發至下游)。

11.Post-request 處理

請求完成後,數據流將被銷燬。還會發生如下狀況:

  • 請求後統計信息將進行更新(例如,計時,活動請求,升級,運行情況檢查)。可是,在請求處理期間,某些統計信息會更早更新。此時,統計信息還沒有寫入統計信息接收器,它們由主線程按期進行批處理和寫入。在咱們的示例中,這是一個statsd接收器。
  • 訪問日誌將寫入訪問日誌接收器。示例中,這是一個文件訪問日誌。
相關文章
相關標籤/搜索