在2019年5月,CNCF 筹建通用数据平面 API 工作组(Universal Data Plane API Working Group / UDPA-WG),以制定数据平面的标准 API。
当时我写了一个博客文章 “CNCF 正在筹建通用数据平面 API 工作组,以制定数据平面的标准 API” 对此进行了介绍。当时 UDPA 还处于非常早期的筹备阶段,信息非常的少。
现在9个月过去了,我最近收集并整理了一下 UDPA 目前的情况和信息,给大家介绍一下 UDPA 目前最新的进展(截止2020年2月24日)。另外蚂蚁金服开源的云原生网络代理 MOSN 目前已经支持 xDS v2 API,后面也会逐步向着 UDPA 的方向去演进,兼容标准 Istio,感兴趣的读者可以去了解下。
MOSN:https://github.com/mosn/mosn
首先快速介绍一下什么是 UDPA:
UDPA 的目标,援引自 https://github.com/cncf/udpa 的描述:
通用数据平面 API 工作组(UDPA-WG)的目标是召集对数据平面代理和负载均衡器的通用控制和配置 API 感兴趣的业界人士。
UDPA 的愿景,同样援引:
通用数据平面 API(UDPA)的愿景在 https://blog.envoyproxy.io/the-universal-data-plane-api-d15cec7a 中阐明。我们将寻求一组 API,它们为 L4/L7 数据平面配置提供事实上的标准,类似于 SDN 中 L2/L3/L4 的 OpenFlow 所扮演的角色。 这些 API 将在 proto3 中规范定义,并通过定义良好的、稳定 API 版本控制策略,从现有的 Envoy xDS API 逐步演进。API 将涵盖服务发现、负载均衡分配、路由发现、监听器配置、安全发现、负载报告、运行状况检查委托等。 我们将对 API 进行改进和成型,以支持客户端 lookaside 负载均衡(例如 gRPC-LB),Envoy 之外的数据平面代理,硬件 LB,移动客户端以及其他范围。我们将努力尽可能与供应商和实现无关,同时坚持支持已投入生产的 UDPA 的项目(到目前为止,Envoy 和 gRPC-LB)。
对 UDPA 感兴趣的同学,可以通过以下两个途径进一步深入了解:
在展开 UDPA 的细节之前,有必要先解释清楚 UDPA 和 xDS 的关系,因为这对理解 UDPA 会有很大帮助。
在2019年11月的 EnvoyCon 上,Envoy 的开发者,也是目前 UDPA 最主要的负责人之一,来自 Google 的 Harvey Tuch,有一个演讲非常详细而清晰的解答了这个问题,这个演讲的标题是:“The Universal Dataplane API (UDPA): Envoy’s Next Generation APIs”。
备注:这里我直接援引这份演讲的部分内容,以下两张图片均出自 这份演讲的PPT ,鸣谢 Harvey。下图展示了近年来 xDS 协议的演进历程和未来规划:
简单总结说:xDS 将逐渐向 UDPA 靠拢,未来将基于 UDPA 。
下图则展示了 Envoy 在 xDS 版本支持上的时间线:
目前看这个计划在执行时稍微有一点点延误,原计划于2019年年底推出的 v3 的 stable 版本实际上是在1月中定稿的。(备注:具体可参考 Envoy PR api: freeze v3 API )。然后目前正在广泛使用的 v2 API 将被标记为 depreated。而且在2020年底,v3 API 预计被 v4 API 取代(注意 v4 API 将会是基于 UDPA),而目前我们最熟悉的 v2 API 将计划在2020年底移除,不再支持!
上图也展示了未来 xDS 协议的大版本演进和更替的方式,总的来说规律是这样:
所谓 “长江后浪推前浪,前浪死在沙滩上”,又或者说,“江山代有新版出,各领风骚12个月”。
备注:Envoy 具体的稳定 API 版本控制策略,可以参见 Envoy 的设计文档 “Stable Envoy API versioning” ,不过这个文档长的有点过分,嫌长的同学可以直接看这个文档的缩减版本 API versioning guidelines。
言归正传,我们来看一下 UDPA 目前的最新进展。
从 https://github.com/cncf/udpa ,可以看到目前 UDPA 中已经定义好的部分 API 内容:
类型定义
目前只定义了一个类型 TypedStruct。
TypedStruct 包含任意 JSON 序列化后的 protocol buffer 消息以及一个描述序列化消息类型的URL。这与 google.protobuf.Any 非常相似,它使用 google.protobuf.Struct 作为值,而不是使用 protocol buffer 二进制。
message TypedStruct {
// 用于唯一标识序列化 protocol buffer 消息的类型的URL
// 这与 google.protobuf.Any 中描述的语义和格式相同:
// https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto
string type_url = 1;
// 上述指定类型的JSON表示形式。
google.protobuf.Struct value = 2;
}
TypedStruct 定义的背景是:如何在 protocol buffer 的静态类型报文中嵌入一个不透明的配置。这是一个普遍需求,涉及 google.protobuf.Any 和 google.protobuf.Struct 的差别和权衡使用。具体内容请见我之前翻译的博客文章 “[译] 动态可扩展性和Protocol Buffer”,对此做了非常好的介绍和分析讨论。
TypedStruct 可以说是到目前为止对此需求的最佳实践,算是为这一话题正式画上了句号。
数据定义
数据定义也只定义了一个数据 OrcaLoadReport。
其中 ORCA 是 Open Request Cost Aggregation 的缩写,OrcaLoadReport 用于提交请求开销汇总的负载报告。
ORCA 的简短介绍:
如今,在 Envoy 中,可以通过考虑后端负载(例如 CPU)的本地或全局知识来做出简单的负载均衡决策。更复杂的负载均衡决策可能需要借助特定于应用的知识,例如队列深度,或组合多个指标。这对于可能在多个维度上受到资源限制的服务(例如,CPU 和内存都可能成为瓶颈,取决于所应用的负载和执行环境,无法确定是哪个先触及瓶颈)以及这些维度不在预定类别中的位置时很有用(例如,资源可能是“池中的可用线程数”,磁盘 IOPS 等)。
有关 Orca 的更详细的信息,请见设计文档 Open Request Cost Aggregation (ORCA) 。
目前 Envoy 正在实现对 ORCA 的支持,然后这个特性被作为 UPDA 标准的一部分直接在 UDPA API 中定义。以下为 OrcaLoadReport 定义,可以看到包含有 CPU/内存的利用率和 RPS 信息:
message OrcaLoadReport {
// CPU利用率表示为可用CPU资源的一部分。应该来自最新的样本或测量。
double cpu_utilization = 1 [(validate.rules).double.gte = 0, (validate.rules).double.lte = 1];
// 内存利用率表示为可用内存资源的一部分。应该来自最新的样本或测量。
double mem_utilization = 2 [(validate.rules).double.gte = 0, (validate.rules).double.lte = 1];
// 端点已服务的总RPS。应该涵盖端点负责的所有服务。
uint64 rps = 3;
...
}
服务定义
服务定义依然也只定义了一个服务 OpenRcaService。
OpenRcaService 是一个带外(Out-of-band/OOB)负载报告服务,它不在请求路径上。OpenRcaService 定期以足够的频率对报告进行采样,以提供与请求的关联。OOB 报告弥补了带内(in-band)报告的局限性。
service OpenRcaService {
rpc StreamCoreMetrics(OrcaLoadReportRequest) returns (stream udpa.data.orca.v1.OrcaLoadReport);
}
注解定义
UDPA 目前定义了四个注解(Annotation):
还有一个 ProtodocAnnotation 在提出设计后,存在分歧,暂时还没有正式加入 UDPA。这个注解的目的是标记当前尚未实现的 UDPA 消息;
UDPA API 总结
从上面列出的 UDPA API 列表可以看到,目前 UDPA 中正式推出的 API 内容非常的少,也就:
考虑到 UDPA 推出的时间是 2019年5月份,迄今有9个月的时间,这个进展有些出乎意料。
翻了一遍 https://github.com/cncf/udpa 上的内容,包括所有的 commit 和 PR ,发现活跃的开发者主要是两位同学:Google 的 htuch 和 Tetrate公司的 Lizan。然后 cncf/UDPA 项目的 star 数量也非常低,才 55 个 star,可以认为社区基本上没什么人关注。
但是,稍后当我看到 UDPA 的设计文档时,才发现原来 UDPA 的精华都在设计中,只是进度原因还未能正式出成型的 API。
我们来重点看一下 UDPA 的设计,主要的设计文档有两份:
UDPA 对此的解释是:
Envoy v2 xDS API 当前正在转向通用数据平面 API(Universal Dataplane API/UDPA)。重点是传输协议与数据模型的关注点分离。
关于传输协议与数据模型的关注点分离,一个典型的例子是“集装箱运输机制”(类比 UDPA-TP )和 “集装箱中标准规格”(类比 UDPA-DM)。在 UDPA 的设计中,数据模型的定义和传输协议的实现是分离的,这意味着只要设计不同的数据模型,就可以重用一套统一的传输协议。因此,UDPA 的可扩展性就变得非常强大。
对此,我个人有些惊喜,因为去年年底我和彦林同学在商讨通过 MCP/xDS/UDPA 协议融合注册中心和控制平面时,就发现这三者的工作机制非常类似。考虑到后续可能会有各种不同的资源需要定义并在这个工作机制上做资源同步和分发,当时有过类似的想法,希望能把底层这套资源同步机制标准化,以便重用:
目前看来,UDPA-TP 已经在朝这个目标迈出了坚实的步伐。当然如果能再往前迈进一步就更好了:这个底层资源同步的工作机制,没有必要限制在 UDPA 的范畴,完全可以变成一个用途更加广泛的通用机制。
下面来详细看一下 UDPA-TP 和 UDPA-DM 的设计,以下内容来自 UDPA 的两份设计文档以及个人的解读。
UDPA-TP 设计
UDPA-TP 的设计文档,在开始部分列出了 UDPA-TP 的关键设计动机,具体包括:
以下是 UDPA 的术语,对于后面理解 UDPA 非常有帮助:
然后是和联邦相关的术语:
下图可以帮助理解这些术语:
(备注:图中可能有误,simple UDPA management server 和 Advanced UDPA management server 之间应该是 “UDPA-TP”, 而不应该是 “UDPA-Fed-TP”。)
UDPA-TP 传输协议提供了在管理服务器和 DPLB 客户端之间传输命名和版本化资源的方法,我们称这些实体为 UDPA-TP 端点。UDPA-TP 端点可以是客户端和管理服务器,也可以是两个管理服务器(例如,在联邦配置时)。上图说明了使用 UDPA 在 UDPA-TP 端点之间传送资源的各种方式。
其中,UDPA管理服务器分为两种:
而对应的 DPLB 客户端也分为两种:
简单 DPLB 虽然只能连接一台管理服务器,但是也是可以实现联邦的:简单 DPLB 连接的管理服务器可以实现联邦,然后为 DPLB 实现了间接联邦(备注:上图中的简单 DPLB 就是例子,两个 Advanced UDPA management server 之间做了联邦)。
UDPA-TP 设计解读
在解读 UDPA-TP 的设计之前,我们回顾一下 Istio 经典的组件和架构图,下面分别是 Istio 1.0 和 Istio 1.1 的架构图:
考虑到 Mixer 和 Citadel 两个组件和 UDPA 的关系相比没那么大, 我们重点看 Proxy / Pilot / Galley:
对照 UDPA-TP 的设计:
UDPA-TP 的设计目前应该还没有对应具体的实现产品,而且我也还没有找到 UDPA-Fed-TP 详细的 API 设计。资料来源太少,所以只能简单的做一些个人的初步解读:
对于 UDPA-TP 的设计,我个人有些不太理解,主要是对于联邦的使用场景上,我的疑虑在于:真的有这么复杂的场景吗?尤其是将联邦的功能引入到 DPLB,这必然会使得 xDS/UDPA 协议不得不为此提供联邦 API 支持,而 Envoy/MOSN 等的实现难度也要大为提升。因此,除非有特别强烈的需求和场景推动,否则最好能在复杂度和功能性之间做好平衡。
我个人更倾向于类似下面的设计:
总之,我个人更倾向于让 DPLB 和 Simple UDPA management server 保持简单和高性能,而将复杂功能交给 Advanced UDPA management server 。后续我会重点关注 UDPA 在联邦功能的实现,如有新进展尽量及时撰文分享。
UDPA 的资源定义和设计
在 UDPA-TP 的设计中,根据资源的来源,资源被划分为两类类型:
资源在定义时,将有三个重要属性:
以下是资源定义的实例(只是示意,暂时还没正式成为 UDPA API 的内容):
message Resource {
// 资源的名称,以区别于其他同类型的资源。
// 遵循反向DNS格式,例如 com.acme.foo/listener-a
string name = 1;
// 资源级别版本
string version = 2;
// 资源有效负载。
// 通过 Any 的 type URL 指定资源类型
google.protobuf.Any resource = 3;
// 资源的TTL。
// 此时间段后,资源将在DPLB上失效。
// 当管理服务器的连接丢失时,将支持资源的优雅降级,例如端点分配。
// 使用新的TTL接收到相同的资源 name/version/type 将不会导致除了刷新TTL之外的任何状态更改。
// 按需资源可能被允许过期,并且可能在TTL过期时被重新获取。
// TTL刷新消息中的resource字段可能为空,name/version/type用于标识要刷新的资源。
google.protobuf.Duration ttl = 4;
// 资源的出处(所有权,来源和完整性)。
Provenance origin_info = 5;
}
UDPA-TP 设计中的其他内容,如安全 / 错误处理 / 传输 / 用户故事 等,就不一一展开了,这些设计目前看离正式成为 API 还有点远。如有兴趣可以直接翻阅 UDPA-TP 的设计文档。
UDPA-DM 设计
UDPA 设计的一个核心内容就是将传输(TransPort)与数据模型(Date Model)的关注点分离,前面介绍了 UDPA-TP 的设计,可以说目前还在进行中,并未完全定型。
而 UDPA-DM 的设计,感觉进度上比 UDPA-TP 还要更早期,这多少有点出乎意料:原以为 UDPA 会基于 xDS 现有的成熟 API 定义,快速推出一套覆盖常见通用功能的 API ,甚至直接把 xDS 中的部分内容清理干净之后搬过来。但事实是:目前 UDPA-DM 中已经定义的 API 内容非常少,仅有 L7 Routing ,而且还在设计中,其他大家熟悉的 Listener / Cluster / Endpoint / Security / RatingLimit 等API都还没有看到。
而 UDPA-DM 的设计和实现方式,也因为资料较少而有些不够明朗。在 UDPA-DM 的设计文档的开头,有如下一段描述:
As a starting point, we recognize that the UDPA-DM is not required to be as expressive as any given DPLB client’s full set of capabilities, instead it should be possible to translate from UDPA-DM to various DPLB native configuration formats. We envisage UDPA-DM as a lingua franca that captures a large amount of useful functionality that a DPLB may provide, making it possible to build common control planes and ecosystems around UDPA capable DPLBs. 首先,我们认识到 UDPA-DM 不需要像任何已有的 DPLB 客户端那样,全部能力都具备表现力,而是应该可以从 UDPA-DM 转换为各种 DPLB 原生配置格式。我们将 UDPA-DM 设想为一种通用语言,它具备大量 DPLB 应该提供的有用的功能,从而有可能在支持 UDPA 的 DPLB 周围构建通用的控制平面和生态系统。 The UDPA-DM will be a living standard. We anticipate that it will initially cover some obvious common capabilities shared by DPLBs, while leaving other behaviors to proxy specific API fields. Over time, we expect that the UDPA-DM will evolves via a stable API versioning policy to accommodate functionalities as we negotiate a common representation. UDPA-DM 将成为事实标准。我们期望它最初将涵盖 DPLB 共有的一些显而易见的通用功能,同时将其他行为留给代理特定的API 字段。随着时间的推移,我们期望 UDPA-DM 将通过稳定的 API 版本控制策略来发展,以容纳各种功能,而我们将协商通用的表示形式。
对这两段文字描述的理解,我是有一些困惑的,主要在清楚解 UDPA-DM 的定义和具体的 DPLB 原生实现(典型如 Envoy 的 xDS)之间的关系。下面这张图是我画的:
前面谈到 xDS 的演进路线, v3 / v4 会逐渐向 UDPA 靠拢,尤其 v4 会基于 UDPA 来。目前由于 UDPA API 远未成型,而 xDS v3 中对 UDPA API 的使用非常少(基本只用到了 annotation 定义),因此目前到底是哪个方案尚不明朗。
以下是 UDPA-DM 设计文档中描述的 UDPA-DM 的关键设计:
Routing API 设计
Routing API 中有三个术语:
在 UDPA-DM 的 Routing API 设计中,针对请求匹配的方式,相比 xDS 做了重大的改动,主要体现在除了线性匹配之外,还支持分层匹配。
这里先解释一下线性匹配和分层匹配这两种路由时需要用到的请求匹配方式:
目前 xDS v2 API 使用的是线性匹配方式,而 UDPA-DM 的 Routing API 会引入分层匹配,形成线性-分层的混合匹配方式,如下图所示:
设计文档对这个混合匹配模型有如下说明:
The model does not map directly to any given DPLB today but borrows from some Envoy concepts and should have an efficient realization. It may be possible to use HAproxy ACLs to model this topology.目前该模型并没有直接映射到任何给定的 DPLB,而是借鉴了 Envoy 的一些概念,这个模型应该会有一个有效的实现。可能会使用 HAproxy ACL 对这种拓扑进行建模。
由于目前 Routing API 并没有完成设计,也没有正式成为 UDPA 的 API,而在最新刚定稿的 xDS v3 协议中,RoutesDiscoveryService 和 ScopedRoutesDiscoveryService 也都没有引入这个新的模型,因此预期这个模型将在2020年继续完成设计和定稿,可能在年底的 xDS v4 中会有所体现。然后,UDPA-DM 和 xDS 之间到底会是转换模型,还是子集模型,届时就清楚了。
由于 Routing API 尚未设计完成,所以这里不详细展开 Routing API 的定义了。Routing API 的相关资料如下,有兴趣的同学可以关注(然而已经很长时间没有新的进展了):
UDPA 目前还处于早期设计阶段,关键的 UDPA-TP 和 UDPA-DM 的设计有推出草稿但是远未完成,内容也和我们期望的一个完整的通用数据平面 API 有很长的距离。而且项目进展并不理想,感觉重视程度和人力投入都有限。
最近因为想了解一下 UDPA 的进展,所以做了 UDPA 的调研和学习,比较遗憾的是 UDPA 的资料非常匮乏,除了我本文列出来的几个官方网站和设计文档之外,基本就只有 Harvey 的演讲。
调研完成之后发现 UDPA 的进展不如人意,尤其是最近的工作几乎停滞,关键的 UDPA-TP 和 UDPA-DM 的设计未能完稿,xDS v3 中也只引用了极少的 UDPA API 定义。这篇总结文章差点因此难产,因为未知/待定/未完成的内容太多,而且由于缺乏资料输入,很多信息也只是我个人的理解和想法,按说这不是一个严谨的深度介绍文章应有的态度。
但考虑到目前 UDPA 的资料实在是太少,本着“有比没有好”的想法,我硬着头皮完成了这篇文章。后续如果有新的输入,我会及时完善或者修订本文,也欢迎对 UDPA 有兴趣和了解的同学联系我讨论和指导。
领取专属 10元无门槛券
私享最新 技术干货