自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Envoy 基礎(chǔ)入門教程,看這一篇就夠了

開發(fā) 架構(gòu)
Envoy 是一個獨立的進(jìn)程,設(shè)計為伴隨每個應(yīng)用程序服務(wù)一起運行。所有 Envoy 實例形成一個透明的通信網(wǎng)格,每個應(yīng)用程序通過 localhost 發(fā)送和接收消息,不需要知道網(wǎng)絡(luò)拓?fù)?。對服?wù)的實現(xiàn)語言也完全無感知,這種模式也被稱為 Sidecar 模式。

Envoy 是一個用 C++ 開發(fā)的高性能代理,Envoy 是一種 L7 代理和通信總線,專為大型的現(xiàn)代面向服務(wù)的架構(gòu)而設(shè)計。

核心能力

Envoy 的誕生源于以下理念:

網(wǎng)絡(luò)對于應(yīng)用程序來說應(yīng)該是透明的,當(dāng)網(wǎng)絡(luò)和應(yīng)用程序出現(xiàn)問題時,應(yīng)該很容易確定問題的源頭。

當(dāng)然要實現(xiàn)上述目標(biāo)是非常困難的。Envoy 試圖通過提供以下高級功能來實現(xiàn)這一目標(biāo):

非侵入架構(gòu): Envoy 是一個獨立的進(jìn)程,設(shè)計為伴隨每個應(yīng)用程序服務(wù)一起運行。所有 Envoy 實例形成一個透明的通信網(wǎng)格,每個應(yīng)用程序通過 localhost 發(fā)送和接收消息,不需要知道網(wǎng)絡(luò)拓?fù)?。對服?wù)的實現(xiàn)語言也完全無感知,這種模式也被稱為 Sidecar 模式。

L3/L4 過濾器架構(gòu): Envoy 的核心是一個 L3/L4 層的網(wǎng)絡(luò)代理。可插拔的過濾器鏈機制允許編寫不同的 TCP/UDP 代理任務(wù)的過濾器,并將其插入到主服務(wù)器中。而且已經(jīng)內(nèi)置支持了各種任務(wù)的過濾器,例如原始 TCP 代理、UDP 代理、HTTP 代理、TLS 客戶端證書身份驗證、Redis、MongoDB、Postgres 等。

HTTP L7 過濾器架構(gòu): HTTP 是現(xiàn)代應(yīng)用程序架構(gòu)的關(guān)鍵組件,因此 Envoy 支持了一個額外的 HTTP L7 過濾器層。HTTP 過濾器可以被插入到 HTTP 連接管理子系統(tǒng)中,執(zhí)行不同的任務(wù),如緩存、速率限制、路由/轉(zhuǎn)發(fā)、嗅探 Amazon 的 DynamoDB 等。

頂級的 HTTP/2 支持: 在 HTTP 模式下運行時,Envoy 同時支持 HTTP/1.1 和 HTTP/2。Envoy 可以作為透明的 HTTP/1.1 到 HTTP/2 雙向代理運行。這意味著可以連接任何組合的 HTTP/1.1 和 HTTP/2 客戶端與目標(biāo)服務(wù)器。推薦的服務(wù)到服務(wù)配置在所有 Envoy 之間使用 HTTP/2 創(chuàng)建持久連接網(wǎng)格,請求和響應(yīng)可以在該連接上進(jìn)行多路復(fù)用。

HTTP/3 支持(目前處于 alpha 版): 從 Envoy 1.19.0 版本開始,Envoy 現(xiàn)在支持上游和下游的 HTTP/3,而且可以在任何方向上進(jìn)行 HTTP/1.1、HTTP/2 和 HTTP/3 之間的轉(zhuǎn)換。

HTTP L7 路由: 在 HTTP 模式下運行時,Envoy 支持路由子系統(tǒng),該子系統(tǒng)能夠根據(jù)路徑、權(quán)限、內(nèi)容類型、運行時值等路由和重定向請求。在使用 Envoy 作為前端/邊緣代理時,此功能非常有用,但在構(gòu)建服務(wù)到服務(wù)的網(wǎng)格時也可以利用它。

gRPC 支持: gRPC 是 Google 的一個 RPC 框架,使用 HTTP/2 或更高版本作為底層多路復(fù)用傳輸。Envoy 支持用作 gRPC 請求和響應(yīng)的路由和負(fù)載均衡基礎(chǔ)所需的所有 HTTP/2 功能,這兩個系統(tǒng)非常互補。

服務(wù)發(fā)現(xiàn)和動態(tài)配置: Envoy 可以選擇使用一組分層的動態(tài)配置 API 來進(jìn)行集中管理。這些層向 Envoy 提供了關(guān)于后端集群中的主機、后端集群自身、HTTP 路由、監(jiān)聽套接字和加密材料的動態(tài)更新。對于更簡單的部署,可以通過 DNS 解析(甚至完全跳過)來完成后端主機發(fā)現(xiàn),并且進(jìn)一步的層可以由靜態(tài)配置文件替代。

健康檢查: 構(gòu)建 Envoy 網(wǎng)格的推薦方法是將服務(wù)發(fā)現(xiàn)視為最終一致的過程。Envoy 包含一個健康檢查子系統(tǒng),可以選擇對上游服務(wù)集群執(zhí)行主動健康檢查。然后,Envoy 使用服務(wù)發(fā)現(xiàn)和健康檢查信息的結(jié)合來確定健康的負(fù)載均衡目標(biāo)。Envoy 還通過異常值檢測子系統(tǒng)支持被動健康檢查。

高級負(fù)載均衡: 分布式系統(tǒng)中不同組件之間的負(fù)載均衡是一個復(fù)雜的問題。由于 Envoy 是一個獨立的代理而不是庫,因此可以獨立實現(xiàn)高級負(fù)載均衡以供任何應(yīng)用程序訪問。目前 Envoy 支持自動重試、熔斷、通過外部速率限制服務(wù)進(jìn)行全局速率限制、異常檢測等。

前端/邊緣代理支持: 在邊緣使用相同的軟件有很大的好處(可觀察性、管理、相同的服務(wù)發(fā)現(xiàn)和負(fù)載均衡算法等)。Envoy 的功能集使其非常適合作為大多數(shù)現(xiàn)代 Web 應(yīng)用程序用例的邊緣代理。這包括 TLS 終止、HTTP/1.1、HTTP/2 和 HTTP/3 支持以及 HTTP L7 路由。

