• 构建基于Spring Cloud向Service Mesh框架迁移的解决方案及思路

作为新一代微服务架构体系,Service Mesh 技术有效地解决了 Spring Cloud 微服务架构和服务治理过程中的痛点问题,一经推出便引起了很大的反响。今天我们就针对构建基于 Spring Cloud 向 Service Mesh 框架迁移过程中的诸多问题展开讨论,尽可能提供一套完善的解决方案和迁移思路,供大家参考。

blog-thumb

作为新一代微服务架构体系,Service Mesh 技术有效地解决了 Spring Cloud 微服务架构和服务治理过程中的痛点问题,一经推出便引起了很大的反响。近一年来,伴随着云原生的热火朝天,Service Mesh 被推向了巅峰,从陌生走向大家的视界,甚至一些初创企业都想从中获得第一桶金。对于初创企业或全新产品,选择 Service Mesh 变得相对轻松很多,毕竟不存在迁移的问题。但对于大部分企业或成熟的产品体系,这样大的架构转型就变得很难以实施,需要多加权衡利弊,面对 Service Mesh 带来的好处,不得不迫使向它靠拢。

目前很多企业还是采用基于 SDK 的传统微服务框架(例如,DubboSpring Cloud)进行服务治理,而随着 Service Mesh 的普及,越来越多的企业开始布局自己的 Service Mesh 框架体系,但多数企业刚开始不会激进地将所有业务迁移至 Serivice Mesh,毕竟这样风险太大、收益太慢。像 Java 技术栈应用依然保留原框架,而非 Java 技术栈应用采用 Service Mesh 框架,不同开发语言可以用不同的技术框架,但业务不能被框架割裂,那么在这两种架构体系下应用服务如何互联互通?微服务如何统一治理?传统微服务又如何平滑迁移至 Service Mesh 呢?

如何解决上述问题呢?今天我们就针对构建基于 Spring CloudService Mesh 框架迁移过程中的诸多问题展开讨论,尽可能提供一套完善的解决方案和迁移思路,供大家参考。

1、背景

微服务是近些年来软件架构中的热名词,也是一个很大的概念,不同人对它的理解都各不相同,甚至在早期微服务架构中出现了一批四不像的微服务架构产品,有人把单纯引入Spring BootSpring Cloud框架也叫做微服务架构,却只是将它作为服务的Web容器而已。

随着微服务的火热,越来越多的团队开始实践,将微服务纷纷落地,并投入生产。但随着微服务规模的不断壮大,每增加一个微服务,就可能会增加一些依赖的基础设施和第三方的配置,比如 Kafka 实例等,相应 CI/CD 的配置也会增加或调整。 同时随着微服务数量增多、业务复杂性的提升及需求的多样性等(如,对接第三方异构系统等),服务间通信的错综复杂,一步步地将微服务变得更加臃肿,服务治理也是难上加难,而这些问题在单体架构中很容易解决。为此,有人开始怀疑当初微服务化是否是明智之选,甚至考虑回归到传统单体应用。

正如下图所示,PPT 中的微服务总是美好的,但现实中的微服务却是一团糟糕,想甩甩不掉,越看越糟心。难道就没有办法了么?

现实中vsPPT中的微服务

1.1 传统微服务架构面临的挑战

面对上述暴露出的问题,并在传统微服务架构下,经过实践的不断冲击,面临了更多新的挑战,综上所述,产生这些问题的原因有以下这几点:

  • 过于绑定特定技术栈。 当面对异构系统时,需要花费大量精力来进行代码的改造,不同异构系统可能面临不同的改造。
  • 代码侵入度过高。 开发者往往需要花费大量的精力来考虑如何与框架或 SDK 结合,并在业务中更好的深度融合,对于大部分开发者而言都是一个高曲线的学习过程。
  • 多语言支持受限。 微服务提倡不同组件可以使用最适合它的语言开发,但是在 Spring Cloud 框架下就是Java的天下,多语言的支持难度很大。这也就导致在面对异构系统对接时的无奈,或退而求其次的方案了。
  • 老旧系统维护难。 面对老旧系统,很难做到统一维护、治理、监控等,在过度时期往往需要多个团队分而管之,维护难度加大。

上述这些问题都是在所难免,我们都知道技术演进来源于实践中不断的摸索,将功能抽象、解耦、封装、服务化。 随着传统微服务架构暴露出的这些问题,将迎来新的挑战,让大家纷纷寻找其他解决方案。

1.2 迎来新一代微服务架构

