10.3 构建和部署管道的架构

 
    本章的目标是为读者提供构建和部署管道的工作组件,以便读者可以将这些组件定制到自己的特定环境。
    让我们通过查看构建和部署管道的通用架构以及它表现出的一些通用模式来开始讨论。为了保持这些示例的流畅,我做了一些我通常不会在自己的环境中做的事情,我会相应地介绍这些东西。
    关于部署微服务的讨论将从第1章中看到的图开始。图10-15是在第1章中看到的图的副本,它展示了搭建微服务构建和部署管道所涉及的组件和步骤。
图10-15 构建和部署管道中的每个组件都会自动执行原本手动完成的任务
 
图10-15看起来有些熟悉,因为它是基于用于实现持续集成(Continuous Integration,CI)的通用构建-部署模式的。
    (1)开发人员将他们的代码提交到源代码存储库。
    (2)构建工具监视源代码控制存储库的更改,并在检测到更改时启动一个构建。
    (3)在构建期间,将运行应用程序的单元测试和集成测试。如果一切都通过,就会创建一个可部署的软件制品(一个JAR、WAR或EAR)。
    (4)这个JAR、WAR或EAR可能被部署到运行在服务器上的应用程序服务器(通常是一个开发服务器)。
    有了这个构建和部署管道(如图10-15所示),将执行类似的过程,直到代码为部署做好准备。在图10-15所示的构建和部署中,我们将持续交付添加到了这个过程中。
    (1)开发人员将他们的代码提交到源代码存储库。
    (2)构建/部署引擎监视源代码存储库的更改。如果代码被提交,构建/部署引擎将检查代码并运行代码的构建脚本。
    (3)构建/部署过程的第一步是编译代码,运行它的单元测试和集成测试,然后将服务编译成可执行软件制品。因为我们的微服务是使用Spring Boot构建的,所以构建过程将创建一个可执行的JAR文件,该文件包含服务代码和自包含的Tomcat服务器。
    (4)这是构建/部署管道开始与传统的Java CI构建过程有所不同的地方。在构建了可执行JAR之后,我们将使用部署到其中的微服务来“烘焙”机器镜像。这个烘焙过程的大致作用就是创建一个虚拟机镜像或容器(Docker),并将服务安装到它上面。虚拟机镜像启动后,服务将启动并准备开始接受请求。如果采取传统的CI构建过程,我们可能(我的意思是可能)将编译后的JAR或WAR部署到应用程序服务器,这个应用程序服务器与应用程序是分开(通常由一个不同的团队管理)独立管理的,而如果采取CI/CD过程,我们将微服务、服务的运行时引擎以及机器镜像部署为一个相互依赖的单元,这个单元由编写该软件的开发团队进行管理。这就是这两者之间的不同。
    (5)在正式部署到新环境之前,启动机器镜像,并针对正在运行的镜像运行一系列平台测试,以确定是否一切正常运行。如果平台测试通过,机器镜像将被提升到新环境中,并可使用。
    (6)在将服务提升到下一个环境之前,必须运行对这个环境的平台测试。将服务提升到新环境,需要把在较低环境下使用的确切的机器镜像启动到下一个环境。
    这就是整个过程的秘诀——部署整个机器镜像。在创建服务器之后,不会对已安装的软件(包括操作系统)进行更改。通过提升并始终使用相同的机器镜像,可以保证服务器从一个环境提升到另一个环境时保持不变。
    单元测试、集成测试和平台测试的对比
    从图10-15中可以看到,在构建和部署服务的过程中,我做了几种类型的测试(单元、集成和平台)。 在构建和部署管道中有3种类型的典型测试。
    单元测试——单元测试在服务代码编译之后,但在部署到环境之前立即运行。它们被设计成完全隔离运行,每个单元测试都是很小的,聚焦于某一点。单元测试不应该依赖于第三方基础设施数据库、服务等。通常单元测试的范围将包含单个方法或函数的测试。
    集成测试——集成测试在打包服务代码后立即运行。这些测试旨在测试整个工作流,并对需要被调用的主要服务或组件进行stub或mock。在集成测试过程中,可能会对第三方服务调用进行mock,运行一个内存数据库来保存数据等。集成测试负责测试整个工作流或代码路径。对于集成测试,需要对第三方依赖项进行stub或mock,以便任何调用远程服务的调用都会被stub或mock,通过这种方式,调用就不会离开构建服务器。
    平台测试——平台测试在服务部署到环境之前运行。这些测试通常测试整个业务流程,并调用通常在生产系统中调用的所有第三方依赖项。平台测试在特定的环境中运行,不涉及任何mock服务。平台测试用于确定与第三方服务的集成问题,这些问题在集成测试期间第三方服务被stub时,通常不会被检测到。
 
