您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

Envoy 基于文件和 API 的动态配置方式

时间:2023-10-30 14:36:09  来源:微信公众号  作者:k8s技术圈

Envoy 基于文件和 API 的动态配置方式

前面我们和大家学习了 Envoy 的基础知识,使用静态配置来认识了 Envoy,但实际上 Envoy 的闪光点在于其动态配置,动态配置主要有基于文件和 API 两种方式。

基于文件的动态配置

Envoy 除了支持静态配置之外,还支持动态配置,而且动态配置也是 Envoy 重点关注的功能,本节我们将学习如何将 Envoy 静态配置转换为动态配置,从而允许 Envoy 自动更新。

Envoy 动态配置

前面的章节中,我们都是直接使用的静态配置,但是当我们需要更改配置的时候就比较麻烦了,需要重启 Envoy 代理才会生效。要解决这个问题,我们可以将静态配置更改成动态配置,当我们使用动态配置的时候,更改了配置,Envoy 将会自动去重新加载配置。

Envoy 支持不同的模块进行动态配置,可配置的有如下几个 API(统称为 xDS):

  • EDS:端点发现服务(EDS)可以让 Envoy 自动发现上游集群的成员,这使得我们可以动态添加或者删除处理流量请求的服务。
  • CDS:集群发现服务(CDS)可以让 Envoy 通过该机制自动发现在路由过程中使用的上游集群。
  • RDS:路由发现服务(RDS)可以让 Envoy 在运行时自动发现 HTTP 连接管理过滤器的整个路由配置,这可以让我们来完成诸如动态更改流量分配或者蓝绿发布之类的功能。
  • VHDS:虚拟主机发现服务(VHDS)允许根据需要与路由配置本身分开请求属于路由配置的虚拟主机。该 API 通常用于路由配置中有大量虚拟主机的部署中。
  • SRDS:作用域路由发现服务(SRDS)允许将路由表分解为多个部分。该 API 通常用于具有大量路由表的 HTTP 路由部署中。
  • LDS:监听器发现服务(LDS)可以让 Envoy 在运行时自动发现整个监听器。
  • SDS:密钥发现服务(SDS)可以让 Envoy 自动发现监听器的加密密钥(证书、私钥等)以及证书校验逻辑(受信任的根证书、吊销等)。
  • RTDS:运行时发现服务 (RTDS) API 允许通过 xDS API 获取运行时层。这可以通过文件系统层进行补充或改善。
  • ECDS:扩展配置发现服务 (ECDS) API 允许独立于侦听器提供扩展配置(例如 HTTP 过滤器配置)。当构建更适合与主控制平面分离的系统(例如 WAF、故障测试等)时,这非常有用。
  • ADS:EDS、CDS 等都是单独的服务,具有不同的 REST/gRPC 服务名称,例如 StreamListeners、StreamSecrets。对于希望强制资源按照不同类型的顺序到达 Envoy 的用户来说,有聚合的 xDS,这是一个单独的 gRPC 服务,在一个 gRPC 流中携带所有资源类型。(ADS 只支持 gRPC)。

动态资源,是指由 Envoy 通过 xDS 协议发现所需要的各项配置的机制,相关的配置信息保存于称之为管理服务器(Management Server )的主机上,经由 xDS API 向外暴露;下面是一个纯动态资源的基础配置框架

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

xDS API 为 Envoy 提供了资源的动态配置机制,它也被称为 Data Plane API。

Envoy 基于文件和 API 的动态配置方式

xDS API

Envoy 支持三种类型的配置信息的动态发现机制,相关的发现服务及其相应的 API 联合起来 称为 xDS API。

  • 基于文件系统发现:指定要监视的文件系统路径
  • gRPC 服务:启动 gRPC 流。

  • REST 服务:轮询 REST-JSON URL。
  • 通过查询一到多个管理服务器发现:通过 DiscoveryRequest 协议报文发送请求,并要求服务方以 DiscoveryResponse 协议报文进行响应。

v3 版本的 xDS 支持如下几种资源类型:

  • envoy.config.listener.v3.Listener
  • envoy.config.route.v3.RouteConfiguration
  • envoy.config.route.v3.ScopedRouteConfiguration
  • envoy.config.route.v3.VirtualHost
  • envoy.config.cluster.v3.Cluster
  • envoy.config.endpoint.v3.ClusterLoadAssignment
  • envoy.extensions.transport_sockets.tls.v3.Secret
  • envoy.service.runtime.v3.Runtime

