这是一份基于我们之前讨论内容整理的 Docker Compose 进阶配置与最佳实践笔记。笔记按照功能模块划分,包含了核心原理、代码示例、对比表格以及生产环境的"避坑指南",非常适合作为团队内部 Wiki 或个人技术备忘录。
🐳 Docker Compose 进阶配置与最佳实践笔记
前言 :Docker Compose 不仅仅是"把容器跑起来"的工具。在迈向生产环境的过程中,如何通过 target 优化构建、通过 ports 控制网络边界、通过 healthcheck 保证服务可靠性、通过 configs 实现配置解耦,是区分"能用"与"好用"的关键。
模块一:构建优化 (build.target)
核心场景:使用一个 Dockerfile 支撑开发、测试、生产等多套环境,避免维护多个重复的 Dockerfile。
1. 核心原理
在多阶段构建(Multi-stage Build)中,target 的作用是指定 Dockerfile 中某个特定阶段作为构建终点 。区分的依据是 FROM 指令后面的 AS <stage-name> 别名。
2. 标准 Dockerfile 示例 (Node.js)
dockerfile
# ========== 阶段1: 安装依赖 ==========
FROM node:20-alpine AS deps # ← 别名 "deps"
WORKDIR /app
COPY package*.json ./
RUN npm ci --production=false
# ========== 阶段2: 编译构建 ==========
FROM node:20-alpine AS builder # ← 别名 "builder"
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# ========== 阶段3: 生产运行 ==========
FROM node:20-alpine AS production # ← 别名 "production"
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/main.js"]
3. Compose 中的环境区分策略
yaml
services:
# 🟢 开发环境:停在 builder 阶段,包含源码和 devDependencies,方便热重载
app-dev:
build:
context: .
target: builder
# 🟡 CI 单元测试:同样停在 builder,编译完成可运行 test 命令
app-test:
build:
context: .
target: builder
command: npm run test
# 🔴 生产环境:跑完整个流程,最小镜像,仅含运行时 + 产物
app-prod:
build:
context: .
target: production # 也可省略,默认构建到最后一个阶段
⚠️ 避坑指南
- 别名大小写敏感 :
AS Builder和target: builder不匹配,必须完全一致。 - 构建截断 :
target之后的阶段完全跳过,不会浪费构建时间。 - 缓存优势 :修改了生产阶段代码但
target: builder时,Docker 不会重新构建deps层,极大提升开发体验。 - 慎用索引号 :虽然
target: 1合法(从0开始),但调整阶段顺序后会错位,强烈建议始终使用别名。
模块二:网络安全 (ports)
核心场景:控制容器端口的暴露范围,防止内部服务或调试端口被公网扫描和攻击。
1. 端口绑定的本质
"5555:5555" 这种短语法等价于 "0.0.0.0:5555:5555",意味着将容器端口映射到宿主机所有网络接口。
| 写法 | 绑定地址 | 谁能访问 | 风险等级 |
|---|---|---|---|
"5555:5555" |
0.0.0.0 (所有网卡) |
本机、局域网、公网(若防火墙放行) | 🔴 极高 |
"127.0.0.1:5555:5555" |
仅 loopback | 只有宿主机自身 | 🟢 安全 |
"192.168.1.100:5555:5555" |
指定内网 IP | 仅该内网可达 | 🟡 中等 |
2. 为什么调试端口(如 5555)极其危险?
以 Node.js inspector、Python debugpy、Java JDWP 为例,这些调试协议通常没有认证机制 ,连上就能执行任意代码(RCE)。绑定 0.0.0.0 后,公网扫描器可直接接管服务器。
✅ 最佳实践
yaml
services:
debug-app:
ports:
- "127.0.0.1:5555:5555" # ✅ 仅本机可访问
远程调试方案 :如果需要从本地电脑连接远程服务器的调试端口,必须使用 SSH 隧道,而不是直接暴露端口:
bash
# 在本地机器执行,安全地将远程容器的调试端口转到本地
ssh -L 5555:127.0.0.1:5555 user@remote-server
模块三:服务可靠性 (healthcheck)
核心场景 :让 Docker 自动探测容器内的应用是否真正"可用"(而不仅仅是进程没死),以便配合 Swarm 或 Compose 的 depends_on 实现平滑启动和故障重启。
1. 经典探针命令拆解
yaml
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 5s
retries: 3
参数深度解析:
CMD-SHELL:表示命令会经过/bin/sh -c解析,因此可以使用||等逻辑运算符。(如果是CMD则直接 exec,不支持管道和逻辑符)。curl -s:Silent,静默模式,不输出进度条,防止污染容器日志。curl -f:Fail fast,关键参数 。默认 curl 在遇到 HTTP 404/500 时仍返回退出码 0,加上-f后,HTTP 错误会返回非零退出码。localhost:指的是容器内部的 localhost,不是宿主机。|| exit 1:如果 curl 失败(网络不通/超时/HTTP错误),显式返回退出码 1,统一失败信号。
2. 判定逻辑
Docker 只看退出码 :0 = healthy (健康),非0 = unhealthy (不健康)。
⚠️ 避坑指南与替代方案
- 镜像里没有 curl :Alpine 等极简镜像默认不带 curl,会报
executable file not found。- 替代方案 A (用 wget) :
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"] - 替代方案 B (用专用探针,推荐) :引入
dockerize或grpc_health_probe等极小的二进制工具。
- 替代方案 A (用 wget) :
- 端点必须轻量 :
/health接口应只做内存级检查(如返回{"status":"ok"}),绝不能在健康检查里执行重数据库查询,否则高频探测会拖垮数据库。
模块四:配置解耦 (configs & secrets)
核心场景:将配置文件和敏感数据作为独立对象管理,与镜像解耦,实现"一次构建,多环境运行"。
1. 核心概念对比
| 对比项 | volumes (挂载) |
configs (配置) |
secrets (机密) |
|---|---|---|---|
| 主要用途 | 开发环境、大文件、需读写 | 生产环境普通配置文件 | 密码、Token、证书私钥 |
| 容器内路径 | 自定义任意路径 | 默认 /configs/<name> |
默认 /run/secrets/<name> |
| 存储介质 | 宿主机文件系统 | Compose 管理 (Swarm下加密) | tmpfs (内存中,不落盘) |
| 读写权限 | 可读写 | 强制只读 | 强制只读 |
2. 完整配置示例
yaml
# ========== 定义配置与机密对象 ==========
configs:
nginx_conf: # 对象名称(供服务引用)
file: ./configs/nginx.conf # 宿主机源文件路径
app_settings:
file: ./configs/app.yaml
secrets:
db_password:
file: ./secrets/db_password.txt # 敏感文件,切勿提交到 Git
services:
web:
image: nginx:alpine
configs:
# ✅ 短语法:挂载到 /configs/nginx_conf (只读)
- nginx_conf
# ✅ 长语法:自定义容器内路径和文件权限
- source: app_settings
target: /etc/app/settings.yaml # 指定应用期望的读取路径
uid: "1000" # 文件所有者 UID
gid: "1000" # 文件所属组 GID
mode: 0440 # 文件权限(八进制)
api:
image: my-api:latest
secrets:
- db_password # 挂载到 /run/secrets/db_password
⚠️ 避坑指南
- 默认路径陷阱 :短语法挂载的 config 路径是
/configs/<name>。如果你的应用写死了读取/etc/nginx/nginx.conf,必须使用长语法指定target。 - 不支持热更新 :修改宿主机的 config 文件后,容器内不会 自动同步。必须执行
docker compose up -d重建容器才能生效。 - 构建阶段不可见 :
configs和secrets只在运行时 注入。如果 Dockerfile 构建时需要配置文件,仍需使用COPY或ARG。
📋 总结:生产环境 Compose 检查清单 (Checklist)
在将 docker-compose.yml 部署到生产环境前,请核对以下事项:
- 构建 :是否使用了多阶段构建,并通过
target剔除了源码和开发依赖? - 网络 :所有不需要公网访问的端口(如数据库、Redis、调试端口),是否都绑定了
127.0.0.1? - 健康 :核心服务是否配置了
healthcheck,且探针接口足够轻量? - 配置 :是否将环境相关的配置抽离到了
configs,敏感信息使用了secrets(或外部 Vault)? - 重启 :是否配置了
restart: unless-stopped或on-failure以保证进程崩溃后自动拉起? - 日志 :是否限制了日志大小(如
logging.driver: json-file,max-size: 10m),防止磁盘被撑爆?