最佳的可觀測性: 如上所述,Envoy 的主要目標(biāo)是使網(wǎng)絡(luò)透明化。但是,問題在網(wǎng)絡(luò)層面和應(yīng)用層面都可能會出現(xiàn)。Envoy 為所有子系統(tǒng)提供了強大的統(tǒng)計支持。目前支持的統(tǒng)計數(shù)據(jù)輸出端是 statsd(以及兼容的提供程序),但是接入其他不同的統(tǒng)計數(shù)據(jù)輸出端并不困難。統(tǒng)計數(shù)據(jù)也可以通過管理端口進(jìn)行查看,Envoy 還支持通過第三方提供者進(jìn)行分布式跟蹤。

常用術(shù)語

在我們介紹 Envoy 架構(gòu)之前,有必要先介紹一些常用的術(shù)語定義,因為這些術(shù)語貫穿整個 Envoy 的架構(gòu)設(shè)計。

  • Host(主機): 能夠進(jìn)行網(wǎng)絡(luò)通信的實體(手機、服務(wù)器等上的應(yīng)用程序)。在 Envoy 中主機是邏輯網(wǎng)絡(luò)應(yīng)用程序。一個物理硬件可能運行多個主機,只要每個主機可以獨立進(jìn)行尋址。
  • Downstream(下游): 下游主機連接到 Envoy,發(fā)送請求并接收響應(yīng)。
  • Upstream(上游): 上游主機接收來自 Envoy 的連接和請求并返回響應(yīng)。
  • Listener(偵聽器): 偵聽器是一個帶有名稱的網(wǎng)絡(luò)位置(例如端口、unix domain socket 等),下游客戶端可以連接到該位置。Envoy 暴露一個或多個監(jiān)聽器,供下游主機連接。
  • Cluster(集群): 一個集群是一組邏輯上相似的上游主機,Envoy 連接到這些主機。Envoy 通過服務(wù)發(fā)現(xiàn)來發(fā)現(xiàn)集群的成員。它還可以通過主動健康檢查來確定集群成員的健康狀況。Envoy 根據(jù)負(fù)載均衡策略確定將請求路由到哪個集群成員。
  • Mesh(網(wǎng)格): 一組主機協(xié)同工作,提供一致的網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)。在這里 Envoy Mesh 是指一組 Envoy 代理,它們構(gòu)成了由多種不同服務(wù)和應(yīng)用程序平臺組成的分布式系統(tǒng)的消息傳遞基礎(chǔ)。
  • Runtime configuration(運行時配置): 與 Envoy 一起部署的實時配置系統(tǒng)??梢愿呐渲迷O(shè)置,影響操作而無需重新啟動 Envoy 或更改主要配置。

架構(gòu)設(shè)計

Envoy 采用單進(jìn)程多線程架構(gòu)。

一個獨立的 primary 線程負(fù)責(zé)控制各種零散的協(xié)調(diào)任務(wù),而一些 worker 線程則負(fù)責(zé)執(zhí)行監(jiān)聽、過濾和轉(zhuǎn)發(fā)任務(wù)。

一旦偵聽器接受連接,該連接就會將其生命周期綁定到一個單獨的 worker 線程。這使得 Envoy 的大部分工作基本上是單線程來處理的,只有少量更復(fù)雜的代碼處理工作線程之間的協(xié)調(diào)。

通常情況下 Envoy 實現(xiàn)了 100% 非阻塞。對于大多數(shù)工作負(fù)載,我們建議將 worker 線程的數(shù)量配置為機器上的硬件線程數(shù)量。

Envoy 整體架構(gòu)如下圖所示:

Envoy 架構(gòu)

Envoy 進(jìn)程中運行著一系列 Inbound/Outbound 監(jiān)聽器(Listener),Inbound 代理入站流量,Outbound 代理出站流量。Listener 的核心就是過濾器鏈(FilterChain),鏈中每個過濾器都能夠控制流量的處理流程。

Envoy 接收到請求后,會先走 FilterChain,通過各種 L3/L4/L7 Filter 對請求進(jìn)行處理,然后再路由到指定的集群,并通過負(fù)載均衡獲取一個目標(biāo)地址,最后再轉(zhuǎn)發(fā)出去。

其中每一個環(huán)節(jié)可以靜態(tài)配置,也可以動態(tài)服務(wù)發(fā)現(xiàn),也就是所謂的 xDS,這里的 x 是一個代詞,是 lds、rds、cds、eds、sds 的總稱,即服務(wù)發(fā)現(xiàn),后 2 個字母 ds 就是 discovery service。

第一個 Envoy 代理

下面我們通過一個簡單的示例來介紹 Envoy 的基本使用。

配置

Envoy 使用 YAML 文件來控制代理的行為,整體配置結(jié)構(gòu)如下:

listen -- 監(jiān)聽器
    1.我監(jiān)聽的地址
    2.過濾鏈
        filter1
            路由: 轉(zhuǎn)發(fā)到哪里
                virtual_hosts
                    只轉(zhuǎn)發(fā)什么
                    轉(zhuǎn)發(fā)到哪里 --> 由后面的 cluster 來定義
        filter2
        filter3
        # envoyproxy.io/docs/envoy/v1.28.0/api-v3/config/filter/filter
cluster
    轉(zhuǎn)發(fā)規(guī)則
    endpoints
        --指定了我的后端地址

接下來我們就來創(chuàng)建一個簡單的 Envoy 代理,它監(jiān)聽 10000 端口,將請求轉(zhuǎn)發(fā)到 www.baidu.com 的 80 端口。在下面的步驟中,我們將使用靜態(tài)配置接口來構(gòu)建配置,也意味著所有設(shè)置都是預(yù)定義在配置文件中的。此外 Envoy 也支持動態(tài)配置,這樣可以通過外部一些源來自動發(fā)現(xiàn)進(jìn)行設(shè)置。

Envoy 代理使用開源 xDS API 來交換信息,目前 xDS v2 已被廢棄,最新版本的 Envoy 不再支持 xDS v2,建議使用 xDS v3。

創(chuàng)建一個名為 envoy-1.yaml 的文件,在 Envoy 配置的第一行定義正在使用的接口配置,在這里我們將配置靜態(tài) API,因此第一行應(yīng)為 static_resources:

static_resources:

然后需要在靜態(tài)配置下面定義 Envoy 的監(jiān)聽器(Listener),監(jiān)聽器是 Envoy 監(jiān)聽請求的網(wǎng)絡(luò)配置,例如 IP 地址和端口。我們這里設(shè)置監(jiān)聽 IP 地址為 0.0.0.0,并在端口 10000 上進(jìn)行監(jiān)聽。對應(yīng)的監(jiān)聽器的配置為

