Envoy 基於文件和 API 的動態配置方式

前面我們和大家學習了 Envoy 的基礎知識,使用靜態配置來認識了 Envoy,但實際上 Envoy 的閃光點在於其動態配置,動態配置主要有基於文件和 API 兩種方式。

基於文件的動態配置

Envoy 除了支持靜態配置之外,還支持動態配置,而且動態配置也是 Envoy 重點關注的功能,本節我們將學習如何將 Envoy 靜態配置轉換爲動態配置,從而允許 Envoy 自動更新。

Envoy 動態配置

前面的章節中,我們都是直接使用的靜態配置,但是當我們需要更改配置的時候就比較麻煩了,需要重啓 Envoy 代理纔會生效。要解決這個問題,我們可以將靜態配置更改成動態配置,當我們使用動態配置的時候,更改了配置,Envoy 將會自動去重新加載配置。

Envoy 支持不同的模塊進行動態配置,可配置的有如下幾個 API(統稱爲 xDS):

動態資源,是指由 Envoy 通過 xDS 協議發現所需要的各項配置的機制,相關的配置信息保存於稱之爲管理服務器(Management Server )的主機上,經由 xDS API 向外暴露;下面是一個純動態資源的基礎配置框架。

{
  "lds_config""{...}",
  "cds_config""{...}",
  "ads_config""{...}"
}

xDS API 爲 Envoy 提供了資源的動態配置機制,它也被稱爲 Data Plane API

xDS API

Envoy 支持三種類型的配置信息的動態發現機制,相關的發現服務及其相應的 API 聯合起來 稱爲 xDS API

  1. 基於文件系統發現:指定要監視的文件系統路徑
  1. 通過查詢一到多個管理服務器發現:通過 DiscoveryRequest 協議報文發送請求,並要求服務方以 DiscoveryResponse 協議報文進行響應

v3 版本的 xDS 支持如下幾種資源類型:

Envoy 對 xDS API 的管理由後端服務器實現,包括 LDS、CDS、RDS、SRDS(Scoped Route)、VHDS (Virtual Host)、EDS、SDS、RTDS(Runtime )等。

接下來我們先更改配置來使用 EDS,讓 Envoy 根據配置文件的數據來動態添加節點。

Cluster ID

首先我們這裏定義了一個基本的 Envoy 配置文件,如下所示:

# envoy.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 # 監聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監聽器的地址
          port_value: 10000 # 監聽器的端口

      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 # 調用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains: ["*"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: targetCluster

現在我們還沒有配置 clusters 集羣部分,這是因爲我們要通過使用 EDS 來進行自動發現。

首先我們需要添加一個 node 節點讓 Envoy 來識別並應用這一個唯一的配置,動態配置中 Envoy 實例需要有唯一的 id 標識。將下面的配置放置在配置文件的頂部區域:

node:
  id: envoy_eds_id
  cluster: youdianzhishi_cluster

除了 idcluster 之外,我們還可以配置基於區域的一些位置信息來進行聲明,比如 regionzonesub_zone 等。

EDS 配置

端點發現服務 EDS 是基於 gRPC 或 REST-JSON API 服務器的 xDS 管理服務器,Envoy 使用它來獲取集羣成員。集羣成員在 Envoy 術語中稱爲 “端點”。對於每個集羣,Envoy 從發現服務獲取端點,EDS 是首選的服務發現機制:

接下來我們就可以來定義 EDS 配置了,可以來動態控制上游集羣數據。在前面這部分的靜態配置是這樣的:

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

現在我們將上面的靜態配置轉換成動態配置,首先需要轉換爲基於 EDSeds_cluster_config 屬性,並將類型更改爲 EDS,將下面的集羣配置添加到 Envoy 配置的末尾:

clusters:
  - name: targetCluster
    connect_timeout: 0.25s
    lb_policy: ROUND_ROBIN
    type: EDS
    eds_cluster_config:
      service_name: localservices # 可選,代替集羣的名稱,提供給 EDS 服務
      eds_config: # 集羣的 EDS 更新源配置
        path_config_source: # 本地文件配置源
          path: "/etc/envoy/eds.yaml"
          # watched_directory: # 可選,監視目錄中的文件更改
          #   path: "/etc/envoy"

