微服务应用开发完毕后,接下来要做的就是将已开发好的微服务应用部署到环境中。部署包含两个相互关联的概念:流程和架构。部署流程包括一些由开发人员和运维人员执行的步骤,以便将软件投入到生产环境(商用环境,任何修改都会影响到用户)。部署架构则定义了该软件运行的环境结构。随着自动化、虚拟化技术的发展和完善,早先将代码构建产物手动部署生产环境的历史,已经逐渐被基于流水线自动化部署替代,之前由物理机组成的生产环境,也已逐渐被轻量级、短生命周期的计算基础设施取代。
接下来将基于Spring Boot开发的Java应用为例,介绍下如何将微服务部署到生产环境中。其余语言或框架开发的微服务可类比处理。
包部署
基于Java语言开发的微服务部署到生产环境的一种直接的方法是将软件包部署到生产环境。对于包部署的场景,还需做好预先的准备。对于Java应用来说,还需安装JDK。如果是Web应用,还需安装Web容器,如Apache Tomcat。因为Spring Boot已经集成了Apache Tomcat,所以无需额外安装。配置完生产环境的计算机后,接下来就是将发布包复制到该计算机,并启动该服务。每个服务实例都作为JVM进程运行。
将包部署到环境的逻辑视图如下:
按上图所示,引入流水线后,基于源代码构建出可执行的发布包,然后流水线通过服务运行时管理将发布包部署到环境上。接着就可启动并运行该服务。
使用包部署的方式一个主要好处就是部署服务实例的速度相对较快:直接将服务的构建物复制到服务主机上并启动该服务即可。但是使用该方法也会带来以下问题:(1) 缺乏对技术栈的封装。部署时需要先做好预先准备。如对Java Web应用来说,需要Web容器和JRE。而且这些依赖还需要特定的版本。(2)无法约束服务实例的资源消耗。一个进程可能会消耗机器的所有CPU或内存,争用其他服务实例和操作系统的资源。如果一个服务出现内存泄露,则会导致其他服务和操作系统的不可用。(3) 在同一台计算机上运行多个服务实例缺少隔离。缺乏隔离意味着行为不当的服务实例可能会影响其他服务实例。因此,应用存在不可靠的风险,尤其是在同一台计算机上运行多个服务实例。
虚拟机部署
考虑到包部署的一些显著弊端,可尝试寻找一些替代方案,我们首先考虑的是虚拟机部署方式。如可以将服务构建物复制到已创建和配置好的云主机(如AWS的EC2)上。
通过流水线,将虚拟机部署到环境的逻辑视图如下:
如上图所示,虚拟机镜像构建器基于源码构建出虚拟机镜像。这个虚拟机镜像(如AWS Machine Image, AMI)包含服务代码和服务运行所需的依赖。每个服务实例都是基于虚拟机镜像所实例的虚拟机。
使用虚拟机部署的好处是:(1)封装了技术栈。可将服务及其依赖项封装到虚拟机镜像中。这样可以保证可以正确安装和设置服务运行所需的软件。虚拟机镜像可以无需修改的部署在任何地方。部署变得简单和可靠。(2)隔离的服务实例。虚拟机部署方式的一大好处是每个服务实例都以完全隔离的方式运行。每个虚拟机都有固定数量的CPU和内存,不能从其他服务中窃取资源。但是虚拟机部署存在以下问题:(1)资源利用率低。每个服务实例都拥有一整台虚拟机的开销,包括操作系统。(2)部署速度相对较慢。由于虚拟机中包含操作系统,所以虚拟机镜像构建的时间要更长(相比仅生成服务产物)。另外,必须通过网络传输完整的虚拟机镜像文件,然后从镜像实例化虚拟机。在虚拟机内部,运行操作系统,运行服务都需要时间。(3)操作系统管理的额外开销。将操作系统封装到虚拟机镜像里,一个不得不考虑的问题就是给该操作系统打补丁。
容器部署
容器是一种更现代化、更轻量级的部署机制,是一种操作系统级别的虚拟化机制。容器很好的实现了进程隔离。容器通常包括一个或多个在沙箱中运行的进程,这个沙箱将它们与其他容器隔离。示意图如下:
通过上图可知,通过容器运行时(如Docker已成为容器运行时的事实标准),多个容器可运行在同一台机器上,多个容器共享操作系统。容器比虚拟机实现了更细粒度的虚拟化。
创建容器时,可以指定它的CPU和内存资源,以及依赖于容器实现的I/O资源等。容器运行时强制执行这些限制,并防止容器占用其他机器的资源。
通过流水线,将容器部署到环境的逻辑视图如下:
在构建时,使用容器镜像构建工具,将服务打包成容器镜像,并将其存储到镜像仓库。在运行时,从镜像仓库拉取容器镜像,并用其创建容器。容器通常运行在虚拟机上。单个虚拟机通常会运行多个容器。
这里再次声明下容器镜像。容器镜像是由应用和运行该应用所需的依赖组成的文件系统镜像。它通常是一个完整的Linux根问题件系统,但更轻量级的镜像也可以使用。如对基于Spring Boot的服务来说,其容器镜像包含服务依赖的JRE、服务自身代码及依赖。
使用包部署方式有以下好处:(1)封装了技术栈。与虚拟机部署一样,容器可将服务及其依赖项封装到容器镜像中。不同的是,容器镜像提供更轻量级的封装。(2)隔离的服务实例。与虚拟机部署一样,容器运行时可以保证每个服务实例都以完全隔离的方式运行。(3)可配置的资源。由于容器创建时可以指定CPU和内存,所以容器部署可以实现更精确的资源配置。但是容器部署会带来不必要的容器镜像管理工作。操作统统补丁更新、容器基础设施管理、虚拟机基础设施管理、服务运行时补丁更新等。
容器编排
为将容器镜像管理工作从开发人员层面分离,需要在平台层面提供容器编排能力。Kubernets已经成为容器编排框架的事实标准。容器编排框架是容器之上的一个软件层,它将一组计算机硬件资源转变为用于运行服务的单一资源池。它可保持每个服务所需的示例数量,并确保它们一直在线。Kubernets是一个复杂的主题,后面会专门写文章介绍。基于容器编排框架部署服务已经成为一种流行的方法。笔者参与的云服务开发,就是使用这种方法。
要在Kubernets上部署服务,需要定义一个部署(Deployment)对象。创建部署对象的最简单方式,就是按照要求编写yaml文件。这里不展开说明,后面会写文章专门介绍,有兴趣的同学可以自行查找学习。
部署和发布分离
发布新版本服务的传统方法是:首先在预发环境中对其进行测试,然后一旦这个新版本在预发环境中通过测试,就可以将新版本滚动升级到生产环境。Kubernets部署使滚动升级变得简单。这种发布方法使用的一个前提是预发环境与生产环境一致,该新版本通过了预发环境中的测试,也可在生产环境中正常工作。
但是,事实并非如此。预发环境不太可能和生产环境完全一致,如果没有其他原因,生产环境可能会有更大流量。保持两个环境同步是很耗时的。另外,即使两个环境可以保证一致,也不能确保测试能发现所有的错误。
发布新版本的一种更可靠的方法是将部署流程和发布流程分离:
(1) 部署流程:让服务开始在生产环境中运行。
(2) 发布流程:使最终用户可以使用(访问)服务。
这样,一次版本发布将经历以下流程:
(1) 将新版本部署到生产环境,而不向其路由任何最终用户请求。
(2) 在生产上进行测试。
(3) 将新版本发布给少数最终用户。
(4) 逐步将其发布给更多用户,直到它处理所有生产流量为止。
(5) 任何时候出现问题,立即恢复旧版本。
传统上,将部署流程和发布流程分离是一个挑战,因为需要大量的工作才能实现。一种有效的方式是使用服务网格实现这种能力。
服务网格
服务网格是一种网络基础设施,它负责处理服务与其他服务、服务与外部服务之间的所有网络通信。除了承担微服务的一些职责外,服务网格还提供基于规则的负载均衡和流量路由,可以安全地同时运行多个版本的服务。如将测试用户路由到一个版本的服务,将最终用户路由到另一个版本的服务。
Istio已成为服务网格的主流实现,笔者参与的云服务开发就是使用Istio的服务网格。基于Istio实现部署和发布分离的示例,这里不再展开介绍,后面会写文章单独介绍。
Serverless部署
无论是包部署、还是虚拟机部署,亦或容器部署,其共同特征是开发人员需要负责系统管理。不论运行何种类型的计算资源,开发人员必须承担为操作系统或软件打补丁的工作。在Amazon,这被称为”无差别的活动”(Undifferentiated heavy lifing)。针对这个问题的解决方案是Serverless。
Serverless是一种无服务架构,其目的是让开发人员从服务器中解放出来。使用Serverless后,开发人员只需将应用打包并上传到Serverless平台,而无需关心服务器、虚拟机或容器的任何方面。
尽管Serverless非常强大,但是现有的实现,对平台依赖度过高,并不能完全让开发人员开发出与平台无关的应用。但是,这种架构对于处理一些短生命周期的活动(如调查问卷),仍有极大的价值。
笔者参与的云服务开发并没有使用Serverless,一个主要的原因是这种架构对多云开发不友好。市面上比较流程的Serverless服务,有Amazon Lambda, 阿里云 Serverless 应用平台等。
总结
部署流程由手动部署过渡到流水线部署,计算资源也随着物理机器的抽象而发生了根本性的变化。各大云厂商所运行的虚拟机已经取代了长声明周期的物理机和虚拟机。如今的虚拟机是不可变的(immutable),它们被视为一次性的资源而不再是宠物,使用过后,它们被丢弃和重建,而不是重新配置。容器是虚拟机之上的一个更轻量级的抽象层,已经逐渐成为应用部署的标准。为解决容器带来的容器镜像管理工作,需要在平台层面提供容器编排能力。Kubernets已经成为容器编排框架的事实标准。为提供发布版本的稳定性,一种可靠的方法是将部署流程和发布流程分离。一种有效的部署和发布分离的实现方式是使用服务网格。服务网格安全地同时运行多个版本的服务。如将测试用户路由到一个版本的服务,将最终用户路由到另一个版本的服务。Istio已成为服务网格的主流实现。对许多场景,还可以使用更轻量的Serverless部署平台,如AWS Lambda。但是Serverless与平台耦合度较高,多云适配代价较大。部署流程和架构的演进和微服务架构的普及关系密不可分。微服务架构推动了服务的规模化,而服务的规模化,需要高度自动化的部署流程和基础设施支撑。最后,简单梳理下微服务应用部署的发展流程,示意图如下:
参考
微服务设计 Sam Newman 著, 崔力强 等 译
微服务架构设计模式 Chris Richardson 著, 陈斌 等 译