2017年,这一年在容器技术发展史上具有重要的分水岭意义,标志着“后微服务时代”的开始。这一年,发生了几件重大事件,彻底改变了容器管理领域的格局。
首先是 CoreOS,一直以来与 Docker 竞争的 RKT 容器技术的领头羊,宣布放弃其容器管理系统 Fleet,转而支持 Kubernetes。接着,容器管理行业的领头羊 Rancher Labs 也放弃了自家多年开发的 Cattle 系统,采纳“All-in-Kubernetes”的策略,从此 Rancher 2.0 版本起只支持 Kubernetes。
同年,Kubernetes 的主要对手 Apache Mesos 宣布了与 Kubernetes 的集成计划,“Kubernetes on Mesos”,这标志着他们从竞争对手转变为支持者,让 Kubernetes 能够与 Mesos 的其他顶级框架(例如 HDFS、Spark 和 Chronos 等)实现资源的动态共享和隔离。
此外,2017年10月,Docker 的母公司,也是 Kubernetes 最大的竞争对手之一,宣布 Docker 将同时支持 Swarm 和 Kubernetes,这在事实上承认了 Kubernetes 的领导地位。
这些事件标志着长达三至四年的容器技术竞争战争,主要围绕 Docker Swarm、Apache Mesos 和 Kubernetes,最终以 Kubernetes 的胜利告终。Kubernetes 的崛起不仅是容器技术发展的一个重要里程碑,也预示着软件架构发展新纪元的到来。
在采用了基于 Spring Cloud 的微服务架构之后,小书店 Fenix's Bookstore(虚拟) 成功地应对了伸缩性、独立部署、运维管理等方面的挑战,以及产品经理提出的日益增长的复杂业务需求。然而,对于团队中的开发者、设计师和架构师来说,工作并没有变得更加轻松。微服务所涉及的新技术术语,如配置中心、服务发现、网关、熔断、负载均衡等,对新手来说学习曲线陡峭。而从产品的角度看,Spring Cloud 的各种组件,如 Config、Eureka、Zuul、Hystrix、Ribbon、Feign 等,也构成了产品编译后代码的主要部分。
微服务架构选择在应用层面解决分布式问题,而不是在基础设施层面,主要是因为软件的应用服务比硬件基础设施更加灵活,而后者难以跟上前者的步伐。但是,随着 Kubernetes 在容器编排管理方面的统一,这些技术性的底层问题开始在基础设施层面找到了广泛认可的解决方案。因此,Fenix's Bookstore 开始了它在“后微服务时代”的又一次架构升级,此次升级主要集中在两个目标上。
Fenix's Bookstore 的微服务架构升级主要围绕两个核心目标进行。第一个目标是减少非业务功能代码的占比。在这家书店的系统中,用户服务(Account)、商品服务(Warehouse)和交易服务(Payment)是承担实际业务逻辑的核心模块。而认证授权服务(Security)则同时涉及技术和业务层面,配置中心(Configuration)、网关(Gateway)和服务注册中心(Registry)则完全是技术性质的组件。目的是尽可能地消除这些纯技术组件,以及那些附属于其他业务模块的技术性功能。
第二个目标是在尽量不改变原有代码的情况下完成迁移。依托于 Spring Framework 4 中的 Conditional Bean 等声明式编程特性,现代 Java 技术组件越来越倾向于声明式(Declarative Programming)而非命令式编程(Imperative Programming)。这样的编程风格允许开发者从目标出发描述编码意图,而不是围绕具体的技术实现过程,从而减少代码与技术实现的耦合。如果需要更换技术实现,只需调整配置声明即可。
从升级的角度看,如果仅以 Java 代码来衡量,这次迁移到 Kubernetes 后的项目与之前基于 Spring Cloud 的版本在代码层面上没有任何区别,每一行 Java 代码都保持不变。但实际上,区别在于 Kubernetes 实现中直接删除了配置中心和服务注册中心等工程,在其他工程的 pom.xml 文件中也移除了像 Eureka、Ribbon、Config 等组件的依赖。取而代之的是引入了一系列以 YAML 配置文件为基础的 Skaffold 和 Kubernetes 资源描述。这些资源描述文件将动态构建出 DNS 服务器、服务负载均衡器等虚拟化基础设施,替代了原有应用层面的技术组件。升级后的应用架构如下图所示:
Fenix's Bookstore 采用基于 Kubernetes 的微服务架构,并采用 Spring Cloud Kubernetes 做了适配,其中主要的技术组件包括以下几种。
在 Fenix's Bookstore 的微服务架构中,为了实现容器环境的感知,我们使用了Spring Cloud Kubernetes,它集成了 Fabric8 的 Kubernetes Client。但是,Spring Cloud Kubernetes 版本 1.1.2 中使用的 Fabric8 Kubernetes Client 版本是 4.4.1,这个版本根据 Fabric8 提供的兼容性列表,仅支持到 Kubernetes 1.14。尽管在 Kubernetes 1.16 上也能运行,但在 1.18 版本上就无法正确识别 Api-Server。因此,在 Maven 项目中添加依赖时,需要手动排除旧版本,并引入更新的版本(在这个项目中使用的是 4.10.1)。
配置管理方面,项目利用 Kubernetes 的 ConfigMap 来进行配置管理,并通过 Spring Cloud Kubernetes Config 自动地将 ConfigMap 的内容注入到 Spring 的配置文件中,实现动态更新。服务发现是通过 Kubernetes 的 Service 实现的,Spring Cloud Kubernetes Discovery 能自动将 HTTP 服务请求转换为完全限定域名(FQDN)。负载均衡方面,则直接利用 Kubernetes Service 的内置负载均衡功能(即 DNS 负载均衡),因此不再需要像 Ribbon 这样的客户端负载均衡组件。从 Spring Cloud Kubernetes 1.1.2 开始,Ribbon 的适配支持已被移除,且暂时没有对 Spring Cloud LoadBalancer 这一替代品提供适配。
至于服务网关,虽然保留了 Zuul,但没有采用 Kubernetes 的 Ingress 来替代。这里有两个主要考虑因素:首先,Ingress Controller 并非 Kubernetes 的内置组件,有多种可选方案(例如 KONG、Nginx、Haproxy 等),且需要独立安装。为了保持演示项目的环境简单,我选择不使用 Ingress;其次,考虑到 Fenix's Bookstore 的前端项目存放在网关中,即使移除了 Zuul,仍然需要保持一个前端项目的存在。因此,移除 Zuul 并不能进一步减少项目数量,这减弱了移除 Zuul 的必要性
在 Fenix's Bookstore 的微服务架构更新中,服务熔断功能仍然由 Hystrix 实现。由于 Kubernetes 本身不提供细致的服务治理能力,如熔断、流量控制和监控等,我们计划在后续基于 Istio 的服务网格架构中解决这些问题。在认证授权方面,我们继续使用 Spring Security OAuth 2.0。虽然 Kubernetes 的 RBAC(基于角色的访问控制)能够处理服务层面的访问控制问题,但 Spring Security 跨越了业务和技术的界限,其认证授权模块仍然负责前端用户的认证和授权,这是与业务直接相关的部分。
它是基于 Apache 2.0 协议授权的。遵守这一许可协议的条件下,你可以自由地对代码进行修改和重新发布,甚至用于商业目的。但是,你需要遵守以下要求:署名在原始代码及其衍生代码中保留原作者的署名和代码来源信息;保留许可证在原始代码及其衍生代码中保留 Apache 2.0 协议文件。