这篇文章发表已超过一年。较早的文章可能包含过时内容。请检查页面中的信息自发布以来是否已不再准确。

分布式系统工具包:组合容器模式

我很荣幸能在 DockerCon 2015 上介绍一些 Kubernetes 的概念,我想写一篇博客文章,与那些未能到场的朋友分享这些想法。

在过去两年里,容器已成为一种越来越流行的打包和部署代码的方式。容器镜像解决了现有打包和部署工具在实际中遇到的许多问题,除了这些显著的优势之外,容器还为我们提供了一个机会,来从根本上重新思考我们构建分布式应用的方式。就像面向服务架构 (SOA) 鼓励将应用分解为模块化、聚焦的Service一样,容器应鼓励进一步将这些Service分解为紧密协作的模块化容器。通过建立边界,容器使用户能够使用模块化、可重用的组件构建其Service,这反过来使得Service比由单体容器构建的应用更可靠、更具可扩展性、构建速度更快。

在许多方面,从虚拟机转向容器就像 20 世纪 70 年代和 80 年代初期的单体程序转向 80 年代后期及以后的模块化面向对象程序。容器镜像提供的抽象层与面向对象编程中类的抽象边界有很多共同之处,它提供了同样的机会来提高开发者生产力和应用质量。就像正确的编程方式是将关注点分离到模块化对象中一样,在容器中打包应用的正确方式是将关注点分离到模块化容器中。从根本上说,这意味着不仅要分解整个应用,还要将任何一个服务器中的组件分解成多个易于参数化和可重用的模块化容器。通过这种方式,就像现代语言中无处不在的标准库一样,大多数应用开发者可以将他人编写的模块化容器组合起来,从而更快地构建应用,并使用更高质量的组件。

以模块化容器的角度思考的好处是巨大的,特别是,模块化容器提供以下优势:

  • 加速应用开发,因为容器可以在团队之间甚至更大的社区中重用
  • 固化专家知识,因为每个人都基于反映最佳实践的单一容器化实现进行协作,而不是基于大量功能大致相同的自制容器
  • 赋能敏捷团队,因为容器边界天然地界定了团队的职责和契约
  • 提供关注点分离并聚焦于特定功能,减少了混乱的依赖关系和不可测试的组件

从模块化容器构建应用意味着考虑那些协作提供Service的共生容器组,而不是一个Service一个容器。在 Kubernetes 中,这种模块化容器Service的体现是 Pod。Pod 是一组共享文件系统、内核命名空间和 IP 地址等资源的容器。Pod 是 Kubernetes 集群中调度的最小单元,恰恰是因为 Pod 中容器的共生特性要求它们必须被共同调度到同一台机器上,而可靠地实现这一点的唯一方法就是将容器组作为原子调度单元。

当你开始以 Pod 为单位思考时,自然会出现一些模块化应用开发的通用模式,这些模式会多次重复出现。我相信随着 Kubernetes 的进一步发展,会识别出更多此类模式,但这里列出我们常见的三种:

示例 #1:Sidecar 容器

Sidecar 容器扩展和增强“主”容器,它们接受现有容器并使其变得更好。例如,考虑一个运行 Nginx Web 服务器的容器。添加另一个将文件系统与 git 仓库同步的容器,并在这些容器之间共享文件系统,你就构建了一个 Git push-to-deploy 功能。而且你以模块化的方式实现了它,git 同步器可以由不同的团队构建,并可以在许多不同的 Web 服务器(Apache、Python、Tomcat 等)中重用。由于这种模块化特性,你只需编写和测试你的 git 同步器一次,就可以在无数的应用中重用它。如果它是别人写的,你甚至都不需要自己动手。

Sidecar Containers

示例 #2:Ambassador 容器

Ambassador 容器将本地连接代理到外部世界。例如,考虑一个带有只读副本和一个写入主节点的 Redis 集群。你可以创建一个 Pod,将你的主应用与一个 Redis ambassador 容器组合在一起。这个 ambassador 容器是一个代理,负责分离读写请求并将其发送到相应的服务器。由于这两个容器共享网络命名空间,它们共享一个 IP 地址,你的应用可以在“localhost”上打开连接并找到这个代理,而无需任何服务发现。对于你的主应用而言,它只是连接到 localhost 上的一个 Redis 服务器。这非常强大,不仅因为它实现了关注点分离,并且不同的团队可以轻松拥有这些组件,而且还在于在开发环境中,你可以简单地跳过代理,直接连接到运行在 localhost 上的 Redis 服务器。

Ambassador Containers

示例 #3:Adapter 容器

Adapter 容器标准化和规范化输出。考虑监控 N 个不同应用的任务。每个应用可能使用不同的方式导出监控数据(例如 JMX、StatsD、应用特定的统计信息),但每个监控系统都期望其收集的监控数据采用一致且统一的数据模型。通过使用组合容器的 Adapter 模式,你可以创建 Pods,将应用容器与知道如何进行转换的 adapter 容器组合在一起,从而将来自不同系统的异构监控数据转换为单一的统一表示形式。同样,由于这些 Pods 共享命名空间和文件系统,这两个容器之间的协作变得简单明了。

Adapter Containers

在所有这些情况下,我们都将容器边界用作封装/抽象边界,从而能够构建模块化、可重用的组件,并将它们组合起来构建应用。这种重用使我们能够更有效地在不同开发者之间共享容器,跨多个应用重用代码,并且通常能够更快地构建更可靠、更健壮的分布式系统。我希望你已经了解 Pods 和组合容器模式如何帮助你更快地构建健壮的分布式系统,并实现容器代码的重用。要在你自己的应用中尝试这些模式,我建议你查看开源的 Kubernetes 或 Google Container Engine。