Chris Richardson 微服务系列 第六篇 选择一种微服务部署策略

 
   
   
   
   

这是使用微服务架构构建应用系列的第六篇文章,第一篇文章介绍的微服务架构模式以及使用该模式的优势和劣势,接下来的文章讨论了微服务架构的不同方面:使用APi网关、进程间通信、服务发现以及事件驱动的数据管理。本篇文章我们将看一下有关微服务部署的策略。

动机

部署一个单体应用意味着对一个一般比较庞大的应用运行多个相同的拷贝,通常需要提供N台服务器(物理机或者虚拟机),并在每一台机器上运行M个应用实例。部署一个单体应用一般并不是特别直接,但是相比部署微服务架构的应用来讲,它已经简单很多了。

一个 微服务应用包含数十个甚至上百个服务,服务是由多种不同的编程语言和框架写成,每一个服务都是一个有自己独特的部署、资源、扩展性以及 监控需求的mini应用。比如,你需要根据服务的需求对该服务运行一定数量的实例,而且,每一个服务实例必须提供适当的CPU、内存以及I/O资源,更加具有挑战性的是,尽管这很复杂,部署一个服务同时还要求 快速、可靠、高效。

存在多种微服务部署模式,让我们先看一下一台主机部署多个服务的模式:

一台主机部署多服务实例模式

部署微服务的方法之一是使用每台主机部署多服务实例 的模式,使用这种模式的时候,你提供多台物理或者虚拟的主机,每台主机上运行多个服务实例,从某些角度来讲,这是传统的应用部署方式。每个服务的运行占用一台多多台主机上的端口,主机机器通常 照顾宠物一样来管理这些服务。

下图展示了这种模式的结构图:

Paste_Image.png

这种模式有多种变种,变种之一是每个服务对应一个或一组进程,比如你可能把一个java服务实例以web应用的形式部署到Apache Tomcat 服务器中,一个Node.js 服务实例则可能包含一个父进程以及一到多个子进程。

这种模式的另外一个变种是在同一个进程或进程组中运行多个服务实例,比如你可以在同一个Apache Tomcat服务器中部署多个java web 应用,或者在同一个OSGI容器中运行多个OSGI bundles。

每个主机运行多个服务实例的模式有优势也有劣势。一个大的优势是资源利用率相当的高,多个服务实例共享服务器和对应的操作系统,如果一个进程或进程组运行多个服务实例的话,效率就更加高了,比如多个web应用共享同一台Apache Tomcat服务器和JVM。

该模式的另一个优势是部署服务实例相当的快速,你可以简单的把服务拷贝到主机并启动它。如果服务是java编写的,你拷贝JAR或者WAR包,如果是使用其他编程语言写的,比如 Node.js 或者 Ruby,你拷贝源代码就可以。在网络上拷贝这些字节的数量还是相对比较小的。

当然,由于没有其他的开销,启动一个服务一般是非常快的。如果该服务是其自身的进程,你只需要简单的启动进程,如果服务本身是运行于同一个容器进程或者进程组中的服务实例,你可以将其动态的部署到容器或者使用重启容器的方式启动服务。

尽管有一些魅力,每个主机运行多个服务实例的模式还是有一些重大缺陷的。一个主要缺陷是服务实例之间的隔离性很小或者没有隔离性,除非每个服务实例运行在单独的进程中!虽然你可以精确的监控每一个服务实例的资源使用情况,但你不可以限制每一个实例使用的资源,很有可能一个行为异常的服务实例会消耗掉主机上所有的内存和CPU资源。

同一进程运行多个服务实例的模式压根就没有隔离性,所有的服务实例可能共享相同的JVM heap,一个行为异常的服务实例可以很容易的破坏掉运行在同一进程中的其他服务实例,而且,你没有任何方法监控每一个服务实例的资源使用情况。

这种模式的另一个巨大劣势是对于运维团队来讲,部署一个服务实例必须知道针对该服务的具体细节,服务可能被不同的编程语言和框架写成,因此就有大量的细节需要开发人员分享给运维人员,这些复杂度增加了部署时出错的风险。

如你所见,尽管每个主机运行多个服务实例的模式很熟悉,但是存在一些严重的缺陷,现在让我们看一下可以避免这些问题的另一种模式:

每个主机一个服务实例

部署微服务的另一种模式是每个主机一个服务实例 模式。当你使用这种模式的时候,你可以隔离的在每一个主机中运行对应的服务实例,这种模式有两种不同的标准:每台虚拟机一个服务实例和每个容器一个服务实例。

