本文出自Nginx官网,是微服务介绍系列文章的第六篇。原文地址:https://www.nginx.com/blog/deploying-microservices/
1.介绍
在第一篇文章中我们比较了微服务架构应用和单体应用的差异,讨论了微服务架构的优点与缺点;第二篇文章和第三篇文章讨论了进程间通信相关的问题;第四篇文章讨论服务发现问题;第五篇文章介绍了如何使用事件驱动架构处理分布式数据管理;在本篇文章中我们讨论微服务的部署策略。
2.动机
单体应用一般会部署多个副本,通常在多台物理机或虚拟机上部署多个应用实例来实现,这比微服务应用的部署简单多了。
微服务架构的应用由几十甚至几百个服务组成,服务使用的开发语言和技术框架可能各不相同,每个服务都是独立应用,有自己独有的部署方式、资源需求、伸缩特性和监控需求。有可能需要根据某个服务的具体需求,运行特定数量的实例;每个服务有不同的CPU、内存和IO需求;除了这些复杂性之外,更具挑战性的需求是服务部署必须快速、可靠、低成本。下面介绍几种不同的部署模式。
3.单主机多服务实例模式
该种模式是应用部署的传统模式,在每个物理机或者虚拟机上使用知名端口部署多个服务实例,下图展示了该种模式:
这种模式有很多变种,一种变种是每个服务实例是一个进程或者进程组:你可能使用Tomcat以Web应用的形式部署服务;Node.js的服务实例可能包含一个父进程、一个或者多个子进程。
另外一种变种是在一个进程内或者进程组内运行多个服务实例,比如,在一个Tomcat服务器上部署多个Web应用;在一个OSGI容器中运行多个OSGI包。
这种模式的主要好处是资源利用比较高效,多个服务实例共享服务器和操作系统;在一个进程或者进程组中运行多个服务实例资源利用会更高效,比如多个Web应用共享一个Tomcat和JVM。
另外一个优点是部署服务速度快,只用把服务拷贝到服务器上启动即可:服务可能是Java编译成的JAR或者WAR文件,也可能是Node.js或者Ruby的源代码;不管哪种,需要通过网络传递的数据量不大。
这种模式下,启动速度通常比较快。如果服务本身是进程,简单启动即可;如果服务是容器进程或者容器进程组的实例,将实例动态部署到容器进程或者重启容器进程即可。
这种模式的缺点是除了服务实例自身作为进程之外,其他情况下服务实例不是相互独立的。也许你能精确监测每个服务的资源占用率,却不能限制每个服务的资源使用;某个异常服务可能会消耗光主机的CPU和内存资源。
如果多个服务运行在同一个进程中,那服务就没有一点儿独立性了。所有的服务实例共享JVM堆栈,某个异常服务会影响同进程中的其他服务;更严重的是,你根本没有办法监测每个服务实例的资源占用。
另外一个问题是部署人员需要了解更多细节,不同服务可能使用不同的语言和框架,这意味着开发团队需要跟部署团队共享很多细节,这增加了部署工作的复杂度,给部署工作带来了风险。
4.单主机单服务实例模式
在这种模式下,服务实例独立运行在它自己的主机上,有两种形式:单虚拟机单服务实例和单容器单服务实例。
单虚拟机单服务实例
该种模式下,将服务打包成虚拟机映像文件,如Amazon EC2 AMI;每个服务实例都是一个虚机,如下图所示:
Netflix使用这种方式部署视频流服务,它使用Aminator将每个服务打包成虚机映像文件(EC2 AMI),每个服务实例都是个EC2实例。
有许多工具可用来进行虚拟机打包,可以使用持续集成服务器(像Jenkins),由它调用Aminator自动将服务打包成EC2 AMI;Packer.io是另外一个自动打包工具,它支持更多的虚拟化技术,像EC2、DigitalOcean、VirtualBox、VMware等。
Boxfuse提供另外一种选择,它将java应用打包成轻量级的虚拟机映像文件;这种映像文件支持快速打包、快速启动,有更高的安全性。
CloudNative公司的Bakery产品提供创建亚马逊虚拟机(EC2 AMIs)的SaaS服务,你可以配置持续集成服务器调用Bakery打包服务,生成虚机映像文件AMI。使用Bakery的好处是,你不用浪费时间设置创建AMI的环境。
单虚拟机单服务实例模式的最大好处是服务实例之间完全独立,每个实例有固定数量的CPU和内存,不会占用其他服务的资源;另外一个好处是可以充分利用成熟的云架构,像AWS就能提供负载均衡、自动伸缩等有用功能;还有一个好处是服务打包成VM之后,成了黑盒,外界无法得知服务采用的技术,部署和管理完全使用虚拟机管理的API,简化了部署工作。
单虚拟机单服务实例的缺点是资源利用率低,每一个服务独占VM,需要单独的操作系统,一般的云平台提供几种固定大小的内存、CPU等,这会造成资源浪费;另外,虽然云平台支持自动伸缩特性,但是很难快速适应变化,这就需要部署人员不断手工调整VM配置,增加了部署工作量;另外一个缺点是部署速度慢,因为VM映像文件大,打包、实例化、启动速度都慢,当然这不是绝对的,也有些轻量级VM解决方案(如Boxfuse);还有一个缺点是创建和管理VM需要进行大量的重复工作,这些工作会分散精力,让你不能专注于核心业务。
单容器单服务实例
这种模式下,服务运行在自己的容器中。容器是一种操作系统层面的虚拟化技术,可以包含一个或几个运行在沙箱中的进程;容器中的进程拥有独立的端口号和文件系统,可以限制容器可使用的CPU和内存资源,有些还支持限定I/O速率;常用的容器技术有Docker和Solaris Zones。下图展示了这种模式:
使用这种模式,应用被打包成容器映像文件。映像文件包括服务文件和运行服务需要的库文件。有些容器映像文件包括完整的linux文件系统,有些更轻量级一些。假设需要部署一个Java服务,映像文件需要包括Java运行时环境(可能是Tomcat)和编译后的Java应用。
打包成映像文件之后,就可以将映像文件部署到一个或几个主机(物理或虚拟)上,可以使用类似Kubernetes和Marathon的集群管理工具来管理容器。集群管理工具将主机(物理或虚拟)看做资源池,它根据容器的资源需求和每个主机的可用资源来决定将容器部署到哪个主机上。
这种模式类似单服务单主机,优点是服务实例之间相互独立,很容易监控每个服务实例的资源占用;封装了服务实现,外部不容易获得服务的技术细节;可以使用容器管理API管理服务,部署简单。这种模式跟单服务单主机模式的不同点是,容器映像文件小,打包、启动速度都特别快。
单服务单容器模式的缺点是容器的基础设施没有VM的基础设施成熟;另外由于同一台主机上的多个容器需要共享同一套操作系统内核,容器没有VM安全。另外一个缺点是需要做大量的重复性工作来管理容器映像文件;此外,除非使用托管容器解决方案(如Google Container Engine或Amazon EC2 Container Service(ECS)),否则必须手动管理容器基础设施以及VM的基础设施。
无服务器部署
AWS Lambda是一种无服务器部署技术,支持Java、Node.js和Python编写的服务。服务部署时需要打成ZIP包,上传到AWS Lambda;还需要提供元数据,定义响应请求的方法名称。 AWS Lambda自动运行适当数量的服务实例处理服务请求,你只需要按照请求花费的时间和内存付费。这种技术也有局限性,但是对于开发人员和部署人员而言,不用关心物理服务器、虚机、容器,还是很有吸引力。
Lambda方法是无状态服务,一般通过调用AWS服务来处理请求。比如,当用户向S3 bucket上传一个图像文件时,调用Lambda方法向DynamoDB的图像表中插入一条数据,并向Kinesis stream发布图像处理事件。Lambda方法也能调用第三方的Web服务。
调用Lambda方法有四种方法:1.使用服务请求直接调用;2.消费AWS服务发布的事件时,自动调用;3.AWS API网关处理客户端请求时,自动调用;4.类似Cron计划,定期调用。
AWS Lambda是部署微服务的简便方法,基于请求付费意味着你只用在服务真正运行时花钱;另外,由于不用关心IT基础设施,你可以集中精力处理业务逻辑。
AWS Lambda有明显的局限性:它不适合部署运行时间长的服务,比如消费第三方消息的服务,一次服务必须在300秒内完成;服务必须是无状态的;服务必须支持快速启动,否则可能因为超时而被终止。
5.总结
部署微服务架构的应用是件困难的工作,因为应用可能包含数十个甚至数百个服务,每个服务可能使用不同的语言和架构,每个服务都是一个小型应用(有自己独特的部署需求、资源需求、伸缩性和监控需求)。有几种服务部署模式:单主机单服务、单容器单服务、无服务部署(AWS Lambda)。在第七篇,也就是本系列的最后一篇文章中,我们将讨论如何将单体应用迁移到微服务架构。