1. 概述

容器技术因其隔离性、安全性、可扩展性和可移植性等优势而广受欢迎。虽然技术上可以在一个容器中运行多个服务或应用,但这并不是推荐的做法。

在本文中,我们将探讨为什么推荐每个容器只运行一个服务或应用。首先我们会解释“单一职责”的含义,接着会从多个方面说明这种设计原则带来的好处。

2. 单一职责的容器设计

每个容器应该只承担一个职责,并且把这个职责做到位。这被称为“单一职责原则”,是面向对象设计中 SOLID 原则 中“单一职责原则”的延伸。正如类应该只因一个原因而改变一样,容器也应如此。

ELK Stack 为例,Elasticsearch 是一个强大的搜索和分析引擎,Logstash 是数据收集引擎,Kibana 则是数据可视化仪表盘。虽然技术上可以将它们放在同一个容器中运行,但这并不是一个好的设计,因为每个组件都有其独立的职责。

服务和应用的隔离性从容器化诞生之初就是核心目标之一。虽然可以在一个容器中运行多个进程,例如通过 shell 脚本作为 entrypoint 启动多个服务,但除非这些服务之间高度耦合,否则应尽量避免这种做法。

接下来我们会详细说明每个容器运行一个服务的优势。

3. 每个容器一个服务的优势

3.1. 可复用性

容器只运行一个服务时,它的可复用性更强。✅
我们可以将这个容器作为部署的基本单元,在不同的环境中通过配置调整来实现不同用途。

举个例子:如果我们使用 Elasticsearch、Logstash 和 Kibana 各自独立的容器来搭建 ELK Stack,就可以轻松地通过复制 Elasticsearch 容器来构建高可用的集群。

但如果我们把 Elasticsearch 和 Logstash 放在同一个容器里,当需要扩展 Elasticsearch 时,就会连带启动不必要的 Logstash 实例。而 Logstash 是一个资源消耗较大的服务,通常一个实例就足够使用。

3.2. 可扩展性

横向扩展变得更容易。✅
只需运行更多容器实例即可快速扩容,无需额外配置或修改容器内容。

比如,当 Elasticsearch 集群负载增加时,只需启动更多的 Elasticsearch 容器实例即可提升性能。反之,负载下降时也可以轻松缩减容器数量。

3.3. 隔离性

容器的核心优势之一就是隔离性。✅
将不同服务部署在不同容器中,可以避免某个服务占用过多 CPU 或内存资源而影响其他服务。

我们可以通过 Docker 的 --memory--cpus 参数来限制容器使用的资源上限,从而更好地实现资源隔离。

示例命令如下:

docker run -d --name es-node1 --memory="2g" --cpus="1" elasticsearch:7.17

3.4. 更小的镜像体积

单服务容器的镜像更小,构建更快,部署也更高效。✅
多个服务打包在一个镜像中会增加不必要的依赖和体积,降低构建和部署效率。

例如,一个只包含 Nginx 的镜像比同时包含 Nginx 和 Redis 的镜像要小很多,构建和拉取速度也快得多。

3.5. 更容易测试和调试

服务之间解耦后,调试和测试也更简单。✅
如果每个服务都在独立容器中运行,开发者只需关注自己负责的部分,而无需关心其他服务的状态。

同时,隔离环境也更容易定位问题,避免多个服务之间相互干扰。

3.6. 更容易升级和维护

多服务共存的容器在升级时容易遇到依赖冲突,导致“依赖地狱(dependency hell)”。❌
比如,服务 A 依赖库 X 的 v1.0,而服务 B 升级后需要 X 的 v2.0,这时就会出现冲突。

但如果每个服务都有独立容器,就可以独立升级其依赖库甚至操作系统版本,互不影响。

3.7. 日志收集更清晰

当多个服务写入日志到同一个容器的标准输出或日志文件时,日志分析变得复杂。⚠️
需要额外处理才能区分不同服务的日志内容。

而将每个服务运行在独立容器中,日志自然分离,便于使用容器的日志管理工具(如 docker logs)进行查看和分析。

4. 总结

本文介绍了为什么推荐每个容器只运行一个服务或应用。核心思想是“单一职责原则”,这与面向对象设计中的“单一职责原则”一脉相承。

通过单一服务容器,我们可以获得以下优势:

  • ✅ 更高的可复用性
  • ✅ 更方便的横向扩展
  • ✅ 更好的隔离性
  • ✅ 更小的镜像体积
  • ✅ 更容易的测试和调试
  • ✅ 更简单的依赖管理和版本升级
  • ✅ 更清晰的日志收集与分析

虽然在某些特定场景下(如 init 容器或临时调试)可以运行多个服务,但作为通用实践,每个容器只运行一个服务仍是最佳选择。


原始标题:Why Is It Recommended to Run One Process per Container?