在上面的集羣配置中我們設置了 type: EDS,表示這是一個基於 EDS 的集羣配置,然後使用 eds_cluster_config 屬性來定義 EDS 的配置信息,其中 service_name 屬性是可選的,如果沒有設置則使用集羣的名稱,這個屬性是提供給 EDS 服務的,eds_config 屬性定義了 EDS 更新源的配置,這裏我們使用的是本地文件配置源,所以使用 path_config_source 屬性來指定本地配置文件的路徑,這裏我們使用的是 /etc/envoy/eds.yaml 這個文件,這個文件將會被 Envoy 代理監視,當文件內容發生變化的時候,Envoy 將會自動更新配置。

此外還可以配置一個 watched_directory 屬性來監視目錄中的文件更改,當該目錄中的文件被移動到時,該路徑將被重新加載。這在某些部署場景中是必需的。比如如果我們使用 Kubernetes ConfigMap 來加載 xDS 資源,則可能會使用以下配置:

上述配置將確保 Envoy 監視所屬目錄的移動,這是由於 Kubernetes 在原子更新期間管理 ConfigMap 符號鏈接的方式而必需的。

上游的服務器 192.168.215.3192.168.215.3 就將來自於 /etc/envoy/eds.yaml 這個文件,我們創建一個如下所示的 eds.yaml 文件,內容如下所示:

resources:
  - "@type": type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment
    cluster_name: localservices # 集羣的名稱,如果在集羣 eds_cluster_config 中指定,這將是 service_name 值。
    endpoints:
      - lb_endpoints:
          - endpoint:
              address:
                socket_address:
                  address: 192.168.215.3
                  port_value: 80

上面我們暫時只定義了 192.168.215.3 這一個端點。該配置文件是以 DiscoveryResponse 的格式提供響應實例的。

現在配置完成後,我們可以啓動 Envoy 代理來進行測試。執行下面的命令啓動 Envoy 容器:

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

然後同樣和前面一樣運行兩個 HTTP 服務來作爲上游服務器:

$ docker run -d cnych/docker-http-server; docker run -d cnych/docker-http-server;
$ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED              STATUS          PORTS                                                                                NAMES
4ee790db09db   cnych/docker-http-server   "/app"                   3 seconds ago        Up 3 seconds    80/tcp                                                                               fervent_khorana
f9456b56f1ff   cnych/docker-http-server   "/app"                   4 seconds ago        Up 3 seconds    80/tcp                                                                               wonderful_carson
f9ce95dcc434   envoyproxy/envoy:v1.28.0   "/docker-entrypoint.…"   About a minute ago   Up 44 seconds   0.0.0.0:9901->9901/tcp, :::9901->9901/tcp, 0.0.0.0:80->10000/tcp, :::80->10000/tcp   proxy-eds

根據上面的 EDS 配置,Envoy 將把所有的流量都發送到 192.168.215.3 這一個節點上去,我們可以使用 curl localhost 來測試下:

$ curl localhost
<h1>This request was processed by host: f9456b56f1ff</h1>
$ curl localhost
<h1>This request was processed by host: f9456b56f1ff</h1>

可以看到可以正常得到響應,而且都是由 f9456b56f1ff 這個容器來處理的請求。現在我們來嘗試更新上面的 EDS 配置添加上另外的一個節點,觀察 Envoy 代理是否會自動生效。

由於我們這裏使用的是 EDS 動態配置,所以當我們要擴展上游服務的時候,只需要將新的端點添加到上面我們指定的 eds.yaml 配置文件中即可,然後 Envoy 就會自動將新添加的端點包含進來。用上面同樣的方式添加 192.168.215.4 這個端點,eds.yaml 內容如下所示:

resources:
  - "@type": type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment
    cluster_name: localservices # 集羣的名稱,如果在集羣 eds_cluster_config 中指定,這將是 service_name 值。
    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

更新後,正常情況下 Envoy 就會自動重新加載配置並將新的端點添加到負載均衡中去,這個時候我們再來訪問代理:

$ curl localhost
<h1>This request was processed by host: 2135ba4e10c9</h1>
$ curl localhost
<h1>This request was processed by host: f9456b56f1ff</h1>

可以看到已經可以自動訪問到另外的端點去了。

我在測試階段發現在 Mac 系統下面並沒有自動熱加載,在 Linux 系統下面是可以正常重新加載的。

CDS 配置

現在已經配置好了 EDS,接下來我們就可以去擴大上游集羣的規模了,如果我們想要能夠動態添加新的域名和集羣,就需要實現集羣發現服務(CDS)API,在下面的示例中,我們將配置集羣發現服務(CDS)和監聽器發現服務(LDS)來進行動態配置。

創建一個名爲 cds.yaml 的文件來配置集羣服務發現的數據,文件內容如下所示:

resources:
  - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
    name: targetCluster
    connect_timeout: 0.25s
    lb_policy: ROUND_ROBIN
    type: EDS
    eds_cluster_config:
      service_name: localservices
      eds_config:
        path: /etc/envoy/eds.yaml

此外,還需要創建一個名爲 lds.yaml 的文件來放置監聽器的配置,文件內容如下所示:

resources:
  - "@type": type.googleapis.com/envoy.config.listener.v3.Listener
    name: listener_0 # 監聽器的名稱
    address:
      socket_address:
        address: 0.0.0.0 # 監聽器的地址
        port_value: 10000 # 監聽器的端口

    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 # 調用7層的路由過濾器
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
              route_config:
                name: local_route
                virtual_hosts:
                  - name: backend
                    domains: ["*"]
                    routes:
                      - match:
                          prefix: "/"
                        route:
                          cluster: targetCluster

仔細觀察可以發現 cds.yamllds.yaml 配置文件的內容基本上和之前的靜態配置文件一致的。我們這裏只是將集羣和監聽器拆分到外部文件中去,這個時候我們需要修改 Envoy 的配置來引用這些文件,我們可以通過將 static_resources 更改爲 dynamic_resources 來進行配置。

重新新建一個 Envoy 配置文件,命名爲 envoy-dynamic.yaml,內容如下所示:

# envoy-dynamic.yaml
node:
  id: envoy_eds_id
  cluster: youdianzhishi_cluster
admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901
dynamic_resources: # 動態配置
  lds_config:
    path: "/etc/envoy/lds.yaml"
  cds_config:
    path: "/etc/envoy/cds.yaml"

然後使用上面的配置文件重新啓動一個新的 Envoy 代理,命令如下所示:

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

啓動完成後,同樣可以訪問 Envoy 代理來測試是否生效了:

$ curl localhostcurl localhost
<h1>This request was processed by host: 4ee790db09db</h1>
$ curl localhost
<h1>This request was processed by host: f9456b56f1ff</h1>

現在我們再基於上面配置的 CDSLDSEDS 的配置來動態添加一個新的集羣。比如添加一個名爲 newTargetCluster 的集羣,內容如下所示:

# cds.yaml
resources:
  - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
    name: targetCluster
    connect_timeout: 0.25s
    lb_policy: ROUND_ROBIN
    type: EDS
    eds_cluster_config:
      service_name: localservices
      eds_config:
        path: /etc/envoy/eds.yaml
  - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
    name: newTargetCluster
    connect_timeout: 0.25s
    lb_policy: ROUND_ROBIN
    type: EDS
    eds_cluster_config:
      service_name: localservices
      eds_config:
        path: /etc/envoy/eds-1.yaml

上面我們新增了一個新的集羣,對應的 eds_config 配置文件是 eds-1.yaml,所以我們同樣需要去創建該文件去配置新的端點服務數據,內容如下所示:

resources:
  - "@type": type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment
    cluster_name: localservices # 集羣的名稱,如果在集羣 eds_cluster_config 中指定,這將是 service_name 值。
    endpoints:
      - lb_endpoints:
          - endpoint:
              address:
                socket_address:
                  address: 192.168.215.5
                  port_value: 80
          - endpoint:
              address:
                socket_address:
                  address: 192.168.215.6
                  port_value: 80

這個時候新的集羣添加上了,但是還沒有任何路由來使用這個新集羣,我們可以在 lds.yaml 中去配置,將之前配置的 targetCluster 替換成 newTargetCluster

當然同樣我們這裏還需要運行兩個簡單的 HTTP 服務來作爲上游服務提供服務,執行如下所示的命令:

$ docker run -d cnych/docker-http-server; docker run -d cnych/docker-http-server;

這個時候 Envoy 應該就會自動重新加載並添加新的集羣,我們同樣可以執行 curl localhost 命令來驗證:

$ curl localhost
<h1>This request was processed by host: 5f43efcb9432</h1>
$ curl localhost
<h1>This request was processed by host: 4986b39d716f</h1>

可以看到已經變成了新的兩個端點數據了,證明我們這裏基於文件的 xDS 動態配置已經生效了。

基於 API 的動態配置

當在 Envoy 配置中定義了上游集羣后,Envoy 需要知道如何解析集羣成員,這就是服務發現。端點發現服務(EDS)是 Envoy 基於 gRPC 或者用來獲取集羣成員的 REST-JSON API 服務的 xDS 管理服務。在本節我們將學習如何使用 REST-JSON API 來配置端點的自動發現。

