Chris Richardson 微服务系列翻译全7篇链接:
- 微服务介绍
- 构建微服务之使用API网关
- 构建微服务之微服务架构的进程通讯
- 微服务架构中的服务发现
- 微服务之事件驱动的数据管理
- 微服务部署(本文)
- 重构单体应用为微服务
原文链接:Choosing a Microservices Deployment Strategy
动机
部署一个单体应用意味着运行着庞大应用的多个副本,通常需要 N 台服务器(物理机或虚拟机),在每台服务器上运行 M 个应用实例。部署单体应用一般并不特别直接,但还是比部署微服务应用简单。
一个微服务应用包括几十甚至数百个服务,使用不同的语言和框架写成,每个服务都是一个拥有特定的部署、资源、扩展性及监控需求的小应用。例如:根据服务需求运行若干个服务实例,而且每个服务实例必须配套提供适当的 CPU、内存 和 I/O 资源。更具挑战性的是,部署服务还必须快速、可靠、高效。
单主机部署多服务实例
该模式下,需要多台物理机或虚拟机,在每个主机上部署多个服务实例。这是比较传统的部署方法。每个服务实例运行在一至多台主机的端口上,主机通常像照看宠物一样来管理这些服务。如下图所示:
这一模式有几个变型。其中之一就是每个服务对应一个或一组进程。例如:在 Apache Tomcat 服务器上部署 Java 服务实例作为 web 应用,一个 Node.js 服务实例可能包含一个父进程或一至多个子进程。
另一个变型是在一个进程或进程组中运行多个服务实例。例如:在同一台 Apache Tomcat 服务器中部署多个 Java web 应用,或者在一个 OSGI 容器中运行多个 OSGI 组件。
单主机多服务部署的优点:
1)资源利用率高,多个服务实例共享服务器及操作系统。如果一个进程或进程组运行多个服务实例的话,效率就更高了,比如多个web应用共享同一台 Apache Tomcat 服务器和 JVM。
2)部署服务实例快,只需将服务拷贝到主机并启动。如果服务是 Java 编写的,复制 JAR包 或者 WAR 包;如果是 Node.js 或者 Ruby 等其它语言,拷贝源代码即可。通过网络复制这些字节数还是比较小的。
3)由于没有太多开销,启动服务通常很快。如果服务实例运行在同一容器的进程或进程组,可以动态部署到容器或使用重启容器的方式启动服务。
不足在于:
1)服务实例之间没有隔离。虽然可以准确监控每个服务实例的资源使用情况,但是并不能限制每个实例使用的资源,很有可能一个异常的服务实例会消耗掉主机的所有内存和 CPU资源。
2)同一进程运行多个服务实例根本没有隔离性,所有服务实例共享一个 JVM 堆。一个异常的服务实例能够轻易的破坏运行在同一进程中的其它服务实例。此外,也无法监控每个服务资源使用的情况。
3)对运维团队来讲,需要了解部署服务的具体细节。服务可能用不同的语言和框架写成,因而开发团队必须分享给运维团队大量的细节。这种复杂性增加了部署中出错的风险。
每个主机一个服务实例
这一模式有两种不同实现:每台虚拟机部署一个服务实例和每台容器部署一个服务实例。
每台虚拟机一个服务实例
该模式下,把每个服务打包为一个虚拟机镜像,例如 Amazon EC2 AMI。每个服务实例(例如 EC2 实例)使用虚拟机镜像启动。下图展示了此模式的结构:
这也是 Netflix 部署视频流媒体服务的最初方案。Netflix 使用 Aminator 把每个服务实例打包成 EC2 AMI,每个运行的服务实例就是一个 EC2 实例。
有多种工具可用来构建虚拟机镜像。可以配置持续集成(CI)服务器(例如 Jenkins)来调用 Aminator,把服务打包为 EC2 AMI。Packer.io 是另一个自动化创建虚拟机镜像的工具,不同于 Aminator,它支持包括 EC2、DigitalOcean、VirtualBox 和 VMware 在内的多种不同虚拟化技术。
Boxfuse 公司使用更加优秀的方式来构建虚拟机镜像,克服了下面会讲到的虚拟机镜像的不足。Boxfuse 把 Java 应用打包为一个迷你的虚拟机镜像。这些镜像能够快速构建、启动,由于只暴露了有限的可能被攻击的端口,所以也更安全。
CloudNative 使用 Bakery 这款 SaaS 工具来创建 EC2 AMI。用户的微服务通过测试后,能够配置 CI 服务器调用 Bakery,把服务打包为 AMI。使用 Bakery 这样的 SaaS 工具意味着你不需要浪费宝贵的时间来设置创建 AMI 的基础设施。
每台虚拟机一个服务实例的优点:
- 每个服务实例运行互相隔离,有固定的 CPU 和内存,不会占用别的服务的资源。
- 能够充分利用成熟的云服务平台。AWS 这样的云平台提供了负载均衡和自动扩展这样实用的功能。
- 封装了服务实现的技术细节。一旦服务被打包成虚拟镜像,就变成了黑盒,虚拟机镜像的管理 API 就成了部署该服务的 API。部署变得更简单可靠。
不足:
- 资源利用率低。每个服务实例完全占有包括操作系统在内的整个虚拟机。此外,在公有 IaaS 中,固定大小的虚拟机资源没有被充分利用。
- 公有 IaaS 通常依据虚拟机数量收费,不考虑其忙碌还是空闲。AWS 这类的 IaaS 提供了自动扩展,但是很难针快速响应;因而很容易过度调配虚拟机,增加部署花费。
- 部署新的服务通常很缓慢。虚拟机镜像由于其大小的问题,构建过程会比较慢,而且操作系统启动也要花费一定时间。然而,因为还有 Boxfuse 这样轻量级的虚拟机存在,这一问题也并非普遍。
- 用户或组织中的其他人要负责大量无差别的沉重的工作。除非使用 Boxfuse 这样的工具来解决构建和管理虚拟机镜像这些复杂的事情,否则这种必要且耗时的工作会占用你处理核心业务的时间。
每台容器一个服务实例
使用每台容器部署一个服务实例时,每个服务实例运行在自有容器中。容器是操作系统层面的虚拟化机制,一个容器由运行在沙盒中的一个或多个进程组成。从进程的角度看,它们有着自己的端口命名空间和根文件系统。用户能够限制容器的内存和 CPU 资源,有些容器还能限制 I/O 速率。容器技术的代表包括 Docker 和 Solaris Zone。下图展示了这种模式的架构:
使用这种模式时,用户将服务打包为容器镜像。一个容器镜像就是运行服务所需的应用和库组成的文件系统镜像。一些容器镜像还包括完整的 Linux 根文件系统。以部署 Java 服务为例,构建的容器镜像包括 Java 运行时或者Apache Tomcat 服务器以及编译好的 Java 应用。
一旦将服务打包为容器镜像,就可以启动一到多个容器了。通常一台物理机或虚拟主机上会运行多个容器,可以使用 Kubernetes 或 Marathon 这样的集群管理工具来管理容器。集群管理工具把主机看做资源池,根据每个容器需要的资源和每个主机上可用的资源来调度容器。
每台容器一个服务实例的优点类似虚拟机具有的优势:
- 服务实例之间完全隔离,也能容易的监控每一台容器的资源消耗。
- 与虚拟机类似,容器能够封装实现服务的技术细节。容器管理 API 也可用作管理服务的 API。
- 不同于虚拟机,容器技术更为轻量,容器镜像构建速度也更快。比如在笔记本电脑上,只用短短五秒就能把 Spring Boot 应用打包为 Docker 镜像。由于没有冗长的操作系统启动过程,容器启动也非常迅速。容器启动,服务就会运行。
不足:
- 虽然容器技术正迅速走向成熟,然而相对虚拟机架构来说还略显青涩。由于容器之间共享同一主机的操作系统内核,因而也没有虚拟机那么安全。
- 管理容器镜像也是一项繁重的工作。除非使用 Google Container Engine 或 Amazon EC2 这些容器解决方案,否则需要同时管理容器基础设施和虚拟机基础设施。
- 容器通常部署在按每台虚拟机定价的基础设施上,为了处理负载高峰,可能会过度配置虚拟机,从而增加额外的成本。
有趣的是,容器和虚拟机之间的区别变的模糊起来。如前文所述,Boxfuse 能够快速构建和启动虚拟机,Clear Container 项目则致力于创建轻量级的虚拟机镜像,unikernel 技术也引起了大家的注意。Docker 近期(注:2016 年 1 月 21 日)收购了 Unikernel Systems。
Serverless部署
AWS Lambda 就是 serverless 部署技术的范例。它支持 Java、Node.js 和 Python 服务。为了部署一个微服务,你需要把服务打包为 ZIP 文件并上传到 AWS Lambda,还要提供元数据,指定处理请求的函数名称。AWS Lambda 自动为微服务运行足够的实例来处理请求。可以简单根据每个请求花费的时间和消耗的内存来计费。开发人员无需担心服务器、虚拟机或容器的各个方面。
Lambda 函数是一个无状态的服务,通过调用 AWS 服务处理请求。例如,一个 Lambda 函数在一张图片被上传到 S3 时候调用,他能在 DynamoDB 表中插入一条记录,并向 Kinesis stream 发送一条消息来触发图片的处理。一个 Lambda 函数也可以调用第三方 web 服务。
有以下四种方法来调用 Lambda 函数:
- 直接调用,直接使用 web 服务请求
- 自动调用,自动响应由 S3、DynamoDB、Knesis、或 Simple Email Service 等 AWS 服务生成的事件
- 自动调用,自动通过 AWS API 网关处理来自应用客户端的 HTTP 请求
- 定期调用,通过类似 Cron 的定时任务实现
可以看出,AWS Lambda 是部署微服务的一个便捷的方式。基于请求的定价方式意味着用户只需要为服务实际运行的业务付费。此外,用户无需考虑 IT 基础设施的问题,从而能够专注于应用的开发。
然而,AWS Lambda 也有一些局限性。它并不适合被用来部署长期运行的服务,比如消费来自第三方消息的服务。请求需要在 300 秒内完成,由于 AWS Lambda 理论上能够针对每个请求运行单独的实例,因此服务必须保持无状态。此外,它还必须用一种支持的编程语言来编写。服务也需要快速启动,否则将会超时或停止。
总结
部署一个微服务应用充满挑战。应用由几十个甚至上百个用不同的语言和框架实现的服务所组成,每个服务都是一个拥有独立部署、资源、扩展和监控需求的微应用。微服务部署的模式有多种,包括单虚拟机单服务实例和单容器单服务实例。另一个有趣的微服务部署方法则是 AWS Lambda,一个 serverless 的方式。