为了解决传统微服务面临的问题,以应对全新的挑战,微服务架构也进一步演化,最终催生了Service Mesh 的出现,迎来了新一代微服务架构,也被称为下一代微服务。为了更好地理解 Service Mesh 的概念和存在的意义,我们来回顾一下这一演进过程。

1.2.1 耦合阶段

在微服务架构中,服务发现、熔断、治理等能力是微服务架构重要的组成部分。微服务化之后,服务更加的分散,复杂度变得更高,起初开发者将诸如熔断、超时等功能和业务代码封装在一起,使服务具备了网络控制能力,如下图所示。

耦合阶段

这种方案虽然易于实现,但从设计角度来讲却存在一定的缺陷。

  • 基础设施功能(如,服务发现,负载均衡、熔断等)和业务逻辑高度耦合。
  • 每个微服务都重复实现了相同功能的代码。
  • 管理困难。如果某个服务的负载均衡发生变化,则调用它的相关服务都需要更新变化。
  • 开发者不能集中精力只关注于业务逻辑开发。

1.2.2 公共库SDK

基于上面存在的问题,很容易会想到将基础设施功能设计为一个公共库SDK,让服务的业务逻辑与这些功能降低耦合度,提高重复利用率,更重要的是开发者只需要关注公共库SDK的依赖及使用,而不必关注实现这些公共功能,从而更加专注于业务逻辑的开发,比如 Spring Cloud 框架是类似的方式。如下图所示:

公共库SDK阶段

实际上即便如此,它仍然有一些不足之处。

  • 这些公共库SDK存在较为陡峭的学习成本,需要耗费开发人员一定的时间和人力与现有系统集成,甚至需要考虑修改现有代码进行整合。
  • 这些公共库SDK一般都是通过特定语言实现,缺乏多语言的支持,在对现有系统整合时有一定的局限性。
  • 公共库SDK的管理和维护依然需要耗费开发者的大量精力,并需专门人员来进行管理维护。

1.2.3 Sidecar模式

有了上面公共库SDK的启发,加上跨语言问题、更新后的发布和维护等问题,人们发现更好的解决方案是把它作为一个代理,服务通过这个透明的代理完成所有流量的控制。

这就是典型的 Sidecar 代理模式,也被翻译为边车代理,它作为与其他服务通信的桥梁,为服务提供额外的网络特性,并与服务独立部署,对服务零侵入,更不会受限于服务的开发语言和技术栈,如下图所示。

Sidecar模式阶段

Sidecar 模式进行通信代理,实现了基础实施层与业务逻辑的完全隔离,在部署、升级时带来了便利,做到了真正的基础设施层与业务逻辑层的彻底解耦。另一方面,Sidecar 可以更加快速地为应用服务提供更灵活的扩展,而不需要应用服务的大量改造。Sidecar 可以实现以下主要功能:

  • 服务注册。 帮助服务注册到相应的服务注册中心,并对服务做相关的健康检查。
  • 服务路由。 当应用服务调用其它服务时,Sidecar 可以帮助从服务发现中找到相应的服务地址,完成服务路由功能。
  • 服务治理。 Sidecar 可以完全拦截服务进出的流量,并对其进行相应的调用链跟踪、熔断、降级、日志监控等操作,将服务治理功能集中在 Sidecar 中实现。
  • 集中管控。 整个微服务架构体系下的所有服务完全可以通过 Sidecar 来进行集中管控,完成对服务的流控、下线等。

于是,应用服务终于可以做到跨语言开发、并更专注于业务逻辑的开发。

1.2.4 Service Mesh

Sidecar 模式充分应用于一个庞大的微服务架构系统,为每个应用服务配套部署一个 Sidecar 代理,完成服务间复杂的通信,最终就会得到一个如下所示的网络拓扑结构,这就是 Service Mesh,又称之为“服务网格“。

Service Mesh阶段

至此,迎来了新一代微服务架构——Service Mesh,它彻底解决了传统微服务架构所面临的问题。

1.3 什么是Service Mesh

在开始进入主题之前,我认为有必要再对 Service Mesh 进行统一的阐述,这样将有助于理解它,更加便于阅读接下来的内容。

1.3.1 Service Mesh介绍

Service Mesh翻译为“服务网格”,作为服务间通信的基础设施层。轻量级高性能网络代理,提供安全的、快速的、可靠地服务间通讯,与实际应用部署一起,但对应用透明。应用作为服务的发起方,只需要用最简单的方式将请求发送给本地的服务网格代理,然后网格代理会进行后续的操作,如服务发现,负载均衡,最后将请求转发给目标服务。

