Envoy 中的 xDS REST 和 gRPC 协议详解

Envoy 经过查询文件或管理服务器来动态发现资源。归纳地讲,对应的发现服务及其相应的 API 被称做 xDS。Envoy 经过订阅(subscription)方式来获取资源,如监控指定路径下的文件、启动 gRPC 流或轮询 REST-JSON URL。后两种方式会发送 DiscoveryRequest 请求消息,发现的对应资源则包含在响应消息 DiscoveryResponse 中。下面,咱们将具体讨论每种订阅类型。node

文件订阅

发现动态资源的最简单方式就是将其保存于文件,并将路径配置在 ConfigSource 中的 path 参数中。Envoy 使用 inotify(Mac OS X 上为 kqueue)来监控文件的变化,在文件被更新时,Envoy 读取保存的 DiscoveryResponse 数据进行解析,数据格式能够为二进制 protobuf、JSON、YAML 和协议文本等。git

译者注:core.ConfigSource 配置格式以下:github

{
  "path": "...",
  "api_config_source": "{...}",
  "ads": "{...}"
}复制代码

文件订阅方式可提供统计数据和日志信息,可是缺乏 ACK/NACK 更新的机制。若是更新的配置被拒绝,xDS API 则继续使用最后一个的有效配置。json

gRPC 流式订阅

单资源类型发现

每一个 xDS API 能够单独配置 ApiConfigSource,指向对应的上游管理服务器的集群地址。每一个 xDS 资源类型会启动一个独立的双向 gRPC 流,可能对应不一样的管理服务器。API 交付方式采用最终一致性。能够参考后续聚合服务发现(ADS) 章节来了解必要的显式控制序列。bootstrap

译者注:core.ApiConfigSource 配置格式以下:api

{
  "api_type": "...",
  "cluster_names": [],
  "grpc_services": [],
  "refresh_delay": "{...}",
  "request_timeout": "{...}"
}复制代码

类型 URL

每一个 xDS API 都与给定的资源的类型存在 1:1 对应。关系以下:安全

的概念以下所示,其采用 type.googleapis.com/<resource type> 的形式,例如 CDS 对应于 type.googleapis.com/envoy.api.v2.Cluster。在 Envoy 的请求和管理服务器的响应中,都包括了资源类型 URL。

ACK/NACK 和版本

每一个 Envoy 流以 DiscoveryRequest 开始,包括了列表订阅的资源、订阅资源对应的类型 URL、节点标识符和空的 version_info。EDS 请求示例以下:

version_info:
node: { id: envoy }
resource_names:
- foo
- bar
type_url: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
response_nonce:复制代码

管理服务器可马上或等待资源就绪时发送 DiscoveryResponse做为响应,示例以下:

version_info: X
resources:
- foo ClusterLoadAssignment proto encoding
- bar ClusterLoadAssignment proto encoding
type_url: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
nonce: A复制代码

Envoy 在处理 DiscoveryResponse 响应后,将经过流发送一个新的请求,请求包含应用成功的最后一个版本号和管理服务器提供的 nonce。若是本次更新已成功应用,则 version_info 的值设置为 X,以下序列图所示:

在此序列图及后续中,将统一使用如下缩写格式:

  • DiscoveryRequest: (V=version_info,R=resource_names,N=response_nonce,T=type_url)

  • DiscoveryResponse: (V=version_info,R=resources,N=nonce,T=type_url)

译者注:在信息安全中,Nonce是一个在加密通讯只能使用一次的数字。在认证协议中,它每每是一个随机伪随机数,以免重放攻击。Nonce也用于流密码以确保安全。若是须要使用相同的密钥加密一个以上的消息,就须要Nonce来确保不一样的消息与该密钥加密的密钥流不一样。(引用自维基百科)在本文中nonce是每次更新的数据包的惟一标识。

版本为 Envoy 和管理服务器提供了共享当前应用配置的概念和经过 ACK/NACK 来进行配置更新的机制。若是 Envoy 拒绝配置更新 X,则回复 error_detail 及前一个的版本号,在当前状况下为空的初始版本号,error_detail 包含了有关错误的更加详细的信息:

后续,API 更新可能会在新版本 Y 上成功:

每一个流都有本身的版本概念,但不存在跨资源类型的共享版本。在不使用 ADS 的状况下,每一个资源类型可能具备不一样的版本,由于 Envoy API 容许指向不一样的 EDS/RDS 资源配置并对应不一样的 ConfigSources

什么时候发送更新

管理服务器应该只向 Envoy 客户端发送上次 DiscoveryResponse 后更新过的资源。Envoy 则会根据接受或拒绝 DiscoveryResponse 的状况,当即回复包含 ACK/NACK 的 DiscoveryRequest 请求。若是管理服务器每次发送相同的资源集结果,而不是根据其更新状况,则会致使 Envoy 和管理服务器通信效率大打折扣。