在前面的章節中,我們使用文件來定義了靜態和動態配置,在這裏我們將介紹另外一種方式來進行動態配置:API 動態配置

Envoy 項目在 Java 和 Golang 中都提供了 EDS 和其他服務發現的 gRPC 實現參考。

REST-JSON 服務

接下來我們將更改配置來使用 EDS,從而允許基於來自 REST-JSON API 服務的數據進行動態添加節點。

EDS 配置

下面是提供的一個 Envoy 配置的初始配置 envoy.yaml,文件內容如下所示:

# envoy.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 # 監聽器的名稱
      address:
        socket_address:
          address: 0.0.0.0 # 監聽器的地址
          port_value: 10000 # 監聽器的端口

      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 # 調用7層的路由過濾器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains: ["*"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: targetCluster

接下來需要添加一個 EDS 類型的集羣配置,並在 eds_config 中配置使用 REST API:

clusters:
  - name: targetCluster
    type: EDS
    connect_timeout: 0.25s
    eds_cluster_config:
      service_name: myservice
      eds_config:
        resource_api_version: V3 # xDS資源的API版本,支持 AUTO、V2、V3,如果未指定,默認爲 v2。
        api_config_source: # api_config_source的數據來自於 xDS API Server,即 Management Server。
          api_type: REST
          cluster_names: [xds_cluster] # 該字段只用於REST,cluster_names 的集羣必須是靜態定義的,其類型不能是EDS。
          transport_api_version: V3
          refresh_delay: 5s

上面配置中我們使用 api_config_source 來使用 REST API 的配置,其中 api_type 屬性指定了使用 REST API,cluster_names 屬性指定了使用 xds_cluster 這個集羣來獲取數據,refresh_delay 屬性指定了刷新間隔時間,這裏我們設置爲 5 秒。

然後需要定義 xds_cluster 的解析方式,這裏我們可以使用靜態配置:

- name: xds_cluster
  type: STATIC
  connect_timeout: 0.25s
  load_assignment:
    cluster_name: xds_cluster
    endpoints:
      - lb_endpoints:
          - endpoint:
              address:
                socket_address:
                  address: 192.168.0.112
                  port_value: 8080

然後同樣啓動一個 Envoy 代理實例來進行測試:

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

然後啓動一個如下所示的上游端點服務:

$ docker run -p 8081:8081 -d -e EDS_SERVER_PORT='8081' cnych/docker-http-server:v4

啓動完成後我們可以使用如下命令來測試上游的端點服務:

$ curl http://localhost:8081 -i
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 36
Server: Werkzeug/0.15.4 Python/2.7.16
Date: Fri, 27 Oct 2023 06:58:29 GMT

4caf19d5-6765-470b-a95c-a3615aea9796

現在我們啓動了 Envoy 代理和上游的服務集羣,但是由於我們這裏啓動的服務並不是 xds_cluster 中配置的服務,所以還沒有連接它們。這個時候我們去查看 Envoy 代理得日誌,可以看到如下所示的一些錯誤:

$ docker logs -f api-eds
[2023-10-27 08:43:48.964][1][warning][config] [source/extensions/config_subscription/rest/http_subscription_impl.cc:120] REST update for /v3/discovery:endpoints failed
......

啓動 EDS 服務

爲了讓 Envoy 獲取端點服務,我們需要啓動 xds_cluster,我們這裏將使用 python 實現的一個 REST-JSON 的管理服務,代碼如下所示:

# server.py
from flask import Flask, request, jsonify
import uuid

app = Flask(__name__)

# 存儲 endpoints 的數據庫
endpoints_db = {}

@app.route('/endpoints'methods=['POST'])
def add_endpoint():
    data = request.get_json()
    endpoint_id = str(uuid.uuid4())
    endpoints_db[endpoint_id] = data
    return jsonify({"id": endpoint_id})201


@app.route('/v3/discovery:endpoints'methods=['POST'])
def discovery_endpoints():
    xds_response = {  # 構造 xDS v3 EDS 響應格式
        "version_info""0",
        "resources"[
            {"@type""type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
             "cluster_name""myservice",
             "endpoints"[{"lb_endpoints"[{"endpoint"{"address"{"socket_address": endpoint}}}
                                              for endpoint in endpoints_db.values()]}]
             }
        ],
        "type_url""type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
        "nonce""0"
    }
    return jsonify(xds_response)

if __name__ == '__main__':
    app.run(host="0.0.0.0"port=8080, debug=True)