Service Mesh目的是解决系统架构微服务化后的服务间通信和治理问题。 服务网格由Sidecar节点组成,这个模式的精髓在于实现了数据面(业务逻辑)和控制面的解耦。具体到微服务架构中,即给每一个微服务实例同步部署一个Sidecar

ServiceMesh部署网络结构图

Service Mesh部署网络结构图中,绿色方块为应用服务,蓝色方块为 SideCar,应用服务之间通过Sidecar进行通信,整个服务通信形成图中的蓝色网络连线,图中所有蓝色部分就形成了Service Mesh。其具备如下主要特点:

  • 应用程序间通讯的中间层。
  • 轻量级网络代理。
  • 应用程序无感知。
  • 解耦应用程序的重试/超时、监控、追踪和服务发现。

Service Mesh 的出现解决了传统微服务框架中的痛点,使得开发人员专注于业务本身,同时,将服务通信及相关管控功能从业务中分离到基础设施层。

1.3.2 Service Mesh的功能

那么 Service Mesh 到底能够做什么呢?

Service Mesh 作为微服务架构中负责网络通信的基础设施层,具备网络处理的大部分功能。下面列举了一些主要的功能:

  • 动态路由。 可通过路由规则来动态路由到所请求的服务,便于不同环境、不同版本等的动态路由调整。
  • 故障注入。 通过引入故障来模拟网络传输中的问题(如延迟)来验证系统的健壮性,方便完成系统的各类故障测试。
  • 熔断。 通过服务降级来终止潜在的关联性错误。
  • 安全。Service Mesh上实现安全机制(如TLS),并且很容易在基础设施层完成安全机制更新。
  • 多语言支持。 作为独立运行且对业务透明的 Sidecar 代理,Service Mesh 很轻松地支持多语言的异构系统。
  • 多协议支持。 同多语言一样,也支持多协议。
  • 指标和分布式链路追踪。

概括起来,Service Mesh 主要体现在以下4个方面:

  • 可见性: 运行时指标遥测、分布式跟踪。
  • 可管理性: 服务发现、负载均衡、运行时动态路由等。
  • 健壮性: 超时、重试、熔断等弹性能力。
  • 安全性: 服务间访问控制、TLS 加密通信。

1.3.3 Service Mesh解决什么问题

从上述Service Mesh的介绍和功能来看:

  • 基础设施层是Service Mesh的定位,致力于解决微服务基础设施标准化、配置化、服务化和产品化的问题。
  • 服务间通信是Service Mesh技术层面对的问题,对微服务屏蔽通信的复杂度,解决微服务的通信治理问题。
  • 请求的可靠传递是Service Mesh的目标。
  • 轻量级网络代理是Service Mesh的部署方式。
  • 对应用程序透明是Service Mesh的亮点和特色,实现对业务无侵入。

综合上述,Service Mesh主要解决用户如下3个维度的痛点需求:

  • 完善的微服务基础设施

    通过将微服务通信下沉到基础设施层,屏蔽了微服务处理各种通信问题的复杂度,形成微服务之间的抽象协议层。开发者无需关心通信层的具体实现,也无需关注RPC通信(包含服务发现、负载均衡、流量调度、流量降级、监控统计等)的一切细节,真正像本地调用一样使用微服务,通信相关的一起工作直接交给Service Mesh

  • 语言无关的通信和链路治理

    功能上,Service Mesh并没有提供任何新的特性和能力,Service Mesh提供的所有通信和服务治理能力在Service Mesh之前的技术中均能找到,比如Spring Cloud就实现了完善的微服务RPC通信和服务治理支持。

    Service Mesh改变的是通信和服务治理能力提供的方式,通过将这些能力实现从各语言业务实现中解耦,下沉到基础设施层面,以一种更加通用和标准化的方式提供,屏蔽不同语言、不同平台的差异性,有利于通信和服务治理能力的迭代和创新,使得业务实现更加方便。

    Service Mesh避免了多语言服务治理上的重复建设,通过Service Mesh语言无关的通信和服务治理能力,助力于多语言技术栈的效率提升。

  • 通信和服务治理的标准化

    • 微服务治理层面Service Mesh是标准化、体系化、无侵入的分布式治理平台。
    • 标准化方面Sidecar成为所有微服务流量通信的约束标准,同时Service Mesh的数据平台和控制平面也通过标准协议进行交互。
    • 体系化方面,从全局考虑,提供多维度立体的微服务可观测能力(MetricTraceLogging),并提供体系化的服务治理能力,如限流、熔断、安全、灰度等。

    通过标准化,带来一致的服务治理体验,减少多业务之间由于服务治理标准不一致带来的沟通和转换成本,提升全局服务治理的效率。

