偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

核心能力

Envoy 的誕生源于以下理念:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

常用術(shù)語(yǔ)

在我們介紹 Envoy 架構(gòu)之前,有必要先介紹一些常用的術(shù)語(yǔ)定義,因?yàn)檫@些術(shù)語(yǔ)貫穿整個(gè) Envoy 的架構(gòu)設(shè)計(jì)。

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

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

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

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

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

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

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

Envoy 架構(gòu)

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

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

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

第一個(gè) Envoy 代理

下面我們通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)介紹 Envoy 的基本使用。

配置

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

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

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

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

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

static_resources:

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

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

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

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

過(guò)濾器是通過(guò) filter_chains 來(lái)定義的,每個(gè)過(guò)濾器的目的是找到傳入請(qǐng)求的匹配項(xiàng),以使其與目標(biāo)地址進(jìn)行匹配。Filter 過(guò)濾器的寫(xiě)法如下所示:

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

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

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

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

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

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

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

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

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

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

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

當(dāng)請(qǐng)求于過(guò)濾器匹配時(shí),該請(qǐng)求將會(huì)傳遞到集群。下面的配置就是將主機(jī)定義為訪(fǎng)問(wèn) HTTPS 的 baidu.com 域名,如果定義了多個(gè)主機(jī),則 Envoy 將執(zhí)行輪詢(xún)(Round Robin)策略。配置如下所示:

clusters:
  - name: service_baidu # 集群的名稱(chēng),與上面的 router 中的 cluster 對(duì)應(yīng)
    type: LOGICAL_DNS # 用于解析集群(生成集群端點(diǎn))時(shí)使用的服務(wù)發(fā)現(xiàn)類(lèi)型,可用值有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 來(lái)定義集群的成員,指定 STATIC、STRICT_DNS 或 LOGICAL_DNS 集群的成員需要設(shè)置此項(xiàng)。
      cluster_name: service_baidu # 集群的名稱(chēng)
      endpoints: # 需要進(jìn)行負(fù)載均衡的端點(diǎn)列表
        - 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

最后,還需要配置一個(gè)管理模塊:

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

上面的配置定義了 Envoy 的靜態(tài)配置模板,監(jiān)聽(tīng)器定義了 Envoy 的端口和 IP 地址,監(jiān)聽(tīng)器具有一組過(guò)濾器來(lái)匹配傳入的請(qǐng)求,匹配請(qǐng)求后,將請(qǐng)求轉(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)聽(tīng)器的名稱(chēng)
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽(tīng)器的地址
          port_value: 10000 # 監(jiān)聽(tīng)器的端口

      filter_chains: # 配置過(guò)濾器鏈
        - filters:
            # 過(guò)濾器配置的名稱(chēng),要填寫(xiě) typed_config 配置的過(guò)濾器指定的名稱(chē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:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定義http過(guò)濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過(guò)濾器
                    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 # 集群的名稱(chēng)
      type: LOGICAL_DNS # 用于解析集群(生成集群端點(diǎn))時(shí)使用的服務(wù)發(fā)現(xiàn)類(lèi)型,可用值有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,可能會(huì)覺(jué)得它的配置太復(fù)雜了,讓人眼花繚亂。其實(shí)只要我們理解了網(wǎng)絡(luò)代理程序的流程就不難配置了,比如作為一個(gè)代理,首先要能獲取請(qǐng)求流量,通常是采用監(jiān)聽(tīng)端口的方式實(shí)現(xiàn);其次拿到請(qǐng)求數(shù)據(jù)后需要對(duì)其做微處理,例如附加 Header 或校驗(yàn)?zāi)硞€(gè) Header 字段的內(nèi)容等,這里針對(duì)來(lái)源數(shù)據(jù)的層次不同,可以分為 L3/L4/L7,然后將請(qǐng)求轉(zhuǎn)發(fā)出去;轉(zhuǎn)發(fā)這里又可以衍生出如果后端是一個(gè)集群,需要從中挑選一臺(tái)機(jī)器,如何挑選又涉及到負(fù)載均衡等。

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

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

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

Envoy 配置流程

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

啟動(dòng) Envoy

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

我們這里也通過(guò) Docker 容器來(lái)啟動(dòng) Envoy,將上面的配置文件通過(guò) Volume 掛載到容器中的 /etc/envoy/envoy.yaml 去。

然后使用以下命令啟動(dòng)綁定到端口 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

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

localhost