上面的代碼中我們使用 Flask 實現了一個 /v3/discovery:endpoints 的 POST 接口,這個是 Envoy 用來請求發現 Endpoints 端點的接口,需要注意的是該接口必須返回 DiscoveryResponse 協議格式的數據,我們這裏就是組裝一個 ClusterLoadAssignment

我們這裏直接在本地啓動(當然也可以打包成 Docker 鏡像在容器中啓動):

$ python server.py
 * Serving Flask app 'server'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8080
 * Running on http://192.168.0.112:8080
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 131-555-179

要注意前面配置的 xds_cluster 的地址和端口要和上面的服務一致,並要能夠訪問到。

然後我們就可以將上游的服務配置添加到 EDS 服務中去了,這樣可以讓 Envoy 來自動發現上游服務。上面的管理服務中我們定義了一個 /endpoints 的添加端點的接口,我們只需要將要添加的端點提交給這個接口即可,然後在發現接口裏面會自動獲取添加的端點,然後 Envoy 就可以動態感知到了。

$ curl --location --request POST 'http://localhost:8080/endpoints' \
--header 'Content-Type: application/json' \
--data-raw '{
    "address": "192.168.215.7",
    "port_value": 8081
}'

由於我們已經啓動了上面註冊的上游服務,所以現在我們可以通過 Envoy 代理訪問到它了:

$ curl -i http://localhost
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 36
server: envoy
date: Fri, 27 Oct 2023 09:02:07 GMT
x-envoy-upstream-service-time: 8

4caf19d5-6765-470b-a95c-a3615aea9796

接下來我們在上游集羣中運行更多的節點,並調用 API 來進行動態註冊,使用如下所示的命令來向上遊集羣再添加 4 個節點:

for i in 8082 8083 8084 8085
  do
    docker run -d -e EDS_SERVER_PORT=$i cnych/docker-http-server:v4;
    sleep .5
done

然後將上面的 4 個節點註冊到 EDS 服務上面去,同樣使用如下所示的 API 接口調用:

$ curl --location --request POST 'http://localhost:8080/endpoints' \
--header 'Content-Type: application/json' \
--data-raw '{
    "address": "192.168.215.8",
    "port_value": 8082
}'
$ curl --location --request POST 'http://localhost:8080/endpoints' \
--header 'Content-Type: application/json' \
--data-raw '{
    "address": "192.168.215.9",
    "port_value": 8083
}'
$ curl --location --request POST 'http://localhost:8080/endpoints' \
--header 'Content-Type: application/json' \
--data-raw '{
    "address": "192.168.215.10",
    "port_value": 8084
}'
$ curl --location --request POST 'http://localhost:8080/endpoints' \
--header 'Content-Type: application/json' \
--data-raw '{
    "address": "192.168.215.11",
    "port_value": 8085
}'

註冊成功後,我們可以通過如下所示的命令來驗證網絡請求是否與註冊的節點之間是均衡的:

while true; do curl http://localhost; sleep .5; printf '\n'; done
2a73139d-5929-4224-a227-a1aa560162df
4caf19d5-6765-470b-a95c-a3615aea9796
4395d85c-a216-46e4-bed7-04cc122c1903
9a22d774-62aa-47cc-bc35-c592015e5580
4caf19d5-6765-470b-a95c-a3615aea9796
2a73139d-5929-4224-a227-a1aa560162df
......

根據上面的輸出結果可以看到每次請求的服務是不同的響應,我們一共註冊了 5 個端點服務。

到這裏我們就實現了基於 REST-JSON 方式的 EDS 動態配置了,當然在實際使用的時候,更多的時候會使用 gRPC 的方式來實現管理服務,這樣可以實現流式的數據傳輸,更加高效,可以查看官方提供的 go-control-plane 示例瞭解如何實現(https://github.com/envoyproxy/go-control-plane/blob/main/internal/example/server.go)。

gRPC xDS 服務相比 REST-JSON xDS 服務,通常更爲複雜一些,因爲 gRPC 是基於 HTTP/2 的,並且使用 Protocol Buffers 作爲序列化協議。

首先我們需要定義一個 gRPC 服務來實現 xDS API。可以從 Envoy 的 data-plane-api(https://github.com/envoyproxy/data-plane-api/blob/main/envoy/service/endpoint/v3/eds.proto) 獲取 .proto 文件,然後可以使用 protoc 編譯器生成 Python 代碼,然後就可以去實現具體的業務邏輯了,比如 istio 就類似這種方式。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/oojVct47nMe301KSNveZzA