你的应用什么时候该拆分到多个容器中?

  • 金灵杰

2016 年 3 月 21 日

话题:语言 & 开发架构

Docker 的最佳实践提出了“一个容器内只运行一个进程”之后,是否需要将应用拆分到多个容器中就成了一个热门话题。本文以一个普通的 Java 应用作为讨论,从软件设计的角度来介绍为什么要将其拆分到多个容器中。

假设一个标准的 Java Web 应用包含以下两部分:

  1. 基于Struts 框架的前端应用 ;
  2. 基于Java EE的后端 REST API 服务 ;

这两部分通常运行在同一个容器(如 Tomcat)中,相互之间基于 REST 接口进行交互。类似这种应用,我们应该将其拆分到不同的容器中运行吗?

简单的回答:我们应该将其拆分到不同的容器中,但这需要经过谨慎的考虑。

抛开一些“普适”的原则(如“一个容器只运行一个进程”),我们主要从应用本身和部署策略上进行考虑。

首先,我们抛开具体业务场景,做一些设计思考(Design Thinking):

  1. JVM 本身支持多线程,因此通常的 Java 应用程序都运行在一个进程中,以多线程的方式进行并行处理。类似 Tomcat 这样的容器本身也支持将多个 Java 应用程序运行在一个 Java 进程中。
  2. 事实上,很多已经容器化的应用,在容器中实际运行时也是多进程的, 例如 Apache 的prefork模块。同时,当前流行的网络应用(如 Nginx)大量使用事件驱动和反应器模式,这些设计的方向,都是将 IO 操作交给内核,将业务逻辑处理交给子进程(线程)。Linux 内核非常善于调度子进程,但是这不是诸如 Kubernetes、Swarm 等容器调度工具擅长的地方。进程(线程)侧重于内核资源的划分,而容器侧重于集群资源的划分。
  3. “一个容器只运行一个进程”的原则,更像一种哲学。作为工程师,我们应该从实际出发考虑技术架构和应用逻辑。同时这个“最佳实践”甚至没有获得广泛的赞同,它的传播可能是因为对 Unix 运作原理缺乏了解导致的。
  4. Linux 容器(LXC)历史上有很多种形式,其中很多是建议在一个容器中运行多个进程的。Linux 容器实质上是 clone 系统调用、SELinux、cgroup 等技术的组合,上层使用 LXC 或者 Docker(libcontainer)对于这些底层技术是不相关的,因为最终都依赖于内核的隔离技术。
  5. 进程间交互有多种形式,如套接字、文件、网络等,每种方式都有自己的优缺点。如果考虑将应用拆分到不同容器中,对应用程序内部组件之间的通信方式会有很大影响。
  6. 代码、配置和数据的独立性,也对是否能够拆分到不同容器的重要考量。如果应用程序的代码、配置和数据相互独立性比较强,就能够方便的将其拆分成独立的组件,放置到独立的容器中;反之,如果应用内部分层不清,拆分成本会非常高。值得注意的是,我们没有必要为了容器化而对应用做过多的改造,只要能够按照 Docker 镜像的格式制作成镜像,我们仍然能够享受到 Docker 带来的便利:方便的分发(利用 Docker 注册中心)和运行(使用 docker run 命令)。

上面总结了一些从系统层面看是否需要拆分到多个容器,回到我们的例子:

  1. 这两个 Java 组件功能上相互独立。一个是 web 前端,一个是 API 服务。由于它们是完全不同的服务,是否运行在一个 JVM(Java 进程)中对性能损耗非常小(当然,这只是相对的,因为它们将无法公用堆内存和垃圾回收)。
  2. 这两个组件之间使用 REST API 进行交互,没有使用传统的进程间交互方式(套接字、共享内存等)。
  3. web 前端和 API 服务的使用场景不同,针对不同的场景,在运维层面可能需要独立的进行扩容。例如除了 web 前端之外,API 服务还能提供给其他组件使用,因此 API 服务可能会因为接入方的变化需要独立扩容和缩容。这时候就是充分利用容器编排框架(如 Kubernetes、Mesos、Swarm 等)能力的时候了。

基于上述三点考虑,我会将这个 Java 应用的两个组件拆分到独立的容器中。同时建议配合使用诸如 Kubernetes 的容器编排框架,将两个服务关联到一起。

总结一下我们的“最佳实践”:

如果你的应用代码、配置和数据相对独立,并且有清晰的交互形式,将应用组件拆分到多个容器中是有意义的。


感谢郭蕾对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群(已满),InfoQ 读者交流群(#2))。

语言 & 开发架构