与 Spring Cloud 完全兼容的服务网格可以干什么样的事

背景

目前市场上的服务网格技术多用于解决微服务体系架构下的微服务治理的问题,主要包括有如下的功能:

流量调度系统

  • 服务注册和发现。服务注册,分类、标签和多版本管理。

  • 流量路由。蓝绿部署,灰度部署或 A/B 测试(全路径的流量着色/标签)

  • 东西向流量边车托管。流量劫持,如 Istio。非流量劫持,如:阿里开源的 MOSN

  • 服务实例负载均衡(轮询/加权轮询/一致性hash/随机 等)

弹力容错设计

  • 熔断器。当一个系统处于有问题状态时,快速失败比让客户等待更好。

  • 限流器。限制传入请求的速率。

  • 重试器。许多故障是瞬时性的,在短暂的延迟后可能会自我纠正。

  • 超时控制。超过一定的等待时间间隔,就不可能有成功的结果。

  • 健康检测。确定服务是否健康。

  • 缓存。某些比例的请求可能是类似的。

观测系统

  • 分布式跟踪。如:zipkin,OpenTracing 等支持

  • 服务指标。服务延迟/成功率/请求总数/请求错误码分布 等流量相关指标

  • 服务日志。访问日志,错误日志,等

安全系统

  • 认证体系。例如 mTLS,服务之间的相互访问。

  • 鉴权体系。服务是否有权限访问另外的服务。

  • 服务间通讯加密。保证服务间的通讯安全,如: HTTPS

问题

目前市面上的 Service Mesh 大多基于 Kubernetes 方案。此外,它提供了一个非侵入性和跨语言级别的解决方案。我们认为目前市场上的解决方案还是一个实验室里的方式,对于企业级的方案还有一定差距,其主要存在以下问题:

  • 缺乏完整的服务内部可观察性能力。从服务层面来看,我们主要要进行服务级别指标的采集,这里不仅是请求的应用指标,还有数据库、缓存、消息队列等的采集,另外还包含相应的跟踪日志。并且所有指标和日志必须相互关联。但现有服务网格对于这些服务内的指采集,需要用户自己来干这个事。

  • 灰度部署太简单太幼稚,不实用。真正的金丝雀部署应该能够基于被特定标签标记的特定用户来执行。并且金丝雀流量不仅需要从北到南,还需要从东到西传递到整个服务。现有灰度部署是非常简单的随机或使用 IP 地址方式,而不是用户级的。

  • Java 技术栈的迁移问题。特别是 Spring Cloud 与基于 Kubernetes 的服务网格有着完全不同的架构。要迁移Spring的应用,必须放弃 Spring Cloud 的解决方案,例如服务注册和发现、配置管理、弹性和容错等。但是,Spring Cloud 比当前的基于Kubernetes 的服务网格在服务治理上更加成熟和企业级。

  • 开发环境复杂。与 Spring Cloud 开发相比,当前的服务网格与 Kubernetes 紧密耦合,因此,如果开发人员需要调试/测试他们的代码,他们需要一个 Kubernetes 环境。这无疑增加了开发环境的门槛。

  • 基于 iptables 的流量劫持不是一个有效率的解决方案iptables 存在效率问题,一方面会消耗过多的内核资源,有可能导致整个操作系统hang住。另一方面,iptables 规则繁杂,一不就心就酿成大错,非常难以维护,另外,在 debug 和 troubleshooting 方面也非常之难。

Spring Cloud vs Kubernetes

显然,Spring Cloud 和 Service Mesh 各有优缺点。但如果我们能够将它们结合在一起,让用户能够利用这两种解决方案的优势,那我们就可以拥有最大的能力。

下表是 Spring Cloud 和 现有Service Mesh 的对比。

通过上表的对比,我们可以了解各自的优劣。然而,工程是我们每天必须面对的一种权衡。这种比较可能不公平,因为 Service Mesh 是要解决一个跨语言、跨平台的方案,而 Spring Cloud 只是一个基于 Java 的应用级解决方案。准确的说,Service Mesh 的通用性不得不在它的一些能力上有所妥协,而 Spring Cloud 的专业性可以专攻自己的领域。

但是,我们认为应该有一个解决方案,人们不必做出非黑即白的决定,尤其是在Spring Cloud 和基于 Kubernetes 的 Service Mesh 之间的困难选择

此外,Java 和 Spring 是如此强大和成熟,因此许多企业在其关键业务中深入广泛地使用Java,例如金融、电子商务、电信、制造、零售…等。Java 社区拥有最流行和最大的生态系统,我们认为 Service Mesh 应该与之兼容。

因此,我们决定开发一种新型的服务网格,它可以由 Kubernetes 编排,但可以与 Spring Cloud 完全兼容。换句话说,Spring Cloud 应用可以丝滑地迁移到服务网格。

EaseMesh

EaseMesh 是一个与Spring Cloud生态系统兼容的服务网格。它使用 Easegress (参看 下一代的流量网关) 作为边车和流量网关,并使用 EaseAgent 作为 JavaAgent 进行服务内部监控。

Sidecar 和 JavaAgent 都是非侵入式的技术,换句话说,EaseMesh 可以在不改变一行源代码的情况下将 Spring Cloud 应用迁移到服务网格架构,整个架构已经被赋予了全功能的服务治理、弹性设计和完整的可观察性,而用户不需要修改一行代码

下图是一个示意图:

在左边,图中显示了 “Spring Cloud PetClinic” 微服务应用(Spring Cloud官方的演示应用)。它使用Spring Cloud 全家桶,如:Spring Cloud Gateway、Spring Cloud Circuit Breaker、Spring Cloud Config、Spring Cloud Sleuth、Resilience4j、Micrometer,以及 Spring Cloud Netflix 技术栈中的 Eureka 服务发现。