每台虚拟机一个服务实例模式

当你使用每台虚拟器一个服务实例 模式的时候,你把每一个服务打包为一个虚拟机镜像,比如Amazon EC2 AMI。虚拟机中的每一个服务实例(比如, an EC2 instance)使用虚拟机镜像启动,下图展示了这种模式的架构:

Paste_Image.png

这也是Netflix用来部署其video streaming服务的最初的方案。 Netflix使用 Aminator把其每一个服务打包成EC2 AMI ,每一个运行的服务实例实际就是一个EC2实例。

有不同的工具可以让你构建自己的虚拟机镜像,你可以配置你的持续集成(CI)服务器(比如, Jenkins) 调用Aminator来把服务打包成EC2 AMI。Packer.io是另一个自动化虚拟机镜像的选择,和Aminator不同,它支持不同的虚拟化技术,包括EC2、DigitalOcean、VirtualBox以及VMware。

Boxfuse 公司使用一种更加优秀的方式来构建虚拟机镜像,它克服了我下面将会讲到的虚拟机镜像的一些缺陷,Boxfuse把你的Java应用打包成一个迷你的虚拟机镜像,这些镜像可以被快速的构建、迅速启动,由于他们暴露了有限的可能被攻击的接口,所以也更加的安全。

每一台虚拟机一个服务实例的模式有一些优势。一个主要优势是每一个服务实例的运行都是相互隔离的,服务实例有固定的CUP和内存分配,它不可能窃取其他服务的资源。

该模式的另一优势是可以充分利用成熟的云平台基础架构,AWS这样的云平台都提供了负载均衡以及自动扩展这样的功能。

该模式的另一巨大优势是它封装了你服务实现使用的技术细节,一旦服务被打包成了虚拟机镜像,它就变成了一个黑盒,镜像管理API就成了服务部署API,部署变得更加简单和可靠。

一个虚拟机一个服务实例的模式也有有一些劣势,一个主要的劣势是资源利用率低,每一个服务都完全占有包括操作系统在内的整个虚拟机,更重要的是,在典型的公共IaaS中,固定大小的虚拟机资源并没有被充分利用。

然而,一个公共的IaaS平台在计费的时候并不关心虚拟机忙碌或空闲,AWS这样的IaaS平台提供自动扩展,但是很难快速响应并按需收费,因此你需要经常的拨备虚拟机,增加了部署的花费。

这种方式的另一个劣势是部署一个新的服务通常非常缓慢。虚拟机镜像由于其大小的问题,构建过程非常缓慢,而且通常虚拟机的大小问题使得初始化也很缓慢 ,而且操作系统在启动的时候也要花费一定的时间,注意,这并不是指所有的镜像,因为还有Boxfuse这样的轻量级的虚拟机镜像存在。

这种模式的再一个劣势是你(或者你组中的其他人)需要负责大量非分化的heavy lifting。除非你使用诸如Boxfuse这样的工具处理构建和管理虚拟机镜像这些繁杂的事情,不然就只能是你来负责了,这些很必要但是又耗费时间的活动使你远离了你的业务代码。

让我们看下另一种具有虚拟机镜像优势同时更加轻量级的微服务部署方式吧:

每台容器一个服务实例的模式

当你使用每台容器一个服务实例 模式的时候,每个服务实例运行在自己的容器中。容器是一种操作系统层面的虚拟机制,一个容器由跑在沙箱中的一到多个进程组成。从进程的角度看,他们都有自己的端口命名空间和根文件系统,你可以限制容器的内存和CUP资源使用。一些容器的实现也可以实现I/O的速率限制,容器技术包括Docker 和 Solaris Zones。

下图展示了这种模式的架构:

Paste_Image.png

为使用这种模式,你需要把你的服务打包成容器镜像,一个容器镜像是包含运行服务所需应用和必要library的文件系统镜像,一些容器镜像可能包含完整的Linux根文件系统,比如为了部署一个java服务,你可以构建一个包含java运行时或者Apache Tomcat 服务器以及编译后的java应用的容器镜像。

一旦你把你的服务打包成了容器镜像,你就可以启动一到多个容器了,你通常在一台虚拟机或者物理机器上运行多个容器,你可以使用诸如Kubernetes 或 Marathon这样的集群管理器来管理你的容器。集群管理器把这些主机作为资源池,它根据每台主机上的可用资源以及每台容器的资源需求决定把容器放置在哪台主机。

