本文是构建微服务系列文章的第六篇。第一篇介绍了微服务架构模式并讨论了微服务的缺点和优点。之后的文章讨论了微服务架构的不同方面:使用API网关进程间通信服务发现机制事件驱动的数据管理。本文继续了解微服务的部署策略。

一、动机

部署单体应用意味着要运行一个或者多个极其庞大、复杂的相同应用。一般要提供N个服务器(物理或者虚拟的)并且在每个服务器上运行M个实例。单体应用的部署并不总是很直接的,但是相比微服务应用的部署会比较简单。

微服务应用包括数十个或者上百个服务。这些服务由多种语言和框架开发。每个都是拥有特定部署、资源、扩展性和监控需求的微型应用。例如,基于对某个服务的需求,需要运行该服务的多个实例。每个服务必须供给适当的CPU、内存和IO资源。更有挑战性的是,即使存在复杂性,部署服务也必须快速、可靠和高效的。

微服务部署策略有很多种。首先看一下单主机多服务实例模式。

二、单主机多服务实例模式

部署微服务的一种方式是使用单主机多服务实例模式当使用该模式时,需要提供一个或者多个物理或者虚拟主机,并且在一个主机上运行多个服务实例。在许多方面,这仍然是应用部署的传统方式。每个服务实例运行在一个或者多个主机的端口上。这些主机通常被当做宠物。图6-1显示了该模式的结构:

这里写图片描述

图6-1 主机可以支持多服务实例

这种模式也存在很多的变形。

  • 一种变形是每个服务实例都是一个进程或者进程组。例如,可以在Apache Tomcat服务器上部署一个Java服务实例作为Web应用。Node.js服务实例可以包含父进程和一个或者多个子进程。
  • 另外一个变形是在相同的进程或者进程组中运行多个服务实例。例如,可以在相同的Apache Tomcat服务器上部署多个Java Web程序或者在相同的OSGI容器中运行多个OSGI bundles

单主机多个服务模式优点和缺点并存。

优点:

  • 一个主要的优点是资源使用相对有效。多个服务实例共享服务器和操作系统。一个进程或者进程组运行多个服务实例会更有效,例如,多个Web应用共享Apache Tomcat和JVM
  • 另外一个优点是部署一个服务实例相对快速。只是简单地复制该服务到一个主机并启动它即可。如果服务由Java开发,拷贝就是一个JAR或者WAR文件。对于其他的语言来说,比如Node.js或者Ruby,拷贝的是源代码。在任何情况下,通过网络复制的字节数相对来说都更小;
  • 并且,因为缺乏开销,开启服务通常来说更快。如果服务在自己的进程中,简单开启即可。不然,如果服务是运行在相同容器进程或者进程组的多个服务实例中的一个,则需要动态地部署该到容器中或者重启该容器;

即使优点明显,仍然有很多的缺点:

  • 一个主要的缺点是除非每个实例是单独的进程,服务实例之间几乎没有隔离。虽然可以精确地监控每个服务实例资源的利用,但是不能限制每个服务实例的资源使用。一些不正常的服务实例很有可能会消耗该主机的全部内存或者CPU ;如果运行在相同进程的多个服务实例之间完全没有隔离。所有的实例可能,比如,共享相同的JVM堆栈,异常的服务实例很容易终止运行在相同进程的其他服务实例。另外,也无法监控每个服务实例使用的资源;
  • 另外一个重要的问题是部署服务的执行团队必须知道部署服务的具体细节。服务可能由多种语言和框架开发,所以有很多的细节需要开发团队和执行团队共享。这种复杂性增加了部署过程中的出错的风险。

如你所见,即使很熟悉,单主机多服务模式仍存在一些严重缺陷。让我们来看一下部署微服务的其他方式,以避免这些问题。

三、单主机单服务实例模式

另一种微服务部署的方法是单主机单服务实例模式。当使用该模式时,可以在服务自己的主机上独立运行该服务。这种模式有两种不同的特定形式。

  • 单虚拟机上单服务实例
  • 单容器单服务实例

3.1 单虚拟机单服务实例模式