static_resources:
  listeners:
    - name: listener_0 # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 10000 # 監(jiān)聽器的端口

通過 Envoy 監(jiān)聽傳入的流量,下一步是定義如何處理這些請求。每個監(jiān)聽器都有一組過濾器,并且不同的監(jiān)聽器可以具有一組不同的過濾器。

在我們這個示例中,我們將所有流量代理到 baidu.com,配置完成后我們應(yīng)該能夠通過請求 Envoy 的端點就可以直接看到百度的主頁了,而無需更改 URL 地址。

過濾器是通過 filter_chains 來定義的,每個過濾器的目的是找到傳入請求的匹配項,以使其與目標(biāo)地址進(jìn)行匹配。Filter 過濾器的寫法如下所示:

name: 指定使用哪個過濾器
typed_config:
  "@type": type.googleapis.com/envoy.過濾器的具體值
  參數(shù)1:值1
  參數(shù)2:值2
  。。。

這里選擇什么參數(shù),要看name里選擇的什么參數(shù)要根據(jù)所選擇的過濾器來判定

和 http 相關(guān)的,一般選擇 HTTP connection manager。

在 https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/filter/filter 里找參數(shù)

name 的位置應(yīng)該寫 envoy.filters.network.http_connection_manager

@type 的值到文檔里找具體的值

比如我們這里的配置如下所示:

static_resources:
  listeners:
    - name: listener_0 # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 10000 # 監(jiān)聽器的端口

      filter_chains: # 配置過濾器鏈
        # 在此地址收到的任何請求都會通過這一系列過濾鏈發(fā)送。
        - filters:
            # 指定要使用哪個過濾器,下面是envoy內(nèi)置的網(wǎng)絡(luò)過濾器,如果請求是 HTTP 它將通過此 HTTP 過濾器
            # 該過濾器將原始字節(jié)轉(zhuǎn)換為HTTP級別的消息和事件(例如接收到的header、接收到的正文數(shù)據(jù)等)
            # 它還處理所有HTTP連接和請求中常見的功能,例如訪問日志記錄、請求ID生成和跟蹤、請求/響應(yīng)頭操作、路由表管理和統(tǒng)計信息。
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                # 需要配置下面的類型,啟用 http_connection_manager
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: ingress_http
                access_log: # 連接管理器發(fā)出的 HTTP 訪問日志的配置
                  - name: envoy.access_loggers.stdout # 輸出到stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定義http過濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"] # 要匹配的主機名列表,*表示匹配所有主機
                      routes:
                        - match:
                            prefix: "/" # 要匹配的 URL 前綴
                          route: # 路由規(guī)則,發(fā)送請求到 service_baidu 集群
                            host_rewrite_literal: www.baidu.com # 更改 HTTP 請求的入站 Host 頭信息
                            cluster: service_baidu # 將要處理請求的集群名稱,下面會有相應(yīng)的實現(xiàn)

這里我們使用的過濾器使用了 envoy.filters.network.http_connection_manager,這是為 HTTP 連接設(shè)計的一個內(nèi)置過濾器,該過濾器將原始字節(jié)轉(zhuǎn)換為 HTTP 級別的消息和事件(例如接收到的 header、接收到的正文數(shù)據(jù)等),它還處理所有 HTTP 連接和請求中常見的功能,例如訪問日志記錄、請求 ID 生成和跟蹤、請求/響應(yīng)頭操作、路由表管理和統(tǒng)計信息。

  • stat_prefix:為連接管理器發(fā)出統(tǒng)計信息時使用的一個前綴。
  • route_config:路由配置,如果虛擬主機匹配上了則檢查路由。在我們這里的配置中,無論請求的主機域名是什么,route_config 都匹配所有傳入的 HTTP 請求。
  • routes:如果 URL 前綴匹配,則一組路由規(guī)則定義了下一步將發(fā)生的狀況。/ 表示匹配根路由。
  • host_rewrite_literal:更改 HTTP 請求的入站 Host 頭信息。
  • cluster: 將要處理請求的集群名稱,下面會有相應(yīng)的實現(xiàn)。
  • http_filters: 該過濾器允許 Envoy 在處理請求時去適應(yīng)和修改請求。

當(dāng)請求于過濾器匹配時,該請求將會傳遞到集群。下面的配置就是將主機定義為訪問 HTTPS 的 baidu.com 域名,如果定義了多個主機,則 Envoy 將執(zhí)行輪詢(Round Robin)策略。配置如下所示:

clusters:
  - name: service_baidu # 集群的名稱,與上面的 router 中的 cluster 對應(yīng)
    type: LOGICAL_DNS # 用于解析集群(生成集群端點)時使用的服務(wù)發(fā)現(xiàn)類型,可用值有STATIC、STRICT_DNS 、LOGICAL_DNS、ORIGINAL_DST和EDS等;
    connect_timeout: 0.25s
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN # 負(fù)載均衡算法,支持ROUND_ROBIN、LEAST_REQUEST、RING_HASH、RANDOM、MAGLEV和CLUSTER_PROVIDED;
    load_assignment: # 以前的 v2 版本的 hosts 字段廢棄了,現(xiàn)在使用 load_assignment 來定義集群的成員,指定 STATIC、STRICT_DNS 或 LOGICAL_DNS 集群的成員需要設(shè)置此項。
      cluster_name: service_baidu # 集群的名稱
      endpoints: # 需要進(jìn)行負(fù)載均衡的端點列表
        - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: www.baidu.com
                    port_value: 443
    transport_socket: # 用于與上游集群通信的傳輸層配置
      name: envoy.transport_sockets.tls # tls 傳輸層
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        sni: www.baidu.com

最后,還需要配置一個管理模塊:

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

上面的配置定義了 Envoy 的靜態(tài)配置模板,監(jiān)聽器定義了 Envoy 的端口和 IP 地址,監(jiān)聽器具有一組過濾器來匹配傳入的請求,匹配請求后,將請求轉(zhuǎn)發(fā)到集群,完整的配置如下所示:

# envoy-1.yaml
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  listeners:
    - name: listener_0 # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 10000 # 監(jiān)聽器的端口

      filter_chains: # 配置過濾器鏈
        - filters:
            # 過濾器配置的名稱,要填寫 typed_config 配置的過濾器指定的名稱
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                # 啟用 http_connection_manager
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: ingress_http
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定義http過濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            host_rewrite_literal: www.baidu.com
                            cluster: service_baidu

  clusters:
    - name: service_baidu # 集群的名稱
      type: LOGICAL_DNS # 用于解析集群(生成集群端點)時使用的服務(wù)發(fā)現(xiàn)類型,可用值有STATIC、STRICT_DNS 、LOGICAL_DNS、ORIGINAL_DST和EDS等;
      connect_timeout: 0.25s
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN # 負(fù)載均衡算法,支持ROUND_ROBIN、LEAST_REQUEST、RING_HASH、RANDOM、MAGLEV和CLUSTER_PROVIDED;
      load_assignment:
        cluster_name: service_baidu
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: www.baidu.com
                      port_value: 443
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
          sni: www.baidu.com

第一次使用 Envoy,可能會覺得它的配置太復(fù)雜了,讓人眼花繚亂。其實只要我們理解了網(wǎng)絡(luò)代理程序的流程就不難配置了,比如作為一個代理,首先要能獲取請求流量,通常是采用監(jiān)聽端口的方式實現(xiàn);其次拿到請求數(shù)據(jù)后需要對其做微處理,例如附加 Header 或校驗?zāi)硞€ Header 字段的內(nèi)容等,這里針對來源數(shù)據(jù)的層次不同,可以分為 L3/L4/L7,然后將請求轉(zhuǎn)發(fā)出去;轉(zhuǎn)發(fā)這里又可以衍生出如果后端是一個集群,需要從中挑選一臺機器,如何挑選又涉及到負(fù)載均衡等。

腦補完大致流程后,再來看 Envoy 是如何組織配置信息的,我們再來解釋一下其中的關(guān)鍵字段。

  • listener: Envoy 的監(jiān)聽地址,就是真正干活的。Envoy 會暴露一個或多個 Listener 來監(jiān)聽客戶端的請求。
  • filter: 過濾器,在 Envoy 中指的是一些可插拔和可組合的邏輯處理層,是 Envoy 核心邏輯處理單元。
  • route_config: 路由規(guī)則配置,即將請求路由到后端的哪個集群。
  • cluster: 服務(wù)提供方集群,Envoy 通過服務(wù)發(fā)現(xiàn)定位集群成員并獲取服務(wù),具體路由到哪個集群成員由負(fù)載均衡策略決定。

結(jié)合關(guān)鍵字段和上面的腦補流程,可以看出 Envoy 的大致處理流程如下:

Envoy 配置流程

Envoy 內(nèi)部對請求的處理流程其實跟我們上面腦補的流程大致相同,即對請求的處理流程基本是不變的,而對于變化的部分,即對請求數(shù)據(jù)的微處理,全部抽象為 Filter,例如對請求的讀寫是 ReadFilter、WriteFilter,對 HTTP 請求數(shù)據(jù)的編解碼是 StreamEncoderFilter、StreamDecoderFilter,對 TCP 的處理是 TcpProxyFilter,其繼承自 ReadFilter,對 HTTP 的處理是 ConnectionManager,其也是繼承自 ReadFilter 等等,各個 Filter 最終會組織成一個 FilterChain,在收到請求后首先走 FilterChain,其次路由到指定集群并做負(fù)載均衡獲取一個目標(biāo)地址,然后轉(zhuǎn)發(fā)出去。

啟動 Envoy

配置完成后,我們就可以去啟動 Envoy 了,首先當(dāng)然需要去安裝 Envoy 了,因為 Envoy 是 C++ 開發(fā)的,編譯起來非常麻煩,如果是 Mac 用戶可以使用 brew install envoy 來一鍵安裝,但是最簡單的方式還是使用 Docker 來啟動 Envoy。

我們這里也通過 Docker 容器來啟動 Envoy,將上面的配置文件通過 Volume 掛載到容器中的 /etc/envoy/envoy.yaml 去。

然后使用以下命令啟動綁定到端口 80 的 Envoy 容器:

$ docker run --name=envoy -d \
  -p 80:10000 \
  -v $(pwd)/manifests/2.Envoy/envoy-1.yaml:/etc/envoy/envoy.yaml \
  envoyproxy/envoy:v1.28.0

啟動后,我們可以在本地的 80 端口上去訪問應(yīng)用 curl localhost 來測試代理是否成功。同樣我們也可以通過在本地瀏覽器中訪問 localhost 來查看:

localhost

可以看到請求被代理到了 baidu.com,而且應(yīng)該也可以看到 URL 地址沒有變化,還是 localhost,查看 Envoy 日志可以看到如下信息:

[2023-10-25T06:53:50.003Z] "GET / HTTP/1.1" 200 - 0 103079 399 235 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36" "e081fa5b-31a4-4285-92d9-b8a8c896f2d4" "www.baidu.com" "110.242.68.3:443"
[2023-10-25T06:53:50.819Z] "GET /sugrec?&prod=pc_his&from=pc_web&jsnotallow=1&sid=&hisdata=%5B%7B%22time%22%3A1698206660%2C%22kw%22%3A%22envovy%20typed_config%22%2C%22fq%22%3A2%7D%5D&_t=1698216830777&req=2&csor=0 HTTP/1.1" 200 - 0 155 57 57 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36" "9a9351e7-e7ef-4fa8-9aec-fba96600e4df" "www.baidu.com" "110.242.68.3:443"

此外 Envoy 還提供了一個管理視圖,可以讓我們?nèi)ゲ榭磁渲谩⒔y(tǒng)計信息、日志以及其他 Envoy 內(nèi)部的一些數(shù)據(jù)。上面我們定義的管理視圖的端口為 9901,當(dāng)然我們也可以通過 Docker 容器將管理端口暴露給外部用戶。

docker run --name=envoy -d \
  -p 9901:9901 \
  -p 80:10000 \
  -v $(pwd)/manifests/2.Envoy/envoy-1.yaml:/etc/envoy/envoy.yaml \
  envoyproxy/envoy:v1.28.0

上面的配置就會將管理頁面暴露給外部用戶,當(dāng)然我們這里僅僅用于演示是可以的,如果你是用于線上環(huán)境還需要做好一些安全保護措施。運行成功后,現(xiàn)在我們可以在瀏覽器里面輸入 localhost:9901 來訪問 Envoy 的管理頁面:

envoy admin