1.3.4 Service Mesh框架选型

下面对针对目前市面上常见的 Service Mesh 框架进行的比较汇总,见下表所示:

功能 Linkerd 2 Envoy Istio Conduit
代理 Finagle + Jetty Envoy Envoy Conduit
熔断 支持。基于连接的熔断器Fast Fail和基于请求的熔断器Failure Accrual 支持。通过特定准则,如最大连接数、 最大请求数、最大挂起请求数或者最大重试数的设定。 支持。通过特定准则,如最大连接数和最大请求数等的设定。 暂不支持。
动态路由 支持。通过设置Linkerddtab规则实现不同版本服务请求的动态路由。 支持。通过服务的版本或环境信息实现。 支持。通过服务的版本或环境信息实现。 暂不支持。
流量分流 支持。以增量和受控的方式实现分流。 支持。以增量和受控的方式实现分流。 支持。以增量和受控的方式实现分流。 暂不支持。
服务发现 支持。支持多种服务发现机制,如基于文件的服务发现、ConsulZookeeperKubernetes等。 支持。通过提供平台无关的服务发现接口实现与不同服务发现工具集成。 支持。通过提供平台无关的服务发现接口实现与不同服务发现工具集成。 只支持Kubernetes
负载均衡 支持。提供多种负载均衡算法。 支持。提供多种负载均衡算法,如Round Robin、加权最小请求、哈希环、Maglev等。 支持。提供多种负载均衡算法,如Round Robin、加权最小请求、哈希环、Maglev等。 支持。当前只有HTTP请求支持基于P2C + least-loaded的负载均衡算法。
安全通信 支持TLS 支持TLS 支持TLS 支持TLS
访问控制 不支持。 不支持。 支持。基于RBAC的访问控制。 暂不支持。
可见性 分布式追踪(Zipkin)、运行时指标(InfluxDBPrometheusstatsd) 分布式追踪(Zipkin)、运行时指标(statsd) 分布式追踪(Zipkin)、运行时指标(Prometheusstatsd)、监控(NewRepicStackdriver) 运行时指标(Prometheus)
部署模式 sidecar或者per-host模式 sidecar模式 sidecar模式 sidecar模式
控制平面 Namerd 没有,但可通过API实现。 PilotMixerCitadel Conduit
协议支持 HTTP/1.xHTTP/2gRPC HTTP/1.xHTTP/2gRPCTCP HTTP/1.xHTTP/2gRPCTCP HTTP/1.xHTTP/2gRPCTCP
运行平台 平台无关 平台无关 目前支持Kubernetes,平台无关是最终实现目标。 只支持Kubernetes

上述任何一个 Service Mesh 框架都能够满足您的基本需求。到⽬前为⽌,Istio 具有这几个服务⽹格框架中最多的功能和灵活性,灵活性意味着复杂性,因此需要团队更为充⾜的准备。如果只想使⽤基本的 Service Mesh 治理功能,Linkerd 可能是最佳选择。如果您想⽀持同时包含 KubernetesVM 的异构环境,并且不需要 Istio 的复杂性,那么 Consul 可能是您的最佳选择,⽬前 Istio 也提供了同时包含 KubernetesVM 的异构环境的⽀持。

从另一个角度来看,目前 Istio 社区正在快速迭代以应对各种场景,并力争作为 Service Mesh 的标杆,本文以选取 Istio 框架作为最终迁移框架。

1.4 框架迁移迫在眉睫

为了更好的占领市场,满足更多业务场景的需求,传统微服务架构(如,基于 Spring Cloud 框架的微服务架构)面临了众多新的挑战,而 Service Mesh 的出现正好解决了这些问题。面对新的框架体系,对于传统微服务架构该如何应对?

对于 Spring Cloud 框架的微服务向 Service Mesh 框架迁移必将迫在眉睫,是推翻重来,还是循序迁移?如果迁移,又该如何?

2、Service Mesh迁移方案

对于还未涉足 Service Mesh 的企业或产品,其传统微服务架构如若已采用 Spring Cloud 框架构建,此时向Service Mesh 框架迁移该如何做呢?需要综合考虑哪些因素?是否有依可据呢?

接下来,我们就构建基于Spring CloudService Mesh 框架迁移提供一些建议方案和思路,供大家参考。

2.1 迁移场景