当使用单虚拟机单服务实例模式时,需要将每个服务打包成虚拟机镜像,比如Amazon EC2 AMI。每个服务实例都是一个虚拟机,该虚拟机通过虚拟机镜像启动。图6-2 显示了该模式的结构:

这里写图片描述

图6-2 每个服务可以运行在自己的虚拟机中

这是Netflix部署其视频流服务的主要方式。Netflix使用Aminator 将它的每个服务打包成EC2 AMI。每个运行的服务实例都是一个EC2实例。

你可以使用多种工具来构建自己的VM。可以配置持续集成(continuous integration ,CI)服务器,比如Jenkins来调用Aminator将服务打包成EC2 AMI。Packer是自动化创建VM镜像的另外一个选择。不像Aminator,它支持多种虚拟技术,包括EC2,DigitalOcean,VirtualBox和VMware。

Boxfuse 公司有一种引人瞩目的构建VM镜像的方法,它克服了我下面将要描述的VM的缺点。Boxfuse将Java程序打包成微小的VM镜像。这些镜像的构建、启动十分迅速,而且因为它们只暴露出有限的攻击面所以更加安全。

CloudNative公司拥有Bakery,这是一个创建EC2 AMI的SaaS提供商。在微服务测试通过后,可以配置CI服务器来调用Bakery。Bakery接着将应用打包成AMI。使用SaaS供应商,比如Bakery,意味着不需要浪费宝贵的时间在AMI基础设施的创建设置上。

单虚拟机单服务实例模式有很多的优点。

  • 一个主要的优点是每个服务实例完全独立运行。它有固定数量的CPU和内存,不能从别的服务上盗取资源;
  • 另外一个优点是可以利用成熟的云服务基础设施。云环境,比如AWS,提供了有用的功能,比如负载均衡和自动伸缩;
  • 另外一个优点是它封装了服务的实现技术。一旦服务被打包成VM,它就会变成黑盒。VM的管理API成为部署该服务的API。部署工作会更加简单、可靠;

当然,它也存在一些缺点:

  • 一个缺点就是资源利用率较低。每个服务实例占据整个虚拟机的全部开销,包括操作系统。然而,在典型的公共IaaS中,VM的资源规模都是固定的,这可能导致虚拟机未能充分利用;
  • 公共的IaaS 按照VM 收费,无论它们忙碌还是闲置。例如AWS等IaaS提供商提供了自动伸缩功能,但是对需求的变化做出快速反应是很困难的。所以通常VM实例可能供给过多,这增加了部署成本
  • 另外的一个缺点是部署新版本的服务会很慢。由于体积的因素,VM镜像构建的很慢,同样的原因也造成了VM的实例化很慢。操作系统的启动也会花费时间。但是,并不总是这样,轻量级的VM比如Boxfuse构建的,也是存在的;
  • 另外一个缺点是你(或组织的其他人)通常要负责很多繁重的负担。除非使用类似Boxfuse的工具,来处理构建和管理VM的开销,之后才是你的责任。这种必要、但耗时的工作将你从核心业务中抽离。

让我们来了解一下微服务部署的替代方式——更加轻量级但仍具有很多VM的优点。

3.2 单容器单服务实例模式

当使用单容器单服务实例模式时,每个服务实例运行在自己的容器中。容器是操作系统级别的虚拟化机制。容器包括运行在沙盒中的一个或者多个进程。从进程的角度看,它们有自己的端口命名空间和根文件系统。你可以限制容器的内存和CPU资源。一些容器的实现也有IO速率的限制。容器技术的例子包括DockerSolaris Zones

图6-3显示了该模式的结构:

这里写图片描述

图6-3 服务可以在自己的容器中运行

为了使用这种模式,你需要将应用打包成容器镜像。容器镜像是一个文件系统镜像,它包含了应用和运行该服务所需的库。一些容器镜像含有完整的Linux根文件系统,其他则更加轻量级。为了部署Java服务,可以构建一个容器镜像,包括Java运行时环境,可能是Apache Tomcat和编译好的Java应用。