这个构建/部署过程是基于4个核心模式构建的。这些模式不是我创建的,而是来自构建微服务和基于云的应用程序的开发团队的集体经验。
    持续集成/持续交付(CI/CD)——使用CI/CD,应用程序代码不只是在代码提交时进行构建和测试的,它也在不断地被部署。代码的部署应该是这样的:如果代码通过了它的单元测试、集成测试和平台测试,它应该立即被提升到下一个环境中。在大多数组织中,唯一的停止点是在提升到生产环境这一环节。
    基础设施即代码——最终被推向测试以及更高的环境中的软件制品是机器镜像。在微服务的源代码被编译和测试之后,机器镜像和安装在它上面的微服务将立即被提供给开发人员。机器镜像的供应是通过一系列脚本执行的,这些脚本与每个构建一起运行。在构建完成后,没有人能触碰到服务器。镜像供应脚本保存在源代码控制之下,并像其他代码一样管理。
    不可变服务器——一旦建立了服务器镜像,服务器和微服务的配置就不会在供应过程之后被触碰。这可以保证环境不会因开发人员或系统管理员进行“一个小小的更改”而受到“配置漂移”的影响,并最终导致中断。如果需要进行更改,那么将更改提供给服务器的供应脚本,并启动一个新构建。
    关于凤凰服务器的不变性与重生
    有了不可变服务器的概念,我们应该始终保证服务器的配置与服务器机器镜像的完全一致。在不改变服务或微服务行为的情况下,服务器应该可以选择被杀死,并从机器镜像中重新启动。这种死亡和复活的新服务器被Martin Fowler称为“凤凰服务器”,因为当旧服务器被杀死时,新服务器应该从毁灭中再生。凤凰服务器模式有两个关键的优点。
    首先,它暴露配置漂移并将配置漂移驱逐出环境。如果开发人员不断地拆除并建立新服务器,那么很有可能会提前发现配置漂移。这对确保一致性有很大的帮助。由于配置漂移,我已经把太多的时间和生命都花在了远离家人的“危急情况”电话上。
    其次,通过帮忙发现服务器或服务在被杀死并重新启动后不能完全恢复的状况,凤凰服务器模式有助于提高弹性。请记住,在微服务架构中,服务应该是无状态的,服务器的死亡应该是一个微不足道的小插曲。随机地杀死和重新启动服务器可以很快暴露在服务或基础设施中具有状态的情况。最好是在部署管道中尽早发现这些情况和依赖关系,而不是在收到公司的紧急电话时再发现。
 
我工作的组织使用Netflix的Chaos Monkey随机选择并终止服务器。Chaos Monkey是一个非常宝贵的工具,用于测试微服务环境的不变性和可恢复性。Chaos Monkey随机选择环境中的服务器实例并杀死它们。使用Chaos Monkey是为了寻找无法从服务器丢失中恢复的服务,并且当一个新服务器启动时,新服务器的行为方式将与被杀死的服务器的行为方式相同。
 
 
posted @ 2019-12-03 11:03  mongotea  阅读(317)  评论(0编辑  收藏  举报