这种模式有优势也有劣势。优势类似于虚拟机具有的优势,他们隔离了每一个服务实例,你可以很容易的监控每一台容器的资源消耗,与虚拟机类似,容器也封装了实现服务所使用的不同技术细节,容器管理API同样作为管理服务的API来使用。

当然,不同于虚拟机,容器是更加轻量级的技术。容器镜像的构建通常非常快速,比如,在我的个人笔记本上,把一个Spring Boot 应用打包为Docker镜像仅仅需要5秒钟。由于没有冗长的OS引导过程,容器启动过程也非常快速,容器启动,服务就会运行。

使用容器也有一些缺陷。尽管容器技术正在快速的走向成熟,但是相对虚拟机架构来说还是略显青涩,由于容器间分享同一主机的操作系统内核,容器并没有虚拟机那么安全。

容器的另一劣势是你必须负责管理容器镜像的这些未分化的heavy lifting,除非你使用诸如 Google Container Engine 或者Amazon EC2 Container Service (ECS)这些主机容器解决方案,你必须自己来管理容器的架构或者支撑容器的底层虚拟机架构。

此外,容器通常部署在按照每个VM定价的基础设施上,因此,正如前面提到的那样,为处理负载峰值的情况,你有可能为提供额外的虚拟机而增加额外的花费。

有意思的是,容器和虚拟机之间的区别变的模糊起来。前面提到Boxfuse虚拟机可以快速构建和启动, Clear Containers 工程致力于创建轻量级的虚拟机镜像。对于unikernels也有更多人感兴趣起来。 Docker公司最近收购了Unikernel Systems。

也有一种新的、逐步流行的server-less的部署理念,它回避了部署服务时选择容器还是虚拟机的问题,让我们接着看:

Serverless 部署

AWS Lambda 是serverless部署技术的例子之一,它支持Java、Node.js和Python 服务。为了部署一个微服务,你把服务打包为一个ZIP文件上传到AWS Lambda,你还要提供元数据,其中包括指定调用用以处理请求(也就是事件)的函数的名称 。 AWS Lambda自动为你的微服务运行足够的实例来处理请求。你可以简单根据每个请求花费的时间和消耗的内存进行计费。当然,你很快就会看到AWS Lambda在这些细节中的局限,但是请注意,不管你组织的开发者或者其他的人员都不需要担心服务器、虚拟机或者容器的各个方面,这一点是很有诱惑力的。

一个 Lambda function 是一个无状态的服务,它通常通过调用AWS服务来处理请求。比如,一个Lambda function在一张图片被上传到S3 bucket的时候被调用,它可能往DynamoDB图片表中插入一条记录并向Kinesis stream发送一条消息来触发图片的处理,一个Lambda function也可以调用第三方的web服务。

有四种调用Lambda function的方法:

  1. 使用web服务请求直接调用
  2. 自动以响应诸如S3、DynamoDB、Kinesis或者Simple Email Service等产生的事件的方式调用
  3. 通过AWS API网关处理来自客户端的请求的方式调用
  4. 基于cron-like schedule阶段性的去调用

如你所见,AWS Lambda 是部署微服务的一个方便方式,基于请求的计价意味着你只需要为你服务的实际工作买单,当然,你不必负责IT基础架构的问题,从而专心的开发你的应用。

当然,这也有显著的局限:它不是为部署需要长时间运行的服务而设计的,比如消费来自第三方消息中介的消息的服务。请求必须在300秒内被完成,由于理论上处理请求的AWS Lambda可能运行在不同的实例中,因此服务也必须是无状态的。它们必须使用被支持的编程语言所编写,服务必须快速被启动,否则将会超时或者终止。

总结

部署一个微服务应用是很有挑战性的,应用拥有十几个甚至上百个由不同编程语言和框架实现的服务组成,每一个服务都是一个拥有独特部署、资源、扩展和监控需求的迷你应用 。有每台虚拟机一个服务实例和每台容器一个服务实例两种微服务部署模式,另一种有趣的微服务部署选择是使用AWS Lambda,一个serverless的方式。在该系列下一篇也是最后一篇文章中,我们将讨论如何把单体应用迁移为微服务结构。

作者:nonumber1989 链接:http://www.jianshu.com/p/31c2a5a8b764 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(架构设计,技术架构,微服务)