传统微服务框架,我们以最为典型的 Spring Cloud 框架为例进行迁移说明。首先,我们先看一下这样的一个迁移场景,目前的微服务架构是这样的,如下图左边部分:

  • 应用是部署在虚拟机或物理机上。(服务还未容器化)
  • 框架是基于 Spring Cloud 框架开发。(服务中包含的业务逻辑和 Spring Cloud 组件相依赖,业务和框架耦合度过高)
  • 开发语言是以Java为主。(存在跨语言的问题)
  • 注册中心采用的是 ConsulEureka。(服务需引入注册中心依赖包,存在一定的耦合)

目前开源 Istio 已经成为 Service Mesh 事实上的标准,更是新一代微服务架构发展的趋势,因此公司希望尝试迁移到 Istio 框架,希望最终形成类似下图右边部分。

2.2 迁移路径

面对上述迁移场景,确定要引入 Service Mesh 时,就要彻底搞清楚 Service Mesh 迁移的具体路径。

首先,要对自己项目做以下评估:

  • 是否真的有必要引入迁移到 Service Mesh 上?
  • 当前微服务架构下,是否面临传统微服务架构面临的挑战?
  • 当前微服务架构,是否已经阻碍或影响未来业务的发展?
  • 公司或技术团队,是否有能力、人力、精力来投入到 Service Mesh 的迁移?

其次,完成 Service Mesh 微服务平台的搭建。当前所处阶段是否已经支持容器化和 Kubernetes。如果当前业务已经运行在 Kubernetes 之上,则 Service Mesh 的迁移将会比较顺畅;如果当前业务没有运行在Kubernetes上,因 Service Mesh 当前典型的 Istio 框架对 Kubernetes 有着过度依赖,所以可能就无法直接从 Spring Cloud 迁移到 Istio 框架,即使定制修改 Istio 以接触对 Kubernetes 的依赖,将会付出很大的代价。这时通常有两条迁移路径可以选择。

路径一:非 Kubernetes 环境下,先接入 Sidecar

如果当前业务没法快速容器化,同时又有引入 Service Mesh 的迫切需求,可采取先接入 Sidecar,来满足当前业务的痛点需求。在引入 Sidecar 时,要注意其未来的演进方向,考虑后续可能继续向 Service Mesh 迁移,一旦时机成熟并引入 Kubernetes 容器化后,则能够顺利由 Sidecar 的方式直接演进到 Service Mesh

Service Mesh 当前典型的 Istio 框架在非 Kubernetes 下没有很好的支持(据说未来会完全脱离对Kubernetes 的依赖),对 Istio 进行定制化修改以支持非 Kubernetes 环境将会付出很大的代价,非特别强烈的需求和强大的技术储备,一般不建议这么做,特别是对于一些中小公司而言。

如果一定要在非 Kubernetes 环境下引入 Service Mesh,数据平面可使用 Envoy,控制平面可根据 XDS 协议进行自研。

路径二:先进行 Kubernetes 容器化改造,再接入 Service Mesh

倘若公司有云平台或容器化团队,可采用公司资源共享的方式,先借助其他团队来完成 Kubernetes 容器化改造,再接入 Service Mesh

最后,基于构建的 Service Mesh 框架,将业务应用逐步迁移到 Service Mesh 上来。

2.3 迁移原则

在实施迁移时,必须要时刻遵守以下迁移原则。

  • 渐进式迁移: 为了避免 Service Mesh 迁移过程中的风险,必须采用渐进式迁移原则,每次只迁移少量服务,待迁移后观察足够长的时间,没有问题后再继续迁移。

  • 业务透明: 为减少 Service Mesh 迁移对业务的影响,减少业务的迁移阻力,迁移初期必须确保业务完全透明且不需要过多的变更和修改。

    方案上,为保证迁移对业务的完全透明,在数据平面通信上可采用支持透明拦截的方式,对业务请求流量透明拦截。

  • 兼容性: 在迁移阶段,必然会存在两种模式( Spring CloudService Mesh 框架)并存,在迁移过程中需要充分考虑两者的兼容性,使得迁移前后网络打通,至少能够满足未迁移和已迁移部分能够通信。

2.4 迁移方案

Spring CloudService Mesh 框架迁移,大体上分为四个步骤:Spring Cloud 架构分析、容器化改造、Service Mesh 微服务平台搭建和应用迁移。

2.4.1 Spring Cloud架构分析

Spring Cloud 架构分析的目的在于重新了解我们当前微服务架构下的所有功能,便于在向 Service Mesh 迁移时做准备,考虑哪些功能需要迁移,哪些不需要迁移,哪些需要改造等。我们先看一下基于 Spring Cloud 完整构建的微服务架构解决方案,如下图所示。

