软件开发架构师

边缘控制平面Ambassador全解读

运维 650 2019-03-11 10:42

Ambassador是由Datawire开源的一个 API 网关项目,主要在 Kubernetes 的容器编排框架中使用。Ambassador 本质上是一个通过配置边缘 /API 来管理Envoy数据面板的控制面板。而 Envoy 则是一个基于第 7 层协议的网络代理和通信总线,它是一个由Lyft开源的云原生服务,主要用于处理入口边缘以及内部服务之间的网络通信。今天 Envoy 正迅速成为现代网络事实上的代理,几乎所有的公共云供应商,以及 eBay、Pinterest 和 Groupon 等大型互联网公司都提供对这种代理服务的支持。

本文首先介绍了 Ambassador 的由来,然后详细介绍了它的发展过程,并讨论了开发人员在构建这个控制面板时遇到的技术挑战和经验教训,该面板用于在 Kubernetes 集群里管理微服务应用的入口流量(入口边缘)。

本文要点:

  • Ambassador 是由 Datawire 开源的一个 API 网关,主要在 Kubernetes 容器编排框架中使用。

  • Ambassador 本质上是一个通过配置边缘 /API 来管理 Envoy 代理数据面板的控制面板。

  • Envoy 是一个处理边缘入口和内部服务之间网络通信的 L7 代理和通信总线。

  • 本文介绍了 Ambassador 的由来,然后详细介绍了它的发展过程,并讨论了开发人员在构建这个控制面板时遇到的技术挑战和经验教训,该面板用于管理部署到 Kubernetes 集群中的基于微服务的应用程序中的入口流量。

  • 将 Ambassador 迁移到 Envoyv2 配置和 ADS(聚合发现服务) API 是一个漫长而艰难的过程,需要大量的架构和设计讨论,以及大量的编码,但是社区的早期反馈是积极的。

Ambassador是由Datawire开源的一个 API 网关项目,主要在 Kubernetes 的容器编排框架中使用。Ambassador 本质上是一个通过配置边缘 /API 来管理Envoy数据面板的控制面板。而 Envoy 则是一个基于第 7 层协议的网络代理和通信总线,它是一个由Lyft开源的云原生服务,主要用于处理入口边缘以及内部服务之间的网络通信。今天 Envoy 正迅速成为现代网络事实上的代理,几乎所有的公有云供应商,以及 eBay、Pinterest 和 Groupon 等大型互联网公司都提供对这种代理服务的支持。

本文首先介绍了 Ambassador 的由来,然后详细介绍了它的发展过程,并讨论了开发人员在构建这个控制面板时遇到的技术挑战和经验教训,该面板主要用于在 Kubernetes 集群里管理微服务应用的入口流量。

云原生新兴组合: Kubernetes 与 Envoy

像 DevOps 和微服务一样,云原生也开始变得耳熟能详,并在整个 IT 行业中不断获得关注。据Gartner报道,2018 年全球公共云服务收入预计在 1750 亿美元左右,而明年仍将有超过 15% 的增长。目前的公有云市场完全由几家大公司垄断,而这几家公司也几乎把持了所有的关键技术,这让开源即服务的商业前景变得很不明朗。在这样的背景下,Linux 基金会于 2015 年成立了 CNCF(云原生计算基金会),旨在讨论和托管全栈云原生环境中的开源组件。

CNCF 吸取了 Open Stack 社区的经验教训,在早期项目发展中相对保守和严格,它甚至为项目提供了发展概要,并要求项目有实际的应用案例,或者项目本身是基于已流行项目(如 Kubernetes)的扩展。Google 提供的Kubernetes 容器编排框架,以及 Lyft 提供的边缘路由器Envoy 代理是 CNCF 早期的两个主要项目。虽然平台即服务 (Platform-as-a-Service,PaaS)架构复杂且各不相同, 但 Kubernetes 和 Envoy 一直是其核心组成。

许多 PaaS 供应商,甚至最终用户的技术团队,都将针对网络第 7 层元数据的编排容器和通信路由视为云原生系统的数据面板,比如 HTTP URI、Headers 以及 MongoDB 协议元数据。因此,更多的初创公司则把重心放在如何创建一个有效的 "控制面板 "上,例如帮助终端用户实现与数据面板的交互,配置数据面板的自定义参数,观察数据面板中的监测或日志等。

Kubernetes 控制面板一般都采用了类似 REST 的 API(又称 "Kubernetes API"),像‘kubectl‘这样的 CLI 工具都是对此类 API 的二次封装。首版 Envoy 的控制面板主要使用了JSON 的配置方式,还有一些可以选择性更新的松耦合 API。随后这些 API 又变成了Envoy v2 API的一部分,新版 Envoy 同时提供了一系列基于gRPC协议缓冲类型 API。最初 Kubernetes 的 kubectl 工具并没有提供一种类似 Envoy 的实现,这给后来使用 Kubernetes 的团队制造了不少麻烦。当然很多关于实现 Envoy V2 控制面板的初创公司也因此出现。

