这是本书中关于用微服务构建应用程序的第 6 章。第一章介绍了 微服务的架构模式,并讨论了使用微服务的优点和缺点。后面的章节讨论了微服务架构的不同方面:使用一个API网关,内部进程通信机制,服务发现,和时间驱动数据管理。在这章中,我们看一下部署微服务的策略。
动机
部署单一应用程序意味着运行单个(通常是大型)应用程序的一个或多个相同副本。通常提供N个服务器(物理或虚拟),并在每个服务器上运行应用程序的M个实例。单体应用程序的部署并不总是完全简单的,但它比部署微服务应用程序要简单得多。
一个微服务应用程序包含十个甚至百个微服务。服务是用各种语言和框架编写的。每一个都是一个很小的程序,每个应用程序都是一个迷你应用程序,具有自己特定的部署、资源、可伸缩和监视需求。例如,您需要根据服务的需求运行每个服务的一定数量的实例。同样,必须为每个服务实例提供适当的CPU、内存和I/O资源。更具有挑战性的是,尽管存在这种复杂性,但部署服务必须快速、可靠且具有成本效益。
这儿有一些不同的微服务部署模式。让我们先看一下每台服务器上有多个服务实例的模式。
每台服务器上有多个服务实例的模式(Multiple Service Instances Per Host Pattern)
一种部署你的微服务的方法是使每台服务器上多个服务的模式。当使用这个模式,您提供一个或多个物理或虚拟主机,并在每个主机上运行多个服务实例。在许多方面,这是应用程序部署的传统方法。每个服务实例运行在一个或多个主机上的一个知名端口上.
图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堆。行为不当的服务实例很容易中断在同一进程中运行的其他服务。此外,您无法监视每个服务实例所使用的资源。
这种方法的另一个重要问题是,部署服务的操作团队必须知道如何执行服务的具体细节。服务可以用多种语言和框架编写,因此开发团队必须与操作共享许多细节。这种复杂性增加了部署过程中出现错误的风险。
正如您所看到的,尽管熟悉,每个主机的多个服务实例模式仍有一些显著的缺陷。现在让我们看看部署微服务的其他方法,以避免这些问题。
每个主机服务实例的模式(Service Instance per Host Pattern)
另外一种来部署你未付的方法是,每个主机服务实例的模式(Service Instance per Host)。 当你使用这种模式,每个服务实例在其自己的主机上独立运行。此模式有两种不同的定制化:每个虚拟机的服务实例和每个容器的服务实例。
每个虚拟机模式的服务实例
当您使用“每个虚拟机服务实例”模式时,您将每个服务打包为一个虚拟机(VM)映像,比如Amazon EC2 AMI。每个服务实例都是一个VM(例如,使用该VM映像启动的EC2 instance)。
图6-2显示了这个模式的结构:
这是Netflix部署其视频流服务的主要方法。Netflix使用Aminator将其每个服务打包为EC2 AMI。每个正在运行的服务实例都是一个EC2实例。
您可以使用多种工具来构建自己的虚拟机。您可以配置您的持续集成(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。使用Bakery这样的软件即服务就意味着你不必浪费宝贵的时间来设置 AMI 创建基础设施。
每个虚拟机的服务实例模式具有许多优点。VM 的一个主要优点是每个服务实例都完全隔离运行。它具有固定数量的CPU和内存,并且无法从其他服务窃取资源。
将微服务部署为 VM 的另一个好处是,可以利用成熟的云基础结构。AWS 等云提供负载均衡和自动扩展等有用功能。
将服务部署为 VM 的另一大好处是,它将服务封装在实现技术中,一旦服务被打包为 VM,它就会变成一个黑匣子。VM 的管理 API 成为用于部署服务的 API 部署变得更简单、更可靠。
每个虚拟机的服务实例模式有一些缺点,但一个缺点是资源利用率较低。每个服务实例都有整个 VM(包括操作系统)的开销。此外,在典型的公共 IaaS 中,VM 的大小固定,VM 可能未得到充分利用。
此外,公共 IaaS 通常会对虚拟机收费,无论它们是忙碌还是空闲 AWS 等 IaaS 提供自动扩展,但很难对需求变化做出快速反应。因此,通常必须过度预配 VM,这会增加部署成本。
此方法的另一个缺点是,部署服务的新版本通常很慢,VM 映像由于其大小而通常构建速度很慢。此外,VM 的实例化速度通常很慢,这同样是因为它们的大小。此外,操作系统通常需要一些时间才能启动。但是,请注意,这并不是普遍正确的,因为存在诸如Boxfuse构建的轻量级VM。
每个虚拟机模式的服务实例的另一个缺点是,通常您(或组织中的其他人)负责许多无差别的繁重工作。除非您使用 Boxfuse 等工具来处理构建和管理 VM 的开销,否则您有责任。这种必要但耗时的活动会分散您核心业务的注意力。
现在,让我们看一下部署微服务的替代方法,该方法更轻量级,但仍具有 VM 的许多优点。
每个容器模式的服务实例(service instance per container pattern)
当你使用每个容器模式的服务实例,每个服务实例运行在他自己的容器中。容器是操作系统级别的虚拟化机制。一个容器包含一个活多个运行在一个沙箱中的进程。从进程的角度来看,它们有自己的端口命名空间和根文件系统。您可以限制容器的内存和 CPU 资源。某些容器实现还具有 I/O 速率限制。容器技术的示例包括 Docker 和 Solaris Zones。
图 6-3 显示了这个模式的结构:
为了使用这个模式,将服务打包为容器镜像。一个容器镜像是一个文件系统镜像,容器映像是由运行服务所需的应用程序和库组成的文件系统映像。某些容器映像由完整的 Linux 根文件系统组成。其他的则更轻巧。例如,要部署 Java 服务,您需要构建一个包含 Java 运行时的容器映像,可能是 Apache Tomcat 服务器和编译的 Java 应用程序。
将服务打包为容器映像后,启动一个或多个容器,通常在每个物理主机或虚拟主机上运行多个容器。 你可能使用一个集群管理器,比如 Kubernetes 或者 Marathon 来管理你的容器。群集管理器将主机视为资源池。它根据容器所需的资源和每个主机上的可用资源来决定放置每个容器的位置。
每个容器模式的服务实例既有好处也有缺点。容器的优势与 VM 的优点类似。它们将服务实例彼此隔离。您可以轻松监视每个容器消耗的资源。同样的,就像VMs, 容器封装了用于实现服务的技术。容器管理 API 还用作管理服务的 API。
然而,不想虚拟机,容器是一种轻量级技术。容器镜像的生成速度通常非常快。例如,在我的笔记本电脑上,将Spring Boot应用程序打包为Docker容器只需5秒。容器启动也很快,因为没有冗长的操作系统启动机制。当容器启动时,运行的是服务。
使用容器有一些缺点。虽然容器基础结构正在迅速成熟,但它并不像虚拟机的基础结构那样成熟。同时,容器不如虚拟机安全,因为容器彼此共享主机操作系统的内核。
容器的另一个缺点是,您负责管理容器镜像的无差别繁重工作。此外,除非您使用的是托管容器解决方案(如 Google 容器引擎或 Amazon EC2 容器服务 (ECS),否则您必须管理容器基础设施以及运行它的虚拟机基础设施。
此外,容器通常部署在具有每 VM 定价的基础结构上。因此,如前所述,为了处理负载峰值,可能会产生过度配置 VM 的额外成本。
有趣的是,容器和 VM 之间的区别可能会变得模糊。如前所述,Boxfuse VM 的构建和启动速度很快。Clear Containers 项目旨在创建轻量级虚拟机,人们对unikernels Docker,Inc的兴趣也越来越大。Docker,Inc于2016年初收购了Unikernel Systems。
还有一种更新且日益流行的无服务器部署概念,这种方法回避了必须在容器或虚拟机中部署服务之间进行选择的问题,让我们看看下一步。
无服务器部署(Serverless Deployment)
AWS Lambda 是无服务器部署技术的一个例子。他支持 Java, Node.js, 和 Python 服务。为了部署一个微服务,你将它打包成一个 zip 文件,并将它上传到 AWS Lambda。您还可以提供元数据,其中指定为处理请求(也称为事件)而调用的函数的名称。AWS Lambda 会自动运行足够的微服务实例来处理请求。只需根据所花费的时间和消耗的内存为每个请求付费。当然,细节是魔鬼,您很快就会看到AWS Lambda有局限性。但是,无论是作为开发人员,还是组织中的任何人都不需要担心服务器、虚拟机或容器的任何方面,这种想法非常吸引人。
一个 lambda 函数是一个无状态服务。它通常通过执行 AWS 服务来处理请求。例如,在将映像上传到 S3 存储桶时调用的 lambda 函数可以将项目插入 DynamoDB 映像表中,并将消息发布到 Kinesis 流以触发图像处理。lambda 函数还可以调用第三方 Web 服务。
有四种方式执行一个 lambda 函数:
- 直接,使用 Web 服务请求。
- 自动响应由 AWS 服务(如 S3、DynamoDB、Kinesis 或简单电子邮件服务)生成的事件
- 通过 AWS API 网关自动处理来自应用程序客户端的 HTTP 请求
- 定期,根据 cron-like schedule
就像你能看到的,AWS Lambda 是部署微服务的便捷方式。基于请求的定价意味着您只需为服务实际执行的工作付费。
此外,由于您不负责 IT 基础结构,因此可以专注于开发应用程序。
但是,存在一些重大限制。Lambda 函数不用于部署长时间运行的服务,例如使用来自第三方消息代理的消息的服务。请求必须在 300 秒内完成。服务必须是无状态的,因为从理论上讲,AWS Lambda 可能会为每个请求运行一个单独的实例。它们必须用受支持的语言之一编写。服务也必须快速启动;否则,它们可能会超时并终止。
总结
部署一个微服务程序是一个挑战。你可能有十个或者甚至上百个以各种余元和框架编写的服务。每个应用程序都是一个小型应用程序,具有自己特定的部署、资源、缩放和监视要求。有几种微服务部署模式,包括每个虚拟机的服务实例和每个容器的服务实例。部署微服务的另一个有趣的选择是AWS Lambda,这是一种无服务器方法。在本电子书的下一章也是最后一章中,我们将介绍如何将整体式应用程序迁移到微服务架构。
Microservices in Action: Deploying Microservices Across Varying Hosts with NGINX
NGINX对于各种类型的部署具有许多优势 - 无论是整体式应用程序,微服务应用程序还是混合应用程序(如下一章所述)。使用NGINX,您可以将智能从不同的部署环境抽象到NGINX中。如果您使用特定于不同部署环境的工具,则有许多应用程序功能的工作方式不同,但是如果您使用NGINX,则在所有环境中的工作方式相同。
这一特性还为NGINX和NGINX Plus开辟了第二个特定优势:能够通过同时在多个部署环境中运行应用程序来扩展应用程序。假设您拥有和管理本地服务器,但您的应用程序使用量正在增长,并且您预计峰值超出了这些服务器的处理能力。如果您已经"使用NGINX",而不是购买,预置和保持其他服务器温暖"以防万一",您有一个强大的替代方案:扩展到云中 - 例如,扩展到AWS。也就是说,处理本地服务器上的流量,直到达到容量,然后根据需要在云中启动其他微服务实例。
这只是迁移到NGINX的灵活性的一个例子。维护单独的测试和部署环境、切换环境的基础结构以及跨各种环境管理应用程序组合都变得更加现实和可实现。
NGINX微服务参考架构是明确的,旨在支持这种灵活的部署,在开发和部署期间假设使用容器为。如果您不在那里,请考虑迁移到容器,以及NGINX或NGINX plus,以简化您向微业务的迁移,并使您的应用程序,开发和部署灵活性适应未来, 和人员。