从上图经过分析,我们可以汇总得知它主要由以下几部分组成:

  • 代理&网关: 提供统一对外或对内的访问入口,包括路由、鉴权、限流、熔断、降级等统一处理。
  • 注册中心: 提供服务的注册与发现功能。
  • 应用服务: 覆盖整个业务服务,包括业务逻辑实现、框架SDK及外部组件依赖交互等。
  • 中间件&数据存储: 为应用服务提供额外的支持能力。
  • CI&CD: 持续集成、持续部署。

上述这几部分中哪些内容是我们可以去掉或者说基于 Service Mesh (以 Istio 为例)能够去做的?经过分析得知,可以替换的组件包括网关(Gateway 或者 Zuul,由 Ingress gateway 或者 egress 替换),熔断器(hystrix,由 Sidecar 替换),注册中心(EurekaEureka client,由 PolitSidecar 替换),负责均衡( Ribbon,由 Sidecar 替换)等。

此阶段,我们能够大致知道 Spring Cloud 中的哪些内容可以由 Istio 处理,哪些内容可以继续沿用。

2.4.2 容器化改造

容器化改造,主要针对目前还未引入 Kubernetes 容器化的场景。

在容器化改造之前,我们有必要知道改造的优势及要求。

容器化改造优势:

  • 更省: 极大的资源利用效率, 最大限度榨取和共享物理资源,多项目更能体现出容器化多优势,节约部署 IT 成本。
  • 更快: 秒级启动,实现业务系统更快的开发迭代 和 交付部署。
  • 弹性: 可根据业务负载进行弹性容器伸缩,弹性扩展。
  • 方便: 容器化业务部署支持蓝绿/灰度/金丝雀等发布,回滚,更加灵活方便。
  • 灵活: 监控底层 node 节点健康状态,灵活调度至最优节点部署。
  • 强一致性: 容器将环境和代码打包在镜像内,保证了测试与生产环境的强一致性。

容器化改造要求:

  • 掌握 Docker 技术: 开发人员需熟悉 Docker 容器化技术,熟练编写 Dockerfile 文件。

  • 掌握 Kubernetes 编排系统: 熟悉 Kubernetes 容器化编排系统, 熟悉各组件资源清单编写、高可用、 RBAC 安全策略等。

容器化改造,主要分为以下两个阶段:

  • 容器化构建: 将基于 Spring Cloud 搭建的所有服务实现容器化构建,实现 Docker 镜像打包。
  • 容器化管理: 基于 Kubernetes 进行服务容器的管理。
2.4.2.1 容器化构建

容器化构建需借助编写的 Dockerfile 文件,并通过 Jenkins 自动化完成对 Docker 镜像的制作。这里以一个简单的serviceProvider服务(基于 Spring Cloud 框架开发)为例说明构建过程:

(1)创建 Dockerfile 文件。

在服务serviceProvider/src/main/docker目录下创建一个 Dockerfile 文件,内容如下:

FROM java:8
RUN mkdir /microservice
WORKDIR /microservice
ADD /service-provider-1.0.jar /microservice/
EXPOSE 8001
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/microservice/service-provider-1.0.jar"]

(2)配置pom docker-maven-plugin插件。

在服务serviceProviderpom.xml 中配置 build 部分,内容如下:

<!-- 打包配置 -->
 <build>
  <defaultGoal>install</defaultGoal>
  <!-- build后的文件名,默认值是${artifactId}-${version}。  -->
     <finalName>service-provider-${project.version}</finalName>
    
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
   
   <!-- 配置docker maven插件,绑定install生命周期,在运行maven install时生成docker镜像 -->
         <plugin>
             <groupId>com.spotify</groupId>
             <artifactId>docker-maven-plugin</artifactId>
             <version>0.4.13</version>
             <executions>
                 <execution>
                     <phase>install</phase>
                     <goals>
                         <goal>build</goal>
                         <goal>tag</goal>
                     </goals>
                 </execution>
             </executions>
             <configuration>
                 <!-- 修改这里的docker节点ip,需要打开docker节点的远程管理端口2375。可在hosts中进行dockerip的配置 -->
                 <dockerHost>http://dockerip:2375</dockerHost>
                 <imageName>${project.build.finalName}</imageName>
                 <baseImage>java</baseImage>
                 <!-- 这里的entryPoint定义了容器启动时的运行命令,容器启动时运行 java -jar 包名 , -Djava.security.egd这个配置解决tomcat8启动时,因为需要收集环境噪声来生成安全随机数导致启动过慢的问题-->
                 <entryPoint>
                     ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/${project.build.finalName}.jar"]
                 </entryPoint>
                 <resources>
                     <resource>
                         <targetPath>/</targetPath>
                         <directory>${project.build.directory}</directory>
                         <include>${project.build.finalName}.jar</include>
                     </resource>
                 </resources>
                 <image>${docker.image.prefix}/${project.build.finalName}</image>
                 <newName>${docker.image.prefix}/${project.build.finalName}:${docker.tag}</newName>
                 <forceTags>true</forceTags>
                 <!-- 如果需要在生成镜像时推送到远程库,pushImage设为true -->
                 <pushImage>false</pushImage>
             </configuration>
         </plugin>
  </plugins>
 </build>