Envoy 对 xDS API 的管理由后端服务器实现,包括 LDS、CDS、RDS、SRDS(Scoped Route)、VHDS (Virtual Host)、EDS、SDS、RTDS(Runtime )等。

  • 所有这些 API 都提供了最终的一致性,并且彼此间不存在相互影响;
  • 部分更高级别的操作(例如执行服务的 A/B 部署)需要进行排序以防止流量被丢弃,因此,基于一个管理服务器提供多类 API 时还需要使用聚合发现服务(ADS )API。
  • ADS API 允许所有其他 API 通过来自单个管理服务器的单个 gRPC 双向流进行编组,从而允许对操作进行确定性排序

接下来我们先更改配置来使用 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

除了 id 和 cluster 之外,我们还可以配置基于区域的一些位置信息来进行声明,比如 region、zone、sub_zone 等。

EDS 配置

端点发现服务 EDS 是基于 gRPC 或 REST-JSON API 服务器的 xDS 管理服务器,Envoy 使用它来获取集群成员。集群成员在 Envoy 术语中称为“端点”。对于每个集群,Envoy 从发现服务获取端点,EDS 是首选的服务发现机制:

  • Envoy 明确了解每个上游主机(相对于通过 DNS 解析的负载均衡器进行路由),并且可以做出更智能的负载均衡决策。
  • 每个主机的发现 API 响应中携带的额外属性告知 Envoy 主机的负载均衡权重、金丝雀状态、区域等。这些附加属性由 Envoy 网格在负载均衡、统计收集等过程中全局使用。

接下来我们就可以来定义 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

现在我们将上面的静态配置转换成动态配置,首先需要转换为基于 EDS 的 eds_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 资源,则可能会使用以下配置:

  • 将 xds.yaml 存储在 ConfigMap 内。
  • 将 ConfigMap 挂载到 /config_map/xds
  • 配置路径 /config_map/xds/xds.yaml
  • 配置监视目录 /config_map/xds

上述配置将确保 Envoy 监视所属目录的移动,这是由于 Kubernetes 在原子更新期间管理 ConfigMap 符号链接的方式而必需的。

上游的服务器 192.168.215.3 和 192.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.yaml 和 lds.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>

现在我们再基于上面配置的 CDS、LDS、EDS 的配置来动态添加一个新的集群。比如添加一个名为 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/ target=_blank class=infotextkey>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 就类似这种方式。



Tags:Envoy   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Istio Envoy 配置解读,看这篇就够了
前面我们创建了一个 Gateway 和 VirtualService 对象,用来对外暴露应用,然后我们就可以通过 ingressgateway 来访问 Bookinfo 应用了。那么这两个资源对象是如何实现的呢?Gatew...【详细内容】
2023-11-07  Search: Envoy  点击:(128)  评论:(0)  加入收藏
Envoy 基于文件和 API 的动态配置方式
前面我们和大家学习了 Envoy 的基础知识,使用静态配置来认识了 Envoy,但实际上 Envoy 的闪光点在于其动态配置,动态配置主要有基于文件和 API 两种方式。基于文件的动态配置Env...【详细内容】
2023-10-30  Search: Envoy  点击:(306)  评论:(0)  加入收藏
Envoy 对决 Nginx:现代反向代理之争,谁将称雄?
EnvoyEnvoy 是一个专为云原生应用设计的开源边缘和服务代理。下面分别介绍 Envoy 的优点和缺点:优点: 适应云原生应用:Envoy 专注于云原生应用,支持动态、微服务导向的环境,与容...【详细内容】
2023-10-09  Search: Envoy  点击:(289)  评论:(0)  加入收藏
“轻量级的”Istio,微软开源了一个基于 Envoy 的服务网格
微软宣布了一个新的开源项目,即“开放服务网格(Open Service Mesh,OSM)”。它是一种在 Kubernetes 上运行的轻量级且可扩展的服务网格;旨在作为 Service Mesh Interface(SMI)规范的...【详细内容】
2020-08-10  Search: Envoy  点击:(241)  评论:(0)  加入收藏
云原生时代的流量入口:Envoy Gateway
流量入口代理作为互联网系统的门户组件,具备众多选型:从老牌代理 HAProxy、Nginx,到微服务 API 网关 Kong、Zuul,再到容器化 Ingress 规范与实现,不同选型间功能、性能、可扩展性...【详细内容】
2020-07-30  Search: Envoy  点击:(496)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(5)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(12)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(8)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(10)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(8)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(5)  评论:(0)  加入收藏
站内最新
站内热门
站内头条