需要注意的是當(dāng)前的管理頁面不僅允許執(zhí)行一些破壞性的操作(比如,關(guān)閉服務(wù)),而且還可能暴露一些私有信息(比如統(tǒng)計信息、集群名稱、證書信息等)。所以應(yīng)該只允許通過安全網(wǎng)絡(luò)去訪問管理頁面。

遷移 NGINX 到 Envoy

因為現(xiàn)階段大部分的應(yīng)用可能還是使用的比較傳統(tǒng)的 Nginx 來做服務(wù)代理,為了對比 Envoy 和 Nginx 的區(qū)別,我們這里將來嘗試將 Nginx 的配置遷移到 Envoy 上來,這樣也有助于我們?nèi)チ私?Envoy 的配置。

首先我們使用 Nginx 官方 Wiki 的完整示例來進(jìn)行說明,完整的 nginx.conf 配置如下所示:

user  www www;
pid /var/run/nginx.pid;
worker_processes  2;

events {
  worker_connections   2000;
}

http {
  gzip on;
  gzip_min_length  1100;
  gzip_buffers     4 8k;
  gzip_types       text/plain;

  log_format main      '$remote_addr - $remote_user [$time_local]  '
    '"$request" $status $bytes_sent '
    '"$http_referer" "$http_user_agent" '
    '"$gzip_ratio"';

  log_format download  '$remote_addr - $remote_user [$time_local]  '
    '"$request" $status $bytes_sent '
    '"$http_referer" "$http_user_agent" '
    '"$http_range" "$sent_http_content_range"';

  upstream targetCluster {
    192.168.215.3:80;
    192.168.215.4:80;
  }

  server {
    listen        8080;
    server_name   one.example.com  www.one.example.com;

    access_log   /var/log/nginx.access_log  main;
    error_log  /var/log/nginx.error_log  info;

    location / {
      proxy_pass         http://targetCluster/;
      proxy_redirect     off;

      proxy_set_header   Host             $host;
      proxy_set_header   X-Real-IP        $remote_addr;
    }
  }
}

上面的 Nginx 配置有 3 個核心配置:

  • 配置 Nginx 服務(wù)、日志結(jié)構(gòu)和 Gzip 功能
  • 配置 Nginx 在端口 8080 上接受對 one.example.com 域名的請求
  • 根據(jù)不同的路徑配置將流量轉(zhuǎn)發(fā)給目標(biāo)服務(wù)

并不是所有的 Nginx 的配置都適用于 Envoy,有些方面的配置我們可以不用。Envoy 代理主要有 4 中主要的配置類型,它們是支持 Nginx 提供的核心基礎(chǔ)結(jié)構(gòu)的:

  • Listeners(監(jiān)聽器):他們定義 Envoy 代理如何接收傳入的網(wǎng)絡(luò)請求,建立連接后,它會傳遞到一組過濾器進(jìn)行處理
  • Filters(過濾器):過濾器是處理傳入和傳出請求的管道結(jié)構(gòu)的一部分,比如可以開啟類似于 Gzip 之類的過濾器,該過濾器就會在將數(shù)據(jù)發(fā)送到客戶端之前進(jìn)行壓縮
  • Routers(路由器):這些路由器負(fù)責(zé)將流量轉(zhuǎn)發(fā)到定義的目的集群去
  • Clusters(集群):集群定義了流量的目標(biāo)端點和相關(guān)配置。

我們將使用這 4 個組件來創(chuàng)建 Envoy 代理配置,去匹配 Nginx 中的配置。Envoy 的重點一直是在 API 和動態(tài)配置上,但是我們這里仍然使用靜態(tài)配置。

Nginx 配置的核心是 HTTP 配置配置,里面包含了:

  • 定義支持哪些 MIME 類型
  • 默認(rèn)的超時時間
  • Gzip 配置

我們可以通過 Envoy 代理中的過濾器來配置這些內(nèi)容。在 HTTP 配置部分,Nginx 配置指定了監(jiān)聽的端口 8080,并響應(yīng)域名 one.example.com 和 www.one.example.com 的傳入請求:

server {
    listen        8080;
    server_name   one.example.com  www.one.example.com;
    ......
}

在 Envoy 中,這部分就是監(jiān)聽器來管理的。開始一個 Envoy 代理最重要的方面就是定義監(jiān)聽器,我們需要創(chuàng)建一個配置文件來描述我們?nèi)绾稳ミ\行 Envoy 實例。

這里我們定義一個 static_resources 配置,它是 Envoy 配置的根節(jié)點,它包含了所有的靜態(tài)配置,包括監(jiān)聽器、集群、路由等。我們將創(chuàng)建一個新的監(jiān)聽器并將其綁定到 8080 端口上,該配置指示了 Envoy 代理用于接收網(wǎng)絡(luò)請求的端口,如下所示:

static_resources:
  listeners:
    - name: listener_0 # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 8080 # 監(jiān)聽器的端口

需要注意的是我們沒有在監(jiān)聽器部分定義 server_name,這需要在過濾器部分進(jìn)行處理。

當(dāng)請求進(jìn)入 Nginx 時,location 部分定義了如何處理流量以及在什么地方轉(zhuǎn)發(fā)流量。在下面的配置中,站點的所有傳入流量都將被代理到一個名為 targetCluster 的上游(upstream)集群,上游集群定義了處理請求的節(jié)點。

location / {
    proxy_pass         http://targetCluster/;
    proxy_redirect     off;

    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
}

在 Envoy 中,這部分將由過濾器來進(jìn)行配置管理。在靜態(tài)配置中,過濾器定義了如何處理傳入的請求,在我們這里,將配置一個過濾器去匹配上一步中的 server_names,當(dāng)接收到與定義的域名和路由匹配的傳入請求時,流量將轉(zhuǎn)發(fā)到集群,集群和 Nginx 配置中的 upstream 是一致的。

filter_chains:
  - filters:
      - 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
          http_filters: # 定義http過濾器鏈
            - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          route_config:
            name: local_route
            virtual_hosts:
              - name: backend
                domains:
                  - "one.example.com"
                  - "www.one.example.com"
                routes:
                  - match:
                      prefix: "/"
                    route:
                      cluster: targetCluster

其中 envoy.filters.network.http_connection_manager 是 Envoy 內(nèi)置的一個過濾器,用于處理 HTTP 連接的,除此之外,還有其他的一些內(nèi)置的過濾器,比如 Redis、Mongo、TCP。

在 Nginx 中,upstream(上游)配置定義了處理請求的目標(biāo)服務(wù)器集群,在我們這里的示例中,分配了兩個集群。