(3)打包。

在执行 mvn package 的时候,会根据 pom.xmlbuild 配置自动打包 Docker 镜像,并推送到Docker服务器上。

至此,就顺利地完成了容器化构建,这一步对于原有服务基本没有影响,打包成功后只需进行验证测试即可,以确保镜像打包存在问题。

2.4.2.2 容器化管理

容器化管理实际上就是将服务容器通过 Kubernetes编排系统,完成对服务部署、管理等,需要对 Kubernetes 有一定的了解,会熟练使用它,看参考之前写的”Kubernetes从入门到精通“系列文章。

2.4.3 Service Mesh 微服务平台搭建

Service Mesh 我们选取 Istio 框架,关于选型方案可参考之前文章:Service Mesh 框架选型对比分析:Linkerd、Envoy、Istio、Conduit

2.4.3.1 istio基础框架搭建

基于 Istio 框架搭建 Istio 基础框架,在控制平面和数据平台提供分别提供以下能力:

  • 控制平面: 提供服务⽹格控制指令下发、服务配置、 权限控制等功能。
  • 数据平台: 提供服务治理、服务监控及运维、流量管控等功能。

上述功能在 Istio 框架上都能找到对应的功能,并通过适当的资源清单配置即可完成。

Istio 架构图如下:

至此,一个 Istio 基础框架搭建完成,能够提供 Service Mesh 的所有能力。

2.4.3.2 istio扩展和定制

在迁移路径中已经提及过,对于非 Kubernetes 环境,建议先引入 Sidecar,并采取 istio 对虚拟机的支持方案,在虚拟机环境下运行。但如果有多平台支持的场景,比如既有 Kubernetes 环境,又有虚拟机环境,需对 istio 进行定制化改造,去掉对 Kubernetes 的强依赖和耦合,增加对其他平台的支持。(对于多平台的支持,目前istio 还未支持,但从 istio 官方相关文档可以看出,多平台的支持最终肯定支持,我们只需拭目以待。)

IstioKubernetes 的耦合主要有以下几个方面,因此需要针对性的适配修改。

(1)API资源管理层对 Kubernetes API Server 的依赖

资源管理层是 IstioKubernetes 依赖最大的地方。Istio 对核心资源的管理,是以 Kubernetes CRD 为基础,并使用 kubectl 作为命令行操作入口,kubectl 调用 API Server,将资源存放在 etcd 中,并通过 Kubernetes CRD 机制触发资源变更事件通知,通知关心 Istio 资源变更事件的模块进行相关处理。

如需解除IstioKubernetes 的绑定,则需要自行实现这一套API管理方式,并且做到平台无关。

(2)通信访问层面对 kube DNS 的依赖

通信层面,在客户端发送请求前,先通过 DNS 获取服务的虚拟IP地址,IstioDNS 实现沿用Kubernetes DNS 方案,基于 DNS 通过服务名实现直接访问。因此需要在 DNS 方案层面接触和Kubernetes 的耦合,并使用平台无关的 DNS 解决方案。

2.4.3.3 两种框架并存

对于体量较大的业务,不可能一次性迁移完成,需遵守“渐进式迁移”原则,逐步迁移,所以实际迁移过程中可能面临这样的诉求:

  • 一些存量老业务运行在虚拟机或者物理机上,暂时没有容器化改造计划,但希望通过 Service Mesh 来做服务治理。

  • 新上的业务或者存量的非关键业务可以做为试点,先容器化、Service Mesh 化,其它业务依然采用原有的运行方式和微服务框架。

  • 对于未迁移的存量应用和迁移完成的 Service Mesh 应用依然能保持业务上的互通。

面对上述这些真实而又合理的诉求,在进行 Service Mesh 微服务平台搭建时,必然会存在两种框架并存的场景,如下图所示,左边是未迁移的存量服务,右边是容器化并 Service Mesh 化的试点服务,但这种模式服务间却是互不相同,且无法统一治理。

