本文要点
服务网格公布了用于控制服务到服务(由东到西)流量的调节器(knobs)和控制杆(levers)
服务网格公布的“levers”可以被专门的工作流使用来降低发布的风险并缩短平均恢复时间(MTTR)
为了从服务网格中获得真正的价值,需要在网格之上编写扩展来驱动其功能。
目前,服务网格的实现因 API 和技术的不同而有所不同,并且没有任何放缓的迹象。
构建在不稳定性的 API 之上可能是危险的
需要一种简化的、工作流友好的服务网格 API 来保护组织平台代码免受特定服务网格实现细节的影响
各组织正试图利用云原生技术和基础架构来重塑它们的软件技术栈。这些技术和基础架构可以极大地提高它们快速且安全交付软件的能力。随着我们的应用程序变得越来越分散,基础架构也越来越“模糊”,在每天发布数十个(或更多)版本的情况下,维护其高可用性、正确性和安全性会变得非常困难。Kubernetes 和容器帮助我们标准化了基于“不变交付”的打包和部署模型。但是一旦这些应用程序部署后,需要通过网络进行相互通信,这给开发人员和运营商带来了很多挑战。服务网格为我们提供了一个基础框架,我们可以在此基础上构建和运行服务,从而极大地解决了应用程序网络通信安全及渐进式交付方面的一些挑战。
服务运营商和应用程序开发人员需要解决服务到服务的通信挑战,比如服务发现、客户端负载平衡、配置传输安全性、身份识别、流量路由、基于断路器的通信弹性、网络请求监控等。搭建跨语言或框架应用程序的过程,是非常容易出错并十分乏味的。我们使用某种语言构建了应用程序,后面我们怎样支持其他语言呢?我们为某个云平台构建的应用程序,是否可以扩展到其他平台呢?服务网格通过在每个工作负载旁注入一个服务代理来解决这些问题,通过这个服务代理,工作负载可以与网络上的任何其他服务进行交互,从而能够透明且一致地解决这些问题。
服务代理通常是专用的特定的工作负载实例,它们不与群集中的其他实例共享。这种服务代理的集合称为“数据平面”。服务网格中所有服务请求都经过这些代理,这些代理对这些服务请求加以拦截来丰富诸如重试、超时、断路、服务发现、负载平衡等强大的功能。Envoy Proxy 是一个开源的服务代理,它是实现服务网格的常用方案。由于进出工作负载实例的所有请求都遍历其服务代理,因此服务代理可以捕获相应请求的度量指标,并影响网络通信的变更。服务网格的运营商与控制平面进行交互,控制平面是一个与支持数据平面的请求路径无关的组件集。
服务网格遵循二八定律。服务网格可以让我们以云、语言和框架无关的方式,解决 80%的应用程序网络问题。但最后 20%需要我们编写自己的“胶水代码”来驱动和操作服务网格,以“编排它”交付有价值的高阶服务。服务网格发布了一组强大的独立功能集,但是需要对它们进行整合后,才能被我们的组织充分利用。服务网格支持的高阶服务示例包括:
智能自动伸缩
渐进式交付
通信模式检测
混沌注入与分析
请求溯源及监管链
零信任网络实践
AB 实验(这是渐进式交付的更成熟版本)。
服务网格帮助我们完成了任务的 80%,但要真正实现上面的这些功能,还需要编写 20%的代码来扩展网格。
构建在服务网格之上以降低风险
我们来看一个整合了多个功能的服务网格示例,这个整合后的服务网格可以帮助我们实现降低系统变更风险的最终目标。
渐进式交付是由 Azure Devops 的产品所有者 Sam Guckenheimer 和 Redmock 分析师 James Governor 在 2018 年创造的术语,该术语用来描述为了缩小故障的影响范围而渐进地发布新应用程序功能的方式。使用流量控制、功能标记、请求路由和度量指标收集的组合,可以按照渐进式交付技术构建强大的 CI / CD 管道,从而降低交付新代码和代码变更的风险。
在应用程序或服务代码不进行任何变更的情况下,我们可以用服务网格作为框架构建渐进式交付能力,它可以协调流量控制、请求路由、度量指标收集等功能。例如,如果我们想变更推荐服务,我们可以部署一个新版本的推荐服务,并精确地控制哪些用户被路由到新部署上。按照渐进式交付方式,我们可以将“推荐”服务的 v2 版部署到生产环境中,并且只向给内部员工公开。然后,我们观察新服务的度量指标及日志,并为继续设置交付的阈值。如果我们发现这个新版本有问题,我们可以通过回滚(或减少流量)来缩小影响范围。例如,如果我们发现请求处理过程中错误增多或延迟变长,我们可以停止这个金丝雀的渐进式交付,甚至可以将其回滚。如果新版本发现正常,我们可以开放流量给更多用户(例如,1%的实时用户)。我们遵循这种方式,逐步地向其他用户组公开新服务,并通过观察度量指标和日志来决定是继续发布还是回滚。
这个服务网格使用控制平面和数据平面架构实现,其中数据平面是一个个的服务代理,通过这些服务代理可以收集请求度量指标并执行请求路由控制。控制平面是与请求路径无关的组件集,运营商和用户可以通过和它交互来收集度量指标、建立策略及变更配置来影响数据平面。
控制平面有一个 API 接口或一些固定的方法来驱动网格的配置。在大多数开源的服务网格实现中,通常会使用声明式的配置或 API 接口,它具有“声明性意图”,服务网格负责“协调完成该意图”。例如,Istio(一个开源的服务网格)的配置被声明为描述服务网络行为意图的 YAML 资源,并且 Istio 的控制平面将此意图合成到服务代理的配置中。对于上面的“推荐”服务,我们可能希望通过拆分路由到每个版本的流量比例来控制到金丝雀版本的流量。在 Istio 中,使用“VirtualService”可以做到这一点:
Istio 还能使用 statsD、Prometheus、Jaeger 等工具从服务代理上收集度量指标来进行分布式跟踪,之后再将这些度量指标整合到后端分析引擎。这些度量指标还可用于决策是否继续交付或发布过程。分析这些度量指标,如果指标正常,我们可以指示服务网格将 20%的实时流量路由到金丝雀版本,如果指标异常,我们可以回滚部分流量(甚至全部回滚)。
服务网格的另一个实现AWS App Mesh使用了类似的声明式 API 来描述流量路由规则。我们创建 JSON 资源并配置到 App Mesh 的控制平面,之后控制平面会将该 JSON 资源的意图整合到 Envoy 配置中(Envoy 是 App Mesh 的数据平面):
AWS App Mesh 可以向 CloudWatch、X-Ray 等发送度量指标和请求遥测数据。通过这些数据,渐进式交付引擎可以评估是否继续向新版本开放流量。
像Linkerd和Consul Connect等其他的服务网格实现也在开发它们自己的流量控制 API,但可能也会遵循类似的声明式 API 模式。
当我们基于服务网格实现渐进式交付时,首先要考虑哪些部分属于二八定律中的 80%,哪些部分属于 20%。虽然服务网格不仅能收集服务或请求级别的度量指标,还能控制服务的流量调度(流量转移、限制、路由),但是它无法感知服务升级或回滚的过程,如触发服务进一步发布或回滚的阈值。这样的控制逻辑是非常个性化且强依赖组织的,它需要组织在服务网格之上编写 20%的胶水代码实现。为此,组织需要使用特定的服务网格控制平面 API。
需要一个开放的服务网格 API
当一个组织想要搭建它的渐进式交付能力时,或者如前所述在服务网格之上搭建其他能力时,必须围绕它所采用的特定服务网格 API 构建相应的胶水代码。目前,所有服务网格实现的这类 API 处于不断发展变化的过程中。智能化的组织已经开始构建专用的特定配置 API 来更清晰地表达他们的意图,更好地适应在服务网格之上构建的增值服务,并对不断变化的 API 进行抽象。然后,使用转换层将它的配置转换为特定服务网格实现的 API。通过抽象特定的服务网格 API,组织可以限制任何特定服务网格 API 实现的影响范围,尤其是在升级和发布非向后兼容的变更时。
抽象出特定的服务网格 API 的另一个优势是能够在以后更换服务网格技术时,不会影响基于特定服务网格编写的那 20%的代码和功能。事实上,随着服务网格实现的不断发展变化,我们会发现,服务网格越来越专注于某个功能领域(如安全性、可观察性、流量控制等)。拥有独立的、与网格无关的 API,可以让组织整合或即插即用某些网格功能。
最后,当组织尝试将内部的流程、部署与公有云中的流程、部署统一时,用于抽象网络的独立 API 将变得越来越重要。我们为扩展内部工作负载编写的 Istio 的工具,在部署到 AWS 时仍然可以使用。例如,AWS 的 App Mesh 是一个 AWS 原生服务网格,如果将它部署到 AWS,那么利用它原生的服务网格是有意义的。通过为服务网格和在服务网格之上构建的功能提供一个统一的 API,我们在应用到新平台时无需重新造轮子,并且可以在上面安全地构建它。
一方面,由于上述原因,头部组织一直在尝试定义这种“非正式的、与网格无关的 API”,另一方面,每个组织都做这件事情,有点浪费资源。如果我们通过合作的形式构建一个开源项目,该项目在服务网格实现之上提供稳定的 API,使我们能灵活地采用所需的解决方案,使用时即插即用,并且在我们的网格基础设施之上还可以拥有 20%的集成胶水代码,这将会怎么样?
构建开放服务网格 API 的方法
SuperGloo是一个开源项目,最初开始于构建一个开放、稳定、与网格无关的 API。如果我们使用一个简化的服务网格 API,用它抽象任何的服务网格,则可以实现如下目标:
为安装任何服务网格提供一致、简化的体验
构建服务网格的扩展功能(使用 20%的胶水代码)并对冲服务网格环境中的任何波动(采用选择、版本变更、同类最佳等)
服务网格实现和资源发现并管理它们
在单一管理平台和 API 下管理多个服务网格安装实现
管理一个或多个服务网格实现的多集群
SuperGloo有一些API,分别用于使用“Install”和“Mesh”配置对象来安装和解释服务网格的功能。例如,使用 SuperGloo 的“Install”API 安装 Linkerd,如下所示:
网格特征本身在Mesh
配置中定义:
Istio 和 AWS App Mesh 的操作使用类似的方法。当我们定义网格的流量规则时,事情将变得非常有趣。例如,通过 SuperGloo,我们可以使用RoutingRule
API 给网格分配流量规则。指定一个加权路由规则在“review”服务的 v1 和 v2 之间切换流量,我们可以执行以下操作:
请注意,SuperGloo 路由到“Upstreams”,这是另一个用于抽象特定后端目标和版本组合的 API 对象。这些“Upstreams”是根据标签自动发现的(例如,在 Kubernetes 或可能在 Consul 中的标签),我们不必显式地创建它们(尽管我们可以根据需要创建它们)。 RoutingRule
还包含指定sourceSelector
的功能,因此我们可以选择到达应用“RoutingRule”目的地的流量来源。在此背后,这些对象创建了必要的特定网格对象。
这些对象适用于任何服务网格实现。请查阅文档来查看完整的 API。目前,SuperGloo 支持 Istio、Linkerd 和 AWS App Mesh,将来还会支持更多网格。
围绕特定服务网格实现的统一 API 让我们通过 SuperGloo 一致地管理任何服务网格实现,包括在有多个网格或多个集群(即使只有一个网格的多个集群)的更复杂的场景下。我们编写的 20%代码可以保持与网格无关,并在网格上提供有价值的扩展。一个很好的例子就是构建渐进式交付能力。事实上,Weaveworks 的Flagger项目就是这样做的,并可以利用SuperGloo来保持网格无关性。
感想
随着组织进一步采用服务网格技术并在它上面进行二次构建,在明确的标准出现之前,他们将疲于将其有价值的 20%代码与任何一个实现相结合。即使只有一个实现,事情也会快速发展,API 也会发生变化,这种波动可能会给那些迫不及待地使用服务网格的不同实现作为解决方案的组织造成严重破坏。使用 SuperGloo,我们可以使用 20%的代码对冲这种波动性,甚至可以处理整合了多个网格或多个群集的更高级用例。
作者简介
Christian Posta(@christianposta)是 Sloo.IO 全球 CTO,Red Hat 前首席架构师,并以图书作者(Istio in Action, Manning, Microservices for Java Developers, O’Reilly 2016)、博主、演讲人、开源传道士、开源项目贡献者的身份闻名于社区,他开源的项目包含 Istio、Kubernetes 等。Christian 在大规模企业有多年工作经验,现在致力于帮助企业创建并部署大规模、云原生、高弹性、分布式架构。他喜欢辅导、培训并领导团队从事有关分布式系统、微服务、DevOps、云原生应用程序设计的项目。
查看英文原文:Towards Building an Open API For Consolidating and Federating Service Meshes
评论