upstream targetCluster {
  192.168.215.3:80;
  192.168.215.4:80;
}

在 Envoy 代理中,這部分是通過 clusters 進(jìn)行配置管理的。upstream 等同與 Envoy 中的 clusters 定義,我們這里通過集群定義了主機被訪問的方式,還可以配置超時和負(fù)載均衡等方面更精細(xì)的控制。

clusters:
  - name: targetCluster
    connect_timeout: 0.25s
    type: STRICT_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: targetCluster
      endpoints:
        - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 192.168.215.3
                    port_value: 80
            - endpoint:
                address:
                  socket_address:
                    address: 192.168.215.4
                    port_value: 80

上面我們配置了 STRICT_DNS 類型的服務(wù)發(fā)現(xiàn),Envoy 會持續(xù)異步地解析指定的 DNS 目標(biāo)。DNS 解析結(jié)果返回的每個 IP 地址都將被視為上游集群的主機。所以如果返回兩個 IP 地址,則 Envoy 將認(rèn)為集群有兩個主機,并且兩個主機都應(yīng)進(jìn)行負(fù)載均衡,如果從結(jié)果中刪除了一個主機,則 Envoy 會從現(xiàn)有的連接池中將其剔出掉。

最后需要配置的日志部分,Envoy 采用云原生的方式,將應(yīng)用程序日志都輸出到 stdout 和 stderr,而不是將錯誤日志輸出到磁盤。

當(dāng)用戶發(fā)起一個請求時,訪問日志默認(rèn)是被禁用的,我們可以手動開啟。要為 HTTP 請求開啟訪問日志,需要在 HTTP 連接管理器中包含一個 access_log 的配置,該路徑可以是設(shè)備,比如 stdout,也可以是磁盤上的某個文件,這依賴于我們自己的實際情況。

下面過濾器中的配置就會將所有訪問日志通過管理傳輸?shù)?nbsp;stdout:

- name: envoy.filters.network.http_connection_manager
  typed_config:
    # 啟用 http_connection_manager
    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
    stat_prefix: ingress_http
    access_log:
      - name: envoy.access_loggers.stdout
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
    http_filters: # 定義http過濾器鏈
      - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
    route_config:
    # ......

默認(rèn)情況下,Envoy 訪問日志格式包含整個 HTTP 請求的詳細(xì)信息:

[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%"
%RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION%
%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%"
"%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n

輸出結(jié)果格式化后如下所示:

[2023-10-25T07:25:09.826Z] "GET / HTTP/1.1" 200 - 0 102931 361 210 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36" "6e19ddda-e0a1-41f9-9355-ea5db8d23bcc" "one.example.com" "192.168.215.4:80"

我們也可以通過設(shè)置 format 字段來自定義輸出日志的格式,例如:

access_log:
  - name: envoy.access_loggers.stdout
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
      log_format:
        text_format: "[%START_TIME%] %REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL% %RESPONSE_CODE% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% %REQ(X-REQUEST-ID)% %REQ(:AUTHORITY)% %UPSTREAM_HOST%\n"

此外我們也可以通過設(shè)置 json_format 字段來將日志作為 JSON 格式輸出,例如:

access_log:
  - name: envoy.access_loggers.stdout
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
      log_format:
        json_format:
          {
            "protocol": "%PROTOCOL%",
            "duration": "%DURATION%",
            "request_method": "%REQ(:METHOD)%",
          }

要注意的是,訪問日志會在未設(shè)置、或者空值的位置加入一個字符:-。不同類型的訪問日志(例如 HTTP 和 TCP)共用同樣的格式字符串。不同類型的日志中,某些字段可能會有不同的含義。有關(guān) Envoy 日志的更多信息,可以查看官方文檔對應(yīng)的說明。當(dāng)然日志并不是 Envoy 代理獲得請求可見性的唯一方法,Envoy 還內(nèi)置了高級跟蹤和指標(biāo)功能。

最后我們完整的 Envoy 配置如下所示:

# envoy-2.yaml
static_resources:
  listeners:
    - name: listener_0 # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 8080 # 監(jiān)聽器的端口

      filter_chains:
        - filters:
            - 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
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定義http過濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "one.example.com"
                        - "www.one.example.com"
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: targetCluster

  clusters:
    - name: targetCluster
      connect_timeout: 0.25s
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: targetCluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.215.3
                      port_value: 80
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.215.4
                      port_value: 80

現(xiàn)在我們已經(jīng)將 Nginx 配置轉(zhuǎn)換為了 Envoy 代理,接下來我們可以來啟動 Envoy 代理進(jìn)行測試驗證。

在 Nginx 配置的頂部,有一行配置 user www www;,表示用非 root 用戶來運行 Nginx 以提高安全性。而 Envoy 代理采用云原生的方法來管理使用這,我們通過容器啟動 Envoy 代理的時候,可以指定一個低特權(quán)的用戶。

下面的命令將通過 Docker 容器來啟動一個 Envoy 實例,該命令使 Envoy 可以監(jiān)聽 80 端口上的流量請求,但是我們在 Envoy 的監(jiān)聽器配置中指定的是 8080 端口,所以我們用一個低特權(quán)用戶身份來運行:

$ docker run --name proxy1 -p 80:8080 --user 1000:1000 -v $(pwd)/manifests/2.Envoy/envoy-2.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy:v1.28.0

啟動代理后,就可以開始測試了,下面我們用 curl 命令使用代理配置的 host 頭發(fā)起一個網(wǎng)絡(luò)請求:

$ curl -H "Host: one.example.com" localhost -i
HTTP/1.1 503 Service Unavailable
content-length: 91
content-type: text/plain
date: Wed, 25 Oct 2023 07:37:55 GMT
server: envoy

upstream connect error or disconnect/reset before headers. reset reason: connection timeout%

我們可以看到會出現(xiàn) 503 錯誤,這是因為我們配置的上游集群主機根本就沒有運行,所以 Envoy 代理請求到不可用的主機上去了,就出現(xiàn)了這樣的錯誤。我們可以使用下面的命令啟動兩個 HTTP 服務(wù),用來表示上游主機:

$ docker run -d cnych/docker-http-server; docker run -d cnych/docker-http-server;
$ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS         PORTS                                              NAMES
3ecf1125bd0c   cnych/docker-http-server   "/app"                   2 minutes ago   Up 2 minutes   80/tcp                                             loving_babbage
0195f14ec57a   cnych/docker-http-server   "/app"                   2 minutes ago   Up 2 minutes   80/tcp                                             heuristic_torvalds
a55f6175c5c7   envoyproxy/envoy:v1.28.0   "/docker-entrypoint.…"   3 minutes ago   Up 3 minutes   10000/tcp, 0.0.0.0:80->8080/tcp, :::80->8080/tcp   proxy1

當(dāng)上面兩個服務(wù)啟動成功后,現(xiàn)在我們再通過 Envoy 去訪問目標(biāo)服務(wù)就正常了:

$ curl -H "Host: one.example.com" localhost -i
HTTP/1.1 200 OK
date: Wed, 25 Oct 2023 07:42:51 GMT
content-length: 58
content-type: text/html; charset=utf-8
x-envoy-upstream-service-time: 1
server: envoy

<h1>This request was processed by host: 3ecf1125bd0c</h1>
$ curl -H "Host: one.example.com" localhost -i
HTTP/1.1 200 OK
date: Wed, 25 Oct 2023 07:42:53 GMT
content-length: 58
content-type: text/html; charset=utf-8
x-envoy-upstream-service-time: 8
server: envoy

<h1>This request was processed by host: 0195f14ec57a</h1>

當(dāng)訪問請求的時候,我們可以看到是哪個容器處理了請求,在 Envoy 代理容器中,也可以看到請求的日志輸出:

[2023-10-25T07:42:51.297Z] "GET / HTTP/1.1" 200 - 0 58 3 1 "-" "curl/7.87.0" "ff1e1009-d5a3-4a71-87ef-479e234c9858" "one.example.com" "192.168.215.4:80"
[2023-10-25T07:42:53.153Z] "GET / HTTP/1.1" 200 - 0 58 9 8 "-" "curl/7.87.0" "d0ebdd10-a1d2-406e-8c6e-6fd8451aded3" "one.example.com" "192.168.215.3:80"

到這里我們就完成了將 Nginx 配置遷移到 Envoy 的過程,可以看到 Envoy 的配置和 Nginx 的配置還是有很大的區(qū)別的,但是我們可以看到 Envoy 的配置更加的靈活,而且 Envoy 代理的配置是可以動態(tài)更新的,這樣就可以實現(xiàn)無縫的服務(wù)升級。

使用 SSL/TLS 保護流量

接下來我們將來了解下如何使用 Envoy 保護 HTTP 網(wǎng)絡(luò)請求。確保 HTTP 流量安全對于保護用戶隱私和數(shù)據(jù)是至關(guān)重要的。下面我們來了解下如何在 Envoy 中配置 SSL 證書。

SSL 證書

這里我們將為 example.com 域名生成一個自簽名的證書,當(dāng)然如果在生產(chǎn)環(huán)境時候,需要使用正規(guī) CA 機構(gòu)購買的證書,或者使用 Let's Encrypt 的免費證書服務(wù)。

下面的命令會在目錄 certs/ 中創(chuàng)建一個新的證書和密鑰:

$ mkdir certs; cd certs;
$ openssl req -nodes -new -x509 \
  -keyout example-com.key -out example-com.crt \
  -days 365 \
  -subj '/CN=example.com/O=youdianzhishi.com/C=CN';
  Generating a RSA private key
....+..+....+..+.......+.....+...+....+++++++++++++++++++++++++++++++++++++++*....+...+................+......+........+...+...+.+...+......+.....+......+.+.....+++++++++++++++++++++++++++++++++++++++*............+.....+................+....................+......+....+..............+.+..+...+...............+.......+............+...+...+......+.....+....+..+....+.........+....................+.+......+..+...+.........+.+..+..........+...............+..+...+.+......+...+..+......+.......+............+......+..+......+......++++++
.....+............+.+..+....+.....+.+.........+.....+++++++++++++++++++++++++++++++++++++++*...+...+............+.....+.+...........+...+...+...............+...+......+.........+.+.....+...+.+......+...+...+++++++++++++++++++++++++++++++++++++++*........+..........+...+...+......+.........++++++
-----
$ cd -

流量保護

在 Envoy 中保護 HTTP 流量,需要通過添加 transport_socket 過濾器,該過濾器提供了為 Envoy 代理中配置的域名指定證書的功能,請求 HTTPS 請求時候,就使用匹配的證書。我們這里直接使用上一步中生成的自簽名證書即可。

我們這里的 Envoy 配置文件中包含了所需的 HTTPS 支持的配置,我們添加了兩個監(jiān)聽器,一個監(jiān)聽器在 8080 端口上用于 HTTP 通信,另外一個監(jiān)聽器在 8443 端口上用于 HTTPS 通信。

在 HTTPS 監(jiān)聽器中定義了 HTTP 連接管理器,該代理將代理 /service/1 和 /service/2 這兩個端點的傳入請求,這里我們需要通過 envoy.transport_sockets.tls 配置相關(guān)證書,如下所示:

transport_socket:
  name: envoy.transport_sockets.tls
  typed_config:
    # 一個監(jiān)聽傳輸套接字,用于使用 TLS 接受下游連接(客戶端)。
    "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
    common_tls_context:
      tls_certificates:
        - certificate_chain:
            filename: "/etc/certs/example-com.crt"
          private_key:
            filename: "/etc/certs/example-com.key"

在 TLS 上下文中定義了生成的證書和密鑰,如果我們有多個域名,每個域名都有自己的證書,則需要通過 tls_certificates 定義多個證書鏈。

自動跳轉(zhuǎn)

定義了 TLS 上下文后,該站點將能夠通過 HTTPS 提供流量了,但是如果用戶是通過 HTTP 來訪問的服務(wù),為了確保安全,我們可以將其重定向到 HTTPS 版本服務(wù)上去。

在 HTTP 配置中,我們將 https_redirect: true 的標(biāo)志添加到過濾器的配置中即可實現(xiàn)跳轉(zhuǎn)功能。

route_config:
  virtual_hosts:
    - name: backend
      domains:
        - "example.com"
      routes:
        - match:
            prefix: "/"
          redirect:
            path_redirect: "/"
            https_redirect: true

當(dāng)用戶訪問網(wǎng)站的 HTTP 版本時,Envoy 代理將根據(jù)過濾器配置來匹配域名和路徑,匹配到過后將請求重定向到站點的 HTTPS 版本去。完整的 Envoy 配置如下所示:

# envoy-3.yaml
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001

static_resources:
  listeners:
    - name: listener_http # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 8080 # 監(jiān)聽器的端口

      filter_chains:
        - filters:
            - 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
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定義http過濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "example.com"
                      routes:
                        - match:
                            prefix: "/"
                          redirect:
                            path_redirect: "/"
                            https_redirect: true

    - name: listener_https # 監(jiān)聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽器的地址
          port_value: 8443 # 監(jiān)聽器的端口

      filter_chains:
        - transport_socket:
            name: envoy.transport_sockets.tls
            typed_config:
              # 一個監(jiān)聽傳輸套接字,用于使用 TLS 接受下游連接(客戶端)。
              "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
              common_tls_context:
                tls_certificates:
                  - certificate_chain:
                      filename: "/etc/certs/example-com.crt"
                    private_key:
                      filename: "/etc/certs/example-com.key"
          filters:
            - 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
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定義http過濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "example.com"
                      routes:
                        - match:
                            prefix: "/service/1"
                          route:
                            cluster: service1
                        - match:
                            prefix: "/service/2"
                          route:
                            cluster: service2

  clusters:
    - name: service1
      connect_timeout: 0.25s
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: service1
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.215.3
                      port_value: 80
    - name: service2
      connect_timeout: 0.25s
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: service2
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.215.4
                      port_value: 80

測試

現(xiàn)在配置已經(jīng)完成了,我們就可以啟動 Envoy 實例來進(jìn)行測試了。在我們這個示例中,Envoy 暴露 80 端口來處理 HTTP 請求,暴露 443 端口來處理 HTTPS 請求,此外還在 8001 端口上暴露了管理頁面,我們可以通過管理頁面查看有關(guān)證書的信息。

使用如下命令啟動 Envoy 代理:

$ docker run -it --name tls-proxy -p 80:8080 -p 443:8443 -p 8001:8001 -v $(pwd)/manifests/2.Envoy/certs:/etc/certs/ -v $(pwd)/manifests/2.Envoy/envoy-3.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy:v1.28.0

啟動完成后所有的 HTTPS 和 TLS 校驗都是通過 Envoy 來進(jìn)行處理的,所以我們不需要去修改應(yīng)該程序。同樣我們啟動兩個 HTTP 服務(wù)來處理傳入的請求:

$ docker run -d cnych/docker-http-server; docker run -d cnych/docker-http-server;
$ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS          PORTS                                                                                                                                  NAMES
1690f6562870   cnych/docker-http-server   "/app"                   32 seconds ago   Up 32 seconds   80/tcp                                                                                                                                 distracted_ride
f86925657e62   cnych/docker-http-server   "/app"                   32 seconds ago   Up 32 seconds   80/tcp                                                                                                                                 festive_almeida
d02e37b26b22   envoyproxy/envoy:v1.28.0   "/docker-entrypoint.…"   3 minutes ago    Up 3 minutes    0.0.0.0:8001->8001/tcp, :::8001->8001/tcp, 10000/tcp, 0.0.0.0:80->8080/tcp, :::80->8080/tcp, 0.0.0.0:443->8443/tcp, :::443->8443/tcp   tls-proxy

上面的幾個容器啟動完成后,就可以進(jìn)行測試了,首先我們請求 HTTP 的服務(wù),由于配置了自動跳轉(zhuǎn),所以應(yīng)該會被重定向到 HTTPS 的版本上去:

$ curl -H "Host: example.com" http://localhost -i
HTTP/1.1 301 Moved Permanently
location: https://example.com/
date: Wed, 25 Oct 2023 08:30:48 GMT
server: envoy
content-length: 0

我們可以看到上面有 HTTP/1.1 301 Moved Permanently 這樣的重定向響應(yīng)信息。然后我們嘗試直接請求 HTTPS 的服務(wù):

$ curl -k -H "Host: example.com" https://localhost/service/1 -i
HTTP/1.1 200 OK
date: Wed, 25 Oct 2023 08:31:17 GMT
content-length: 58
content-type: text/html; charset=utf-8
x-envoy-upstream-service-time: 24
server: envoy

<h1>This request was processed by host: f86925657e62</h1>

$ curl -k -H "Host: example.com" https://localhost/service/2 -i
HTTP/1.1 200 OK
date: Wed, 25 Oct 2023 08:31:28 GMT
content-length: 58
content-type: text/html; charset=utf-8
x-envoy-upstream-service-time: 22
server: envoy

<h1>This request was processed by host: 1690f6562870</h1>

我們可以看到通過 HTTPS 進(jìn)行訪問可以正常得到對應(yīng)的響應(yīng),需要注意的是由于我們這里使用的是自簽名的證書,所以需要加上 -k 參數(shù)來忽略證書校驗,如果沒有這個參數(shù)則在請求的時候會報錯:

$ curl -H "Host: example.com" https://localhost/service/2 -i
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

我們也可以通過管理頁面去查看證書相關(guān)的信息,上面我們啟動容器的時候綁定了宿主機的 8001 端口,所以我們可以通過訪問 http://localhost:8001/certs 來獲取到證書相關(guān)的信息:

證書信息

到這里我們就基本了解了 Envoy 的靜態(tài)配置方式,接下來的 xDS 才是 Envoy 中的精華部分。

責(zé)任編輯:姜華 來源: k8s技術(shù)圈
相關(guān)推薦

2023-09-11 08:13:03

分布式跟蹤工具

2020-10-18 07:32:06

SD-WAN網(wǎng)絡(luò)傳統(tǒng)廣域網(wǎng)

2023-02-10 09:04:27

2020-02-18 16:20:03

Redis ANSI C語言日志型

2022-06-20 09:01:23

Git插件項目

2022-08-01 11:33:09

用戶分析標(biāo)簽策略

2021-04-08 07:37:39

隊列數(shù)據(jù)結(jié)構(gòu)算法

2018-11-14 11:57:28

2019-05-14 09:31:16

架構(gòu)整潔軟件編程范式

2018-05-22 08:24:50

PythonPyMongoMongoDB

2023-10-17 08:15:28

API前后端分離

2024-09-23 08:00:00

消息隊列MQ分布式系統(tǒng)

2020-07-03 08:21:57

Java集合框架

2017-03-11 22:19:09

深度學(xué)習(xí)

2022-04-07 10:39:21

反射Java安全

2023-11-18 09:30:42

模型AI

2024-07-31 15:39:00

2019-05-09 15:12:20

Linux 系統(tǒng) 數(shù)據(jù)

2022-07-06 12:07:06

Python函數(shù)式編程

2019-04-01 10:43:59

Linux問題故障
點贊
收藏

51CTO技術(shù)棧公眾號