那么两种框架并存时,如何服务间互通,统一治理呢?

在业内流行这样一句话:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。

同样,我们可以针对 Service Mesh 的控制平面做些文章,通过自定义控制插件(WASM)将 Spring Cloud 框架中原有注册中心的功能纳入进来,由控制平面提供原有服务注册与发现的能力,并结合 Istio 中入口网关 IngressServiceEntry 资源配置,以实现服务间互通,统一治理,整个实现逻辑架构如下图所示。

至此,实现了基于Spring CloudIstio 两种框架的并存。

2.4.4 应用迁移

到这里,我们已经完成了 Service Mesh 微服务平台的搭建,那在这样的平台上我们如何将应用 Spring Cloud 应用逐步向 Service Mesh 迁移呢?

2.4.4.1 去除重叠功能

我们先来看一下 Spring Cloud 框架与 Istio 框架的功能重叠情况:

功能列表 Spring Cloud Isito
服务注册与发现 支持,基于Eureka,consul等组件。 支持,基于XDS接口获取服务信息,并依赖“虚拟服务路由表”实现服务发现,可灵活扩展任何注册中心。
链路监控 支持,基于ZikpinPinpoint或者Skywalking 实现。 支持,基于Sidecar 代理模型,记录网络请求信息。
服务网关 支持,基于 zuul 或者 Spring-Cloud-gateway 实现。 支持,基于Ingress gateway 以及 egress实现。
熔断器 支持,基于Hystrix 实现。 支持,基于声明配置文件,最终转化成路由规则实现。
服务路由 支持,基于网关层实现路由转发。 支持,基于iptables规则实现,通过 VirtualServiceDestinationRule灵活配置。
安全策略 支持,基于 spring-security组件实现,包括认证,鉴权等,支持通信加密。 支持,基于 RBAC 的权限模型,依赖 Kubernetes实现,同时支持通信加密。
配置中心 支持,Spring Cloud Config组件实现。 不支持。
性能监控 支持,基于 Spring cloud 提供的监控组件收集数据,对接第三方的监控数据存储。 支持,基于Sidecar代理,记录服务调用性能数据,并通过metrics adapter,导入第三方数据监控工具。
日志收集 支持,对接第三方日志系统,例如 ELK 支持,基于 Sidecar 代理,记录日志信息,并通过log adapter,导入第三方日志系统。

从上表功能情况,存在大量重叠功能,需将Spring CloudIstio 中重叠功能去除,缺失功能保留,理论上可轻松去重。对于 Spring Cloud 而言,这些重叠功能大部分只需去除 pom.xml 中依赖包、相关配置及代码中注解即可轻松完成,剩余一个相对干净的应用。

2.4.4.2 应用注入

应用注入是指在将应用服务部署到网格时,将 Sidecar 注入到应用服务中去,以实现网格的代理。

Sidecar 注入,分为手动注入和自动注入:

  • 手动注入: 通过手动执行 istioctl kube-inject 来重新构造应用的 CRD yaml

  • 自动注入: 通过 Kubernetesmutable webhook 回调 istio-sidecar-injector 服务来重新构造应用的 CRD yaml

如下图所示:

无论是手动注入还是自动注入,Sidecar 注入的本质是将运行 Sidecar 所需要的镜像地址、启动参数、所连接的 Istio 集群(PilotCitadelGalley)及配置信息填充到注入模版,并添加到应用的 CRD yaml 中,最终通过 Kubernetes 持久化资源并拉起应用和 SidecarPOD

此时,应用已成功迁移部署到 Service Mesh 中了。

3、总结

这篇文章从传统微服务架构开始一步步介绍到 Service Mesh,并提出了传统微服务架构面临的挑战,针对现状,如何能够更好的满足市场需求,而不被市场淘汰,介绍了传统微服务如何平滑迁移至 Service Mesh 的过程,并给出了一些解决方案、步骤及思路,供大家参考。

希望能够帮您解决实际迁移过程中遇到的问题,能够帮助大家在做架构演进或迁移时带来一些思考和启发。

参考资料:

  1. https://istio.io/latest/docs/concepts/what-is-istio/
  2. 刘俊海 著《Service Mesh微服务架构设计》
  3. https://mp.weixin.qq.com/s/y9PZLgHhVcdsMuTzAyIMsQ
  4. https://www.servicemesher.com/blog/service-mesh-rebuild-microservice-market/
  5. https://mp.weixin.qq.com/s/-MszFJORuDJKf3V5ndyimw
  6. https://www.servicemesher.com/blog/netease-yeation-service-mesh/