服务网格 VS API 网关

谈到网络中的控制面板,自然就少不了新兴的服务网格技术。诸如 lstio、Linkerd 和 Conser connect 之类的服务网格,主要在微服务系统中管理服务之间的通信,也就是所谓的“东 - 西”方向通信。lstio 本身是一个非常有效的控制面板,它通过 Envoy 代理来管理网格之间的数据面板(L7 网络流量);Rust 语言编写的 Linkerd 实现了自己的代理;而 Conser Connect 则使用了自定义代理,最近也提供了对 Envoy 代理的支持。

Istio 架构,上半部分为 Envoy 代理的数据面板,下半部分为控制面板 (图片来自 Istio 文档)

服务网格需要事先假定用户对网格通信的双方具有高度的所有权和可控制度。例如,同一个公司的不同工程部门的两个服务,或者是一个可信任网络边界内的第三方应用程序服务 (可能跨越多个数据中心或虚拟专有云)。在这里,运维团队通常会设置一些符合常理的通信缺省值,而服务团队则在此基础上配置各自服务间的路由。在这种情况下,你不可能完全信任每个服务,因此要实施如速率限制和断路等保护,以此来调查和改变所检测到的不良行为,但是服务网格根本不可能实现这类保护,因为边缘或入口 (“南 - 北”) 流量来自外界网络,超出了其控制范畴。

集群入口流量通常来自不可控的外部网络

任何来自我们信任的网络之外的通信都可能来自一个心怀不轨的的第三方,他的动机可能就是网络罪犯或破坏移动应用程序中的客户端库,因此我们必须对边缘 /API 网关部署适当的防御措施。在这里,运维团队将指定一些系统默认值,并根据外部事件实时调整这些值。你可能希望能够实现速率限制、全局配置和指定 API 负载(例如后端服务或数据集过载),以及 DDoS 保护实现等 (基于时间或区域的保护)。另外,服务开发团队的一些任务也需要访问为新 API 配置路由的边缘,通过流量跟踪或金丝雀发布来测试和发布新服务。

顺便说一句,为了进一步讨论 API 网关的角色,Christian Posta 最近发表了一篇有趣的博文“API 网关正在经历身份危机”。本文作者也写过一些文章,介绍了API 网关在云 / 容器迁移或数字转换过程中的角色,以及如何实现将 API 网关与现代持续交付模型的集成

虽然乍一看,服务网格和边缘 /API 网关非常相似,但他们还是存在着一些细微的差异,而这些差异也影响了边缘控制面板的设计。

边缘控制面板设计

控制面板很大程度上由它所要控制的范围以及所使用的人群决定。我的同事Rafael Schloming 之前在旧金山 QCon 会议上谈到过这个问题,他在会上讨论了针对集中化或去中心化的控制实现,以及服务目前处于的开发 / 运营周期 (原型、关键任务等) 是如何影响控制面板实施的。

如上文所述,以边缘代理的控制面板为例,集中化运营或 SRE(网站可靠性工程师)团队可能希望为所有的入口流量指定合理的全局默认值和安全措施。然而在第一线工作的产品开发团队则希望能够进行更细化的控制,并且希望能够在本地更改全局设置。

Ambassador 社区一开始就将开发人员以及应用工程师作为主要目标人群。因此他们主要关注的是基于去中心化配置的控制面板。Ambassador 一开始就按照 Kubernetes 标准设计,因此我们最好也能够参照 Kubernetes 的服务规范来配置边缘,例如使用 YAML 文件,通过 kubectl 加载。

Ambassador 的配置方式有Kubernetes Ingress对象、自定义的Kubernetes 注以及自定义资源定义 (CRDs)。我们最终选择了 Kubernetes 注解,因为它很简洁,也容易上手。理论上 Ingress 对象是一个更好的选择,但 Ingress 规范一直处于测试阶段,除了管理入口流量的 " 最基本 " 功能之外,其他功能还没怎么达成共识。

如下是一个 Ambassador 的注解示例,它在 Kubernetes 服务上实现了一个简单的终端到服务的路由:

复制代码
kind: Service
apiVersion: v1
metadata:
name: my-service
annotations:
getambassador.io/ config: |
---
apiVersion: ambassador/v0
kind: Mapping
name: my_service_mapping
prefix: /my-service/
service: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376

