1. 概述
从同一个 Docker 镜像运行多个容器是常见的做法。然而,当每个容器需要不同的配置时,挑战就出现了。为每个配置变体重新构建镜像不仅效率低下,而且违背了镜像可移植性的初衷。
在本文中,我们将介绍三种实用方法,帮助你使用相同镜像启动多个容器,并为每个容器配置不同的设置。这些方法覆盖了 DevOps 和后端团队在管理微服务或跨环境部署时常见的场景。
文中所有示例均基于 Docker v27 测试通过。
2. 使用相同镜像运行多个配置不同的容器
我们将会介绍三种常见方式来实现多个容器共享同一镜像但使用不同配置。
2.1. 准备演示项目
为了便于演示,我们准备了一个简单的 Express 服务:
- 服务启动时会打印配置信息
- 支持通过环境变量或挂载的 JSON 文件读取配置
- 提供浏览器访问页面展示当前配置
项目结构如下:
.
├── app/
│ └── index.js # Express 服务入口
├── config/
│ ├── analytics-config.json
│ └── notification-config.json
├── Dockerfile
├── docker-compose.env.yml
└── docker-compose.file.yml
你可以通过以下命令克隆项目:
git clone https://github.com/Baeldung/ops-tutorials/
cd docker-modules/managing-multiple-docker-containers-same-image-different-configs
准备好项目后,我们开始介绍具体方法。
2.2. 方法一:使用 docker run
传入环境变量
这是最简单直接的方式,适合配置项较少的情况。
原理: 启动容器时通过 -e
参数传入环境变量,服务启动时读取这些变量并覆盖默认配置。
服务配置读取逻辑如下(简化版):
let config = {
SERVICE_NAME: process.env.SERVICE_NAME || "default-service",
ENVIRONMENT: process.env.ENVIRONMENT || "development",
LOG_LEVEL: process.env.LOG_LEVEL || "info",
PORT: process.env.PORT || 3000,
};
构建镜像:
docker build -t config-demo .
运行第一个容器:
docker run -d \
--name billing_service \
-e SERVICE_NAME=billing-service \
-e ENVIRONMENT=staging \
-e LOG_LEVEL=debug \
-e PORT=3001 \
-p 3001:3001 \
config-demo
运行第二个容器:
docker run -d \
--name user_service \
-e SERVICE_NAME=user-service \
-e ENVIRONMENT=production \
-e LOG_LEVEL=info \
-e PORT=3002 \
-p 3002:3002 \
config-demo
✅ 优点:
- 简单快捷
- 无需额外文件
- 适合快速测试或少量配置
❌ 缺点:
- 配置项多时不易管理
- 容易出错,缺乏结构化
效果截图:
2.3. 方法二:使用 Docker Compose 定义多个服务
该方法通过 YAML 文件统一管理多个容器的配置,更适合本地开发或集成测试。
原理: 在 docker-compose.env.yml
中定义多个服务,每个服务使用相同的镜像,但配置不同环境变量。
示例文件内容:
services:
audit_logger:
build:
context: .
dockerfile: Dockerfile
container_name: audit_logger
environment:
- SERVICE_NAME=audit-logger
- ENVIRONMENT=qa
- LOG_LEVEL=warn
- PORT=3003
ports:
- "3003:3003"
report_engine:
build:
context: .
dockerfile: Dockerfile
container_name: report_engine
environment:
- SERVICE_NAME=report-engine
- ENVIRONMENT=dev
- LOG_LEVEL=error
- PORT=3004
ports:
- "3004:3004"
启动服务:
docker compose -f docker-compose.env.yml up --build -d
访问效果:
✅ 优点:
- 配置集中管理
- 支持版本控制
- 易于扩展和复用
❌ 缺点:
- 仍基于扁平环境变量,复杂配置难以管理
2.4. 方法三:挂载配置文件到容器中
适用于配置复杂、需要结构化管理的场景。
原理: 通过 volume 挂载 JSON 配置文件,并在服务启动时加载该文件。
docker-compose.file.yml 示例:
services:
analytics_processor:
build:
context: .
dockerfile: Dockerfile
container_name: analytics_processor
environment:
- CONFIG_PATH=/app/config.json
volumes:
- ./config/analytics-config.json:/app/config.json
ports:
- "3005:3005"
notification_worker:
build:
context: .
dockerfile: Dockerfile
container_name: notification_worker
environment:
- CONFIG_PATH=/app/config.json
volumes:
- ./config/notification-config.json:/app/config.json
ports:
- "3006:3006"
服务读取配置逻辑:
if (process.env.CONFIG_PATH) {
try {
const configPath = path.resolve(process.env.CONFIG_PATH);
const fileContent = fs.readFileSync(configPath, "utf-8");
const fileConfig = JSON.parse(fileContent);
config = { ...config, ...fileConfig };
console.log(`[INFO] Loaded config from ${configPath}`);
} catch (err) {
console.error(
`[ERROR] Failed to load config from ${process.env.CONFIG_PATH}:`,
err.message
);
}
}
启动命令:
docker compose -f docker-compose.file.yml up --build -d
访问效果:
✅ 优点:
- 配置结构清晰
- 支持版本控制
- 便于 CI/CD 集成
- 生产环境常用方式
❌ 缺点:
- 配置文件需维护多个版本
- 需要额外文件管理
3. 如何选择合适的方法
根据你的使用场景和配置复杂度,可以选择最适合的方法:
3.1. 使用 docker run
传入环境变量的适用场景
✅ 适合:
- 快速测试
- 单个容器运行
- 配置项较少(<5)
- 无需版本控制
⚠️ 不适合:
- 多个容器管理
- 配置复杂或结构化需求
- 生产部署或 CI/CD 场景
3.2. 使用 Docker Compose 定义多个服务的适用场景
✅ 适合:
- 本地开发
- 集成测试
- 多个服务编排
- 配置项中等(5~20)
⚠️ 不适合:
- 超过 20+ 配置项
- 需要结构化配置
- 多环境部署
3.3. 挂载配置文件的适用场景
✅ 适合:
- 生产环境部署
- 结构化配置(如 JSON/YAML)
- 多环境配置管理
- CI/CD 自动化流程
⚠️ 不适合:
- 快速测试
- 配置项少
- 本地快速调试
4. 总结
本文介绍了三种使用相同镜像运行多个配置不同容器的方法:
方法 | 适用场景 | 配置管理 | 可维护性 | 推荐指数 |
---|---|---|---|---|
docker run + 环境变量 |
快速测试 | 简单 | 低 | ⭐⭐ |
Docker Compose + 环境变量 | 本地开发 / 测试 | 中等 | 中 | ⭐⭐⭐ |
挂载配置文件 | 生产部署 / 多环境 | 复杂 | 高 | ⭐⭐⭐⭐ |
选择合适的方法可以提高容器管理的效率,也能更好地支持 CI/CD 和多环境部署。建议根据实际需求选择合适方案,避免过度设计或配置混乱。