可以看到請(qǐng)求被代理到了 baidu.com,而且應(yīng)該也可以看到 URL 地址沒(méi)有變化,還是 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 還提供了一個(gè)管理視圖,可以讓我們?nèi)ゲ榭磁渲?、統(tǒng)計(jì)信息、日志以及其他 Envoy 內(nèi)部的一些數(shù)據(jù)。上面我們定義的管理視圖的端口為 9901,當(dāng)然我們也可以通過(guò) Docker 容器將管理端口暴露給外部用戶(hù)。

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

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

envoy admin

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

遷移 NGINX 到 Envoy

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

首先我們使用 Nginx 官方 Wiki 的完整示例來(lái)進(jìn)行說(shuō)明,完整的 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 個(gè)核心配置:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

在 Envoy 中,這部分將由過(guò)濾器來(lái)進(jìn)行配置管理。在靜態(tài)配置中,過(guò)濾器定義了如何處理傳入的請(qǐng)求,在我們這里,將配置一個(gè)過(guò)濾器去匹配上一步中的 server_names,當(dāng)接收到與定義的域名和路由匹配的傳入請(qǐng)求時(shí),流量將轉(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過(guò)濾器鏈
            - name: envoy.filters.http.router # 調(diào)用7層的路由過(guò)濾器
              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)置的一個(gè)過(guò)濾器,用于處理 HTTP 連接的,除此之外,還有其他的一些內(nèi)置的過(guò)濾器,比如 Redis、Mongo、TCP。

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

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

在 Envoy 代理中,這部分是通過(guò) clusters 進(jìn)行配置管理的。upstream 等同與 Envoy 中的 clusters 定義,我們這里通過(guò)集群定義了主機(jī)被訪(fǎng)問(wèn)的方式,還可以配置超時(shí)和負(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 類(lèi)型的服務(wù)發(fā)現(xiàn),Envoy 會(huì)持續(xù)異步地解析指定的 DNS 目標(biāo)。DNS 解析結(jié)果返回的每個(gè) IP 地址都將被視為上游集群的主機(jī)。所以如果返回兩個(gè) IP 地址,則 Envoy 將認(rèn)為集群有兩個(gè)主機(jī),并且兩個(gè)主機(jī)都應(yīng)進(jìn)行負(fù)載均衡,如果從結(jié)果中刪除了一個(gè)主機(jī),則 Envoy 會(huì)從現(xiàn)有的連接池中將其剔出掉。

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

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

下面過(guò)濾器中的配置就會(huì)將所有訪(fǎng)問(wèn)日志通過(guò)管理傳輸?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過(guò)濾器鏈
      - name: envoy.filters.http.router # 調(diào)用7層的路由過(guò)濾器
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
    route_config:
    # ......

默認(rèn)情況下,Envoy 訪(fǎng)問(wèn)日志格式包含整個(gè) HTTP 請(qǐng)求的詳細(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"

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

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"

此外我們也可以通過(guò)設(shè)置 json_format 字段來(lái)將日志作為 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)%",
          }

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

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

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

      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過(guò)濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過(guò)濾器
                    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 代理,接下來(lái)我們可以來(lái)啟動(dòng) Envoy 代理進(jìn)行測(cè)試驗(yàn)證。

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

下面的命令將通過(guò) Docker 容器來(lái)啟動(dòng)一個(gè) Envoy 實(shí)例,該命令使 Envoy 可以監(jiān)聽(tīng) 80 端口上的流量請(qǐng)求,但是我們?cè)?Envoy 的監(jiān)聽(tīng)器配置中指定的是 8080 端口,所以我們用一個(gè)低特權(quán)用戶(hù)身份來(lái)運(yù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

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

$ 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%

我們可以看到會(huì)出現(xiàn) 503 錯(cuò)誤,這是因?yàn)槲覀兣渲玫纳嫌渭褐鳈C(jī)根本就沒(méi)有運(yùn)行,所以 Envoy 代理請(qǐng)求到不可用的主機(jī)上去了,就出現(xiàn)了這樣的錯(cuò)誤。我們可以使用下面的命令啟動(dòng)兩個(gè) HTTP 服務(wù),用來(lái)表示上游主機(jī):

$ 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)上面兩個(gè)服務(wù)啟動(dòng)成功后,現(xiàn)在我們?cè)偻ㄟ^(guò) Envoy 去訪(fǎng)問(wèn)目標(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)訪(fǎng)問(wèn)請(qǐng)求的時(shí)候,我們可以看到是哪個(gè)容器處理了請(qǐng)求,在 Envoy 代理容器中,也可以看到請(qǐng)求的日志輸出:

[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 的過(guò)程,可以看到 Envoy 的配置和 Nginx 的配置還是有很大的區(qū)別的,但是我們可以看到 Envoy 的配置更加的靈活,而且 Envoy 代理的配置是可以動(dòng)態(tài)更新的,這樣就可以實(shí)現(xiàn)無(wú)縫的服務(wù)升級(jí)。

使用 SSL/TLS 保護(hù)流量

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

SSL 證書(shū)

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

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

$ 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 -

流量保護(hù)

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

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

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

transport_socket:
  name: envoy.transport_sockets.tls
  typed_config:
    # 一個(gè)監(jiān)聽(tīng)傳輸套接字,用于使用 TLS 接受下游連接(客戶(hù)端)。
    "@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 上下文中定義了生成的證書(shū)和密鑰,如果我們有多個(gè)域名,每個(gè)域名都有自己的證書(shū),則需要通過(guò) tls_certificates 定義多個(gè)證書(shū)鏈。

自動(dòng)跳轉(zhuǎn)

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

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

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

當(dāng)用戶(hù)訪(fǎng)問(wèn)網(wǎng)站的 HTTP 版本時(shí),Envoy 代理將根據(jù)過(guò)濾器配置來(lái)匹配域名和路徑,匹配到過(guò)后將請(qǐng)求重定向到站點(diǎn)的 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)聽(tīng)器的名稱(chēng)
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽(tīng)器的地址
          port_value: 8080 # 監(jiān)聽(tīng)器的端口

      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過(guò)濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過(guò)濾器
                    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)聽(tīng)器的名稱(chēng)
      address:
        socket_address:
          address: 0.0.0.0 # 監(jiān)聽(tīng)器的地址
          port_value: 8443 # 監(jiān)聽(tīng)器的端口

      filter_chains:
        - transport_socket:
            name: envoy.transport_sockets.tls
            typed_config:
              # 一個(gè)監(jiān)聽(tīng)傳輸套接字,用于使用 TLS 接受下游連接(客戶(hù)端)。
              "@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過(guò)濾器鏈
                  - name: envoy.filters.http.router # 調(diào)用7層的路由過(guò)濾器
                    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

測(cè)試

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

使用如下命令啟動(dòng) 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

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

$ 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

上面的幾個(gè)容器啟動(dòng)完成后,就可以進(jìn)行測(cè)試了,首先我們請(qǐng)求 HTTP 的服務(wù),由于配置了自動(dòng)跳轉(zhuǎn),所以應(yīng)該會(huì)被重定向到 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)信息。然后我們嘗試直接請(qǐ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>

我們可以看到通過(guò) HTTPS 進(jìn)行訪(fǎng)問(wèn)可以正常得到對(duì)應(yīng)的響應(yīng),需要注意的是由于我們這里使用的是自簽名的證書(shū),所以需要加上 -k 參數(shù)來(lái)忽略證書(shū)校驗(yàn),如果沒(méi)有這個(gè)參數(shù)則在請(qǐng)求的時(shí)候會(huì)報(bào)錯(cuò):

$ 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ò)管理頁(yè)面去查看證書(shū)相關(guān)的信息,上面我們啟動(dòng)容器的時(shí)候綁定了宿主機(jī)的 8001 端口,所以我們可以通過(guò)訪(fǎng)問(wèn) http://localhost:8001/certs 來(lái)獲取到證書(shū)相關(guān)的信息:

證書(shū)信息

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

責(zé)任編輯:姜華 來(lái)源: 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)

2022-08-01 11:33:09

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

2021-04-08 07:37:39

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

2023-02-10 09:04:27

2020-02-18 16:20:03

Redis ANSI C語(yǔ)言日志型

2022-06-20 09:01:23

Git插件項(xiàng)目

2018-11-14 11:57:28

2022-04-07 10:39:21

反射Java安全

2017-03-11 22:19:09

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

2023-11-18 09:30:42

模型AI

2019-05-14 09:31:16

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

2018-05-22 08:24:50

PythonPyMongoMongoDB

2020-07-03 08:21:57

Java集合框架

2024-09-23 08:00:00

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

2023-10-17 08:15:28

API前后端分離

2025-08-07 04:10:00

光模塊AI網(wǎng)絡(luò)

2024-07-31 15:39:00

2019-05-09 15:12:20

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

2019-04-10 10:43:15

Redis內(nèi)存淘汰策略
點(diǎn)贊
收藏

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