如果你以前配置过边缘代理、反向代理或 API 网关,那相信你不会对 getambassador.io/config 中的配置感到陌生,其主要功能就是将发送到终端前缀的流量映射到名为 my-service 的 Kubernetes 服务上。由于本文主要关注 Ambassador 的设计和实现,因此我们不会涵盖可以配置的所有功能,比如路由、金丝雀发布和速率限制等。虽然 Ambassador 主要是面向开发人员的角色,但它也实现了很多针对运营商的扩展,并且也可以集中配置身份验证、TLS/SNI、追踪和服务网格集成等。

接下来让我们再回到 Ambassador,并看一下它在过去两年中的演变。

Ambassador < v0.40:Envoy v1 APIs, 模板以及热重启

Ambassador 本身是部署在容器中的 Kubernetes 服务,并使用 Kubernetes Services 注解作为其核心配置模型。这种方法使应用程序开发人员可以将路由管理作为 Kubernetes 服务定义的一部分 (也称作 "GitOps" 方法)。将简单的 Ambassador 注解转换为有效的Envoy v1配置并不是一项简单的任务,毕竟 Ambassador 与 Envoy 的配置在设计上并不相同。因此,在进行配置转换的时候,我们本着聚合和简化操作的目的,更改了很多 Ambassador 内部的逻辑。

具体来讲,Kubernetes 用户可按照如下步骤完成对 Ambassador 的配置:

  1. Kubernetes API 异步通知 Ambassador 的更改。

  2. Ambassador 将配置转换为抽象的中间代码 (IR)。

  3. 从 IR 中生成一个 Envoy 配置文件。

  4. 使用 Ambassador 验证 Envoy 配置文件。

  5. 如果配置文件有效,Ambassador 将使用 Envoy 的热重新启动机制来部署新的配置,并保持连接。

  6. 流量将会在新启动的 Envoy 进程中传输。

这个初始实现有很多好处:首先机制简单,其次 Ambassador 同 Envoy 之间的配置转换是可靠的,最后 Ambassador 与基于文件的热重启的整合也是可靠的。

但是,这一版本的 Ambassador 仍有很多需要改善的地方。首先,尽管热重启对于大多数用例有效,但是它还不够快;而且一些用户 (特别是那些部署大型应用程序的用户) 发现它限制了更改配置的频率;最后,热重启有时也会中断连接,尤其是像 websocket 或 gRPC 流这类的长期连接。

更重要的是,尽管初版 Ambassador 到 Envoy 的中间代码 (IR) 允许快速原型设计,但由于功能还太初级,很难做出实质性的改变。虽然这是从一开始就存在的问题,但随着 Envoy 升级到Envoy v2 API,这变成了一个关键问题。正如 Matt Klein 在他的博文“通用数据面板 API“中所描述的那样,v2 API 为 Ambassador 提供了很多好处:诸如访问新特性、解决上面提到的连接中断问题,但遗憾的是现有的 IR 实现仍然没有本质的提高。

Ambassador > v0.40: Envoy v2 API(ADS)、中间代码、KAT 测试

在与Ambassador 社区协商后,Ambassador 首席工程师Flynn带领的 Datawire 团队在 2018 年对 Ambassador 内部进行了重构。重构主要有两个关键目标。首先,能够集成 Envoy v2 的配置格式,这将支持诸如服务器名称指示、基于标签的速率限制和改进的身份验证等特性。其次,能够对 Envoy 配置进行更强大的语义验证,毕竟它的复杂性越来越高(特别是在大规模应用程序部署的时候)。

Datawire 团队首先按照多遍编译器的思路调整了 Ambassador 的内部结构。添加了类层次结构以便可以更好的反映 Ambassador 配置资源、中间代码和 Envoy 配置资源之间的分离。而 Datawire 以外的社区也重新设计了 Ambassador 的核心部分。

之所以采取这些方法是处于如下几个方面的考虑:首先,Envoy 代理仍是一个快速发展的项目,我们不能每次都因为 Envoy 配置的微小变化就对 Ambassador 进行重新设计。而且,我们也希望能够实现配置文件的语义验证。

随着对 Envoy v2 整合工作的深入,Datawire 团队很快又发现了一个测试上的问题,那就是随着 Ambassador 开始支持越来越多的特征,它在处理不太常见但又正确的特征组合时出现了非常多的错误。这促使他们创建一个新的测试要求,而这意味着需要对 Ambassador 的测试套件进行重新编写,以便能够自动测试大部分的特性组合,而不是依靠人工来编写每个测试用例。此外,这套测试套件需要快速,以便最大限度地提高工程效率。