在同一个流中,新的 DiscoveryRequests 将取代此前具备相同的资源类型 DiscoveryRequest 请求。这意味着管理服务器只须要响应给定资源类型最新的 DiscoveryRequest 请求便可。

资源提示

DiscoveryRequest 中的 resource_names 信息做为资源提示出现。一些资源类型,例如 ClusterListener 将使用一个空的 resource_names,由于 Envoy 须要获取管理服务器对应于节点标识的全部 Cluster(CDS)和 Listener(LDS)。对于其余资源类型,如 RouteConfigurations(RDS)和 ClusterLoadAssignments(EDS),则遵循此前的 CDS/LDS 更新,Envoy 可以明确地枚举这些资源。

LDS/CDS 资源提示信息将始终为空,而且指望管理服务器的每一个响应都提供 LDS/CDS 资源的完整状态。缺席的 ListenerCluster 将被删除。

对于 EDS/RDS,管理服务器并不须要为每一个请求的资源进行响应,并且还可能提供额外未请求的资源。resource_names 只是一个提示。Envoy 将默默地忽略返回的多余资源。若是请求的资源中缺乏相应的 RDS 或 EDS 更新,Envoy 将保留对应资源的最后的值。管理服务器可能会依据 DiscoveryRequestnode 标识推断其所需的 EDS/RDS 资源,在这种状况下,提示信息可能会被丢弃。从相应的角度来看,空的 EDS/RDS DiscoveryResponse 响应其实是代表在 Envoy 中为一个空的资源。

ListenerCluster 被删除时,其对应的 EDS 和 RDS 资源也须要在 Envoy 实例中删除。为使 EDS 资源被 Envoy 已知或跟踪,就必须存在应用过的 Cluster 定义(如经过 CDS 获取)。RDS 和 Listeners 之间存在相似的关系(如经过 LDS 获取)。

对于 EDS/RDS ,Envoy 能够为每一个给定类型的资源生成不一样的流(如每一个 ConfigSource 都有本身的上游管理服务器的集群)或当指定资源类型的请求发送到同一个管理服务器的时候,容许将多个资源请求组合在一块儿发送。虽然能够单个实现,但管理服务器应具有处理每一个给定资源类型中对单个或多个 resource_names 请求的能力。下面的两个序列图对于获取两个 EDS 资源都是有效的 {foo,bar}

资源更新

如上所述,Envoy 可能会更新 DiscoveryRequest 中出现的 resource_names 列表,其中 DiscoveryRequest 是用来 ACK/NACK 管理服务器的特定的 DiscoveryResponse 。此外,Envoy 后续可能会发送额外的 DiscoveryRequests ,用于在特定 version_info 上使用新的资源提示来更新管理服务器。例如,若是 Envoy 在 EDS 版本 X 时仅知道集群 foo,但在随后收到的 CDS 更新时额外获取了集群 bar ,它可能会为版本 X 发出额外的 DiscoveryRequest 请求,并将 {foo,bar} 做为请求的 resource_names

这里可能会出现竞争情况;若是 Envoy 在版本 X 上发布了资源提示更新请求,但在管理服务器处理该请求以前发送了新的版本号为 Y 的响应,针对 version_infoX 的版本,资源提示更新可能会被解释为拒绝 Y 。为避免这种状况,经过使用管理服务器提供的 nonce,Envoy 可用来保证每一个 DiscoveryRequest 对应到相应的 DiscoveryResponse

管理服务器不该该为含有过时 nonceDiscoveryRequest 发送 DiscoveryResponse 响应。在向 Envoy 发送的 DiscoveryResponse 中包含了的新 nonce ,则此前的 nonce 将过时。在资源新版本就绪以前,管理服务器不须要向 Envoy 发送更新。同版本的早期请求将会过时。在新版本就绪时,管理服务器可能会处理同一个版本号的多个 DiscoveryRequests请求。

上述资源更新序列代表 Envoy 并不能期待其发出的每一个 DiscoveryRequest 都获得 DiscoveryResponse 响应。

最终一致性考虑

因为 Envoy 的 xDS API 采用最终一致性,所以在更新期间可能致使流量被丢弃。例如,若是经过 CDS/EDS 仅获取到了集群 X,并且 RouteConfiguration 引用了集群 X;在 CDS/EDS 更新集群 Y 配置以前,若是将 RouteConfiguration 将引用的集群调整为 Y ,那么流量将被吸入黑洞而丢弃,直至集群 Y 被 Envoy 实例获取。

对某些应用程序,可接受临时的流量丢弃,客户端重试或其余 Envoy sidecar 会掩盖流量丢弃。那些对流量丢弃不能容忍的场景,能够经过如下方式避免流量丢失,CDS/EDS 更新同时携带 XY ,而后发送 RDS 更新从 X 切换到 Y ,此后发送丢弃 X 的 CDS/EDS 更新。

通常来讲,为避免流量丢弃,更新的顺序应该遵循 make before break 模型,其中

  • 必须始终先推送 CDS 更新(若是有)。

  • EDS 更新(若是有)必须在相应集群的 CDS 更新后到达。

  • LDS 更新必须在相应的 CDS/EDS 更新后到达。

  • 与新添加的监听器相关的 RDS 更新必须在最后到达。

  • 最后,删除过时的 CDS 集群和相关的 EDS 端点(再也不被引用的端点)。

若是没有新的集群/路由/监听器或者容许更新时临时流量丢失的状况下,能够独立推送 xDS 更新。请注意,在 LDS 更新的状况下,监听器须在接收流量以前被预热,例如如其配置了依赖的路由,则先需先从 RDS 进行获取。添加/删除/更新集群信息时,集群也须要进行预热。另外一方面,若是管理平面确保路由更新时所引用的集群已经准备就绪,路由能够不用预热。

聚合服务发现(ADS)

当管理服务器进行资源分发时,经过上述保证交互顺序的方式来避免流量丢弃是一项颇有挑战的工做。ADS 容许单一管理服务器经过单个 gRPC 流,提供全部的 API 更新。配合仔细规划的更新顺序,ADS 可规避更新过程当中流量丢失。使用 ADS,在单个流上可经过类型 URL 来进行复用多个独立的 DiscoveryRequest/DiscoveryResponse 序列。对于任何给定类型的 URL,以上 DiscoveryRequestDiscoveryResponse 消息序列都适用。 更新序列可能以下所示:

每一个 Envoy 实例可以使用单独的 ADS 流。

最小化 ADS 配置的 bootstrap.yaml 片断示例以下:

node:
 id: <node identifier>
dynamic_resources:
 cds_config: {ads: {}}
 lds_config: {ads: {}}
 ads_config:
 api_type: GRPC
 grpc_services:
 envoy_grpc:
 cluster_name: ads_cluster
static_resources:
 clusters:
 - name: ads_cluster
 connect_timeout: { seconds: 5 }
 type: STATIC
 hosts:
 - socket_address:
 address: <ADS management server IP address>
 port_value: <ADS management server port>
 lb_policy: ROUND_ROBIN
 http2_protocol_options: {}
admin:
  ...
复制代码

增量 xDS

增量 xDS 是可用于容许的 ADS、CDS 和 RDS 单独 xDS 端点:

  • xDS 客户端对跟踪资源列表进行增量更新。这支持 Envoy 按需/惰性地请求额外资源。例如,当与未知集群相对应的请求到达时,可能会发生这种状况。

  • xDS 服务器能够增量更新客户端上的资源。这支持 xDS 资源可伸缩性的目标。管理服务器只需交付更改的单个集群,而不是在修改单个集群时交付全部上万个集群。

xDS 增量会话始终位于 gRPC 双向流的上下文中。这容许 xDS 服务器可以跟踪到链接的 xDS 客户端的状态。xDS REST 版本不支持增量。

在增量 xDS 中,nonce 字段是必需的,用于匹配 IncrementalDiscoveryResponse 关联的 ACK 或 NACK IncrementalDiscoveryRequest。可选地,存在响应消息级别的 system_version_info,但仅用于调试目的。

IncrementalDiscoveryRequest 可在如下 3 种状况下发送:

  1. xDS 双向 gRPC 流的初始消息。

  2. 做为对先前的 IncrementalDiscoveryResponse 的 ACK 或 NACK 响应。在这种状况下,response_nonce 被设置为响应中的 nonce 值。ACK 或 NACK 由可由 error_detail 字段是否出现来区分。

  3. 客户端自发的 IncrementalDiscoveryRequest。此场景下能够采用动态添加或删除被跟踪的 resource_names 集。这种场景下,必须忽略 response_nonce

在第一个示例中,客户端链接并接收它的第一个更新并 ACK。第二次更新失败,客户端发送 NACK 拒绝更新。xDS客户端后续会自发地请求 “wc” 相关资源。

在从新链接时,支持增量的 xDS 客户端可能会告诉服务器其已知资源从而避免经过网络从新发送它们。

REST-JSON 轮询订阅

单个 xDS API 可对 REST 端点进行的同步(长)轮询。除了无持久流与管理服务器交互外,消息顺序与上述类似。在任什么时候间点,只存在一个未完成的请求,所以响应消息中的 nonce 在 REST-JSON 中是可选的。DiscoveryRequestDiscoveryResponse 的消息编码遵循 JSON 变换 proto3 规范。ADS 不支持 REST-JSON 轮询。

当轮询期间设置为较小的值时,则能够等同于长轮询,这时要求避免发送 DiscoveryResponse除非对请求的资源发生了更改

本文转自:www.servicemesher.com/blog/envoy-…

相关文章
相关标签/搜索