通过使用 EaseMesh 的 Kubernetes CRD(自定义资源定义),只需几个命令行,整个 Spring Cloud 应用就可以迁移到服务网中。

而服务发现、配置管理、流量调度、弹性与容错设计、全栈监控(指标、日志、追踪)以及整个架构的“全链路布度发布”都被整合起来。(上图的右边部分显示了该架构)

现在,让我们来了解一下细节。

  • 自动装配 Sidecar 和 JavaAgent。通过使用Kubernetes CRD,非常容易将 sidecar 和 JavaAgent 透明地安装到 Java 应用程序中。

  • EaseMesh 使用 Easegress 进行服务治理。Easegress带来了以下主要功能。

    • 应用级服务发现–完全兼容 Java 的服务注册和发现–Eureka、Console、Nacos 等。

    • 一致性共识算法。Easegress有一个内置的Raft协议,主节点将是服务注册和发现,从节点将是 sidecar。

    • 服务弹力设计。Easegress将 Java 系的 resilience4j 移植到Go,其带来了Java社区那些成熟的容错功能–如:熔断、限流、重试等。

    • 流量调度。Easegress可以对南北和东西方向的流量进行着色,并在整个集群中进行同步。这可以使集群范围内的金丝雀部署。

  • 优雅的流量劫持。不同于其他服务网使用 iptables 劫持流量,EaseMesh 只需重新配置/劫持服务发现到边车上来,EaseMesh 将边车的 IP 地址(127.0.0.1)作为远程服务返回,边车负责真正的服务发现和远程通信。这个解决方案不会带来任何网络复杂性,而且性能消耗在应用层,不在内核,不会出现内核CPU过高导致整个系统响应停滞的问题。

下图显示了 EaseMesh 的架构,我们可以看到,其中使用 JavaAgent 配合 Easegress 的边车进行东西向的流量调度,以及一个Easegress 的 Mesh Ingress 进行南北向的流量调度,通过 Easegress 中内置的 Raft/Etcd 进行控制面的管理,并通过JavaAgent+边车做到了完整的观测性。

更多细节,请参考我们的开源 GitHub 仓库 - EaseMesh(Apache 2.0许可证)

凭借 Spring Cloud 和 Kubernetes 的共同优势,我们可以零成本地的对现有的 Java 应用进行成熟企业级的服务治理、编排和监控。

除此之外,我们还可以做更多更先进的企业级解决方案。

灰度发布

首先是灰度发布,所谓灰度发布是一种有范围的发布技术,我们可以限制新版本功能只能被某些用户看到,以降低发布风险。为了做好灰度发布,我们需要以下的功能。

  • 用户标签:一个定义用户标签的机制,用来识别是否是灰度用户。好的标签必须使用用户端数据,如User-Agent、Cookie、User Token、Location等。

  • 服务版本发现:我们需要管理服务版本,了解哪个版本是针对金丝雀用户的。

  • 流量调度:网关可以检查用户并将请求路由到正确的服务实例。

有了EaseMesh,我们可以在不改变任何代码的情况下轻松完成这些工作。你可以查看这个演示的说明文档来了解如何做到这一点。

当然,灰度发布其实并不简单,尤其是多个灰度发现出现的时候,可能会出现很多意想不到的冲突,相关的设计讨论,目前可以在Ease Mesh的 issue#52 中参与讨论。

全链路压力测试

第二件事是在生产中运行性能测试——生产线的全链接压力测试。

在一个微服务架构中,建立一个与生产环境相同的完整环境来对整个网站进行性能测试的成本很高。因此,如果我们能在生产中进行性能测试,那对企业来说将是非常好的。

然而,在生产环境中进行性能测试可能是非常困难的,我们需要解决以下问题。

  • 不会影响真正的客户。性能测试可能会消耗大量的资源,它可能对我们的客户产生很大的影响。

  • 测试数据可以很容易清理。性能测试可能会带来大量的数据,这些数据需要被清理。对于传统的解决方案来说,因为数据可能会跟生产数据混在一起,所以要事先做好数据标注的工作。

使用EaseMesh,我们可以做以下事情。

  • 为所有服务部署影子服务。

  • 镜像所有的中间件。如数据库队列缓存系统。

  • 使用 JavaAgent 或边车将流量转到镜像中间件上。

  • 将进行性能压力测试流量调度到影子服务上。

下图显示了 EaseMesh 如何做到这一点。

我们可以看到EaseMesh的以下工作。

  1. 首先,我们可以克隆这些服务所依赖的所有中间件(DB、Queue、Cache等),我们称它们为 “影子中间件”

  2. 其次,EaseMesh 为所有的服务部署测试服务,我们称它们为 “影子服务”。

  3. 然后,EaseMesh 将“影子服务”的中间件访问重定向到“影子中间件”。

  4. 接下来,EaseMesh 把测试流量调度到“影子服务”和“影子中间件”。

  5. 最后,我们可以安全而轻松地删除所有的“影子服务”和“影子中间件”。

  6. 在不改变任何用户源代码的情况下,EaseMesh为生产中的性能测试带来了完美的解决方案,它是方便、简单和安全的。

上述的两个示例可以看以通过 Spring Cloud 加持 Kubernetes 的 Service Mesh 可以非常方便和优雅的做到完美的无侵入多的解决方案。

此外,我们可以用Ease Mesh做更多这样的事情,我们期待着您对这个新的 Service Mesh 的建议或关注,甚至帮助我们使它变得更好。请随时在我们的 Github 上提出问题或贡献代码。 — https://github.com/megaease/easemesh