一旦将服务打包成容器镜像,之后需要启动一个或多个容器。通常需要在每个物理或虚拟主机上运行多个容器。可以使用集群管理系统,比如Kubernetes或者Marathon来管理容器。一个容器管理系统将主机视作一个资源池。它根据容器的资源要求和每个主机可用的资源来决定每个容器放在哪里。

单容器单服务实例模式的优点有:

  • 容器和虚拟机的优点类似。它们将服务实例之间隔离,还可以很容易地监控每个容器使用的资源。也像虚拟机一样,容器封装了服务的实现技术。容器管理的API也作为服务管理的API;但是不像VM,容器是轻量级的技术。容器镜像通常构建很快。例如,在我的笔记本上只花费了5s就将Spring Boot应用打包成了Docker容器。因为没有冗长的操作系统引导机制,容器的开启也很快。当容器启动时,只有服务运行。

缺点:

  • 虽然容器的基础设施迅速成熟,但是仍没有达到像VM的成熟程度,并且容器也不像VM那样安全,因为容器和其他的容器共享了主机操作系统的内核;
  • 另外一个缺点是,你必须负责容器镜像管理的繁重工作。除非使用主机容器解决方案,比如Google Container Engine或者Amazon EC2 Container Service(ECS) ,接着必须管理容器基础设施和它运行VM的基础设施;
  • 虚拟机经常要部署在需要提前付费的基础设施中。于是,如上所述,为了处理负载高峰,冗余配置会导致超额成本;

有趣的是,容器和虚拟机之间的区别很模糊。正如之前提及的,Boxfuse VM可以快速构建和启动。Clear Containers项目的目标是创建轻量级的VMunikernels 也逐渐引起极大的兴趣。Docker 公司早在2016年就已经获得了Unikernal Systems。

server-less部署架构是较新的概念,也在不断获得关注,它是避免了选择在容器还是VM中部署微服务的问题。详细信息看下文。

四、Serverless 部署

AWS Lambda是一个serverless部署技术的例证。它支持JavaNode.jsPython 服务。为了部署微服务,需要将应用打包成ZIP文件并上传到AWS Lambda。你可以提供元数据,这些元数据指定了被调用来处理请求(或者事件)的函数的名称。AWS Lambda为你的服务自动地运行足够的实例来处理请求。你只需要根据每个请求使用的时间和内存付费。当然,细节决定成败,之后你将会看到AWS Lambda也有缺点。但是值得注意的是,你或者其他人,作为开发者都不需要担心服务器、虚拟机或者极其吸引人的容器层面上的问题。

Lambda function是无状态的服务。它通过调用AWS服务来处理请求。例如,当镜像被上传到S3全家桶时,被调用的Lambda function会向DynamoDB镜像表中插入一条数据并且发布消息给Kinesis流来触发镜像处理。Lambda function也能调用第三方web services。

调用Lambda function有4种方式:

  • 直接使用web service请求;
  • 自动响应AWS 服务生成的事件,比如S3、DynamoDB、Kinesis或者Simple Email Service;
  • 通过AWS API网关自动地处理应用客户端发出的HTTP请求;
  • 周期地执行cron风格的调度任务;

正如你看到的,AWS Lambda是部署微服务的便利方式。基于请求计费意味着可以只为服务真实执行的工作付费。并且,因为不用负责IT基础设施,能够集中开发你的应用。

但是,这种方式仍然存在严重的限制。Lambda function并不打算用来部署长期运行的服务,比如从第三方消息代理获取消息的服务。请求必须在300s内完成。服务必须是无状态的,因为理论上AWS Lambda可以为每个请求运行单独的实例。它们必须由支持语言中的一种开发。服务也必须迅速开启,否则,它们可能超时或者终止。

五、总结

微服务的部署是很有挑战性的,其可能包括数十个甚至上百个由多种语言和框架开发的服务。每个都是拥有特定的部署、资源、扩展和监控需求的微型应用。部署微服务的方式有很多种,包括:

  • 单虚拟机单服务实例模式;
  • 单容器单服务实例模式;

其他有趣的选择包括AWS Lambda,一种serverless方法。在之后的文章中,也是本系列的最后一篇,将探讨如何将单体应用迁移到微服务架构上。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