因此,在对 Ambassador 重构的同时,Datawire 团队创建了新的Kubernetes 验收测试 (KAT)框架。KAT 是一个可扩展的测试框架,它的主要功能如下:

  1. 将一组服务 (连同 Ambassador) 部署到 Kubernetes 集群

  2. 对分离出来的 API 运行一系列验证查询

  3. 对这些查询结果执行一组断言测试

KAT 是为性能而设计的——它提前将测试设置打包,然后在步骤 3 中使用高性能 HTTP 客户机异步查询。KAT 中的流量驱动程序使用了另一个开源工具Telepresence,这也让调试诊断变的更加容易。

随着 KAT 测试框架的到位,Datawire 团队又遇到了一些关于 Envoy v2 配置和热重启的问题,他们开始考虑转换到 Envoy 的ADS(聚合发现服务)API。这样完全消除了配置更改时重启的限制,也避免了高负载或长时间连接下的中断问题。Datawire 团队最终决定使用 GO 编写的Envoy 控制面板连接到 ADS。但是,也增加了 Ambassador 代码库对 GO 的依赖。

使用新的测试框架,新的中间代码生成有效的 Envoy v2 配置以及 ADS,Ambassador 0.50 的主要体系结构更改已经完成。现在,用户使用含有 Ambassador 注解的 Kubernetes 清单的步骤如下:

就在发布之前,Datawire 团队又碰到了一个问题,Azure Kubernetes 服务(AKS)不能正确检测到 Ambassador 注解的更改。通过与 AKS 工程团队的高效合作,他们很快找到了问题点——Kubernetes API 服务器中的代理链丢掉了很多访问请求,对此最好的缓解措施是使用支持调用 API 服务器的 FQDN。不幸的是,这需要 AKS 中的 mutating webhook,而官方的 Kubernetes Python 客户端并没有实现这个特性。因此,我们使用了 Kubernetes Golang 客户端,这也是基于 Go 的第二个依赖项。

总结

正如 Matt Klein 在EnvoyCon开幕式上所提到的,随着目前 Envoy 代理在云原生技术领域的流行,不知道 Envoy 的人群反而成了少数。我们知道,谷歌的 Istio 已经在 Kubernetes 用户中提高了 Envoy 的知名度,现在主要的云供应商都在关注 Envoy,例如,在AWS app 网格Azure Service Fabric 网格。在 EnvoyCon 上,我们还听说了一些大公司如eBay、Pinterest 和 Groupon都在使用 Envoy 作为他们的主要边缘代理,还有一些其他基于 Envoy 的边缘代理控制面板,如Istio GatewaySolo.io Gloo 和HeptioContour等。Envoy 已经成为云原生通信的通用数据面板,当然它在控制面板领域内仍有许多工作。

在本文中,我们讨论了 Datawire 团队和 Ambassador 开源社区如何成功地将 Envoy v2 配置和 ADS API 应用到 Ambassador 边缘控制面板。在构建 Ambassador 0.50 的过程中,我们学到了很多东西:

  • Kubenetes 和 Envoy 是功能非常强大的框架,但它们也在不断成长中——很多时候都没有最新的文档交付,维护人员只能通过阅读源码来学习。

  • Kubernets/Envoy 生态系统中支持最好的类库都是用 Go 编写的。虽然我们喜欢 Python,但是我们不得不采用 Go,这样我们就不必自己维护太多的组件。

  • 有时候重新设计测试工具也能促进软件开发。通常,重新设计测试工具的真正成本只是将旧的测试移植到新的工具实现。

  • 为边缘代理设计和实现一个有效的控制面板是很困难的,来自 Kubernetes、Envoy 和 Ambassador 开源社区的反馈非常有用。

将 Ambassador 迁移到 Envoy v2 配置和 ADS API 是一个漫长而艰难的过程,需要大量架构和设计上的讨论,当然也少不了大量的编码,但早期反馈的结果是积极的。Ambassador 0.50已经发布,你可以试用一下,并在Slack 频道Twitter上分享一下你的意见。

关于作者

丹尼尔·布莱恩特,公司技术变革的领导者,自由职业顾问,Datawire 是其客户之一。他目前的工作是实现公司内部开发的敏捷性:引入更好的需求收集和规划技术,关注敏捷开发中架构的相关性,促进持续集成和交付等。Daniel 目前的技术专长集中在 "DevOps" 工具化、云 / 容器平台和微服务实现。他同时是伦敦 Java 社区 (LJC) 的领导者,并参与了很多开源项目。他还为 InfoQ、DZone 和 Voxxed 等知名技术网站的撰稿,并定期在 QCon、JavaOne 和 Devoxx 等国际会议上发表演讲。

查看英文原文https://www.infoq.com/articles/ambassador-api-gateway-kubernetes

文章评论