Docker Compose 进阶配置与最佳实践笔记

这是一份基于我们之前讨论内容整理的 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        # 也可省略,默认构建到最后一个阶段

⚠️ 避坑指南

  1. 别名大小写敏感AS Buildertarget: builder 不匹配,必须完全一致。
  2. 构建截断target 之后的阶段完全跳过,不会浪费构建时间。
  3. 缓存优势 :修改了生产阶段代码但 target: builder 时,Docker 不会重新构建 deps 层,极大提升开发体验。
  4. 慎用索引号 :虽然 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 (不健康)。

⚠️ 避坑指南与替代方案

  1. 镜像里没有 curl :Alpine 等极简镜像默认不带 curl,会报 executable file not found
    • 替代方案 A (用 wget)test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
    • 替代方案 B (用专用探针,推荐) :引入 dockerizegrpc_health_probe 等极小的二进制工具。
  2. 端点必须轻量/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

⚠️ 避坑指南

  1. 默认路径陷阱 :短语法挂载的 config 路径是 /configs/<name>。如果你的应用写死了读取 /etc/nginx/nginx.conf必须使用长语法指定 target
  2. 不支持热更新 :修改宿主机的 config 文件后,容器内不会 自动同步。必须执行 docker compose up -d 重建容器才能生效。
  3. 构建阶段不可见configssecrets 只在运行时 注入。如果 Dockerfile 构建时需要配置文件,仍需使用 COPYARG

📋 总结:生产环境 Compose 检查清单 (Checklist)

在将 docker-compose.yml 部署到生产环境前,请核对以下事项:

  • 构建 :是否使用了多阶段构建,并通过 target 剔除了源码和开发依赖?
  • 网络 :所有不需要公网访问的端口(如数据库、Redis、调试端口),是否都绑定了 127.0.0.1
  • 健康 :核心服务是否配置了 healthcheck,且探针接口足够轻量?
  • 配置 :是否将环境相关的配置抽离到了 configs,敏感信息使用了 secrets(或外部 Vault)?
  • 重启 :是否配置了 restart: unless-stoppedon-failure 以保证进程崩溃后自动拉起?
  • 日志 :是否限制了日志大小(如 logging.driver: json-file, max-size: 10m),防止磁盘被撑爆?
相关推荐
是良辰1 小时前
Docker Nginx HTTPS 自签证书部署完整操作文档
nginx·docker·https
瞬间&永恒~3 小时前
【Docker】(四)Namespace 详解
运维·docker·容器
就改了3 小时前
Docker安装并运行Redis
redis·docker·容器
yagami_gagami4 小时前
第四届黄河流域公安院校电子物证个人赛服务器取证
linux·服务器·网络·mysql·安全·docker·llama
小挪号底迪滴5 小时前
Docker 多阶段构建实战:让 Python 镜像更小、更快、更安全
python·安全·docker
风曦Kisaki5 小时前
Kubernetes(K8s)笔记Day07: StatefulSet 有状态控制器详解与使用案例
linux·docker·云原生·容器·kubernetes
张文君5 小时前
docker registry 删除镜像
运维·docker·容器
cuiyaonan20005 小时前
some weird issues regarding docker
运维·docker·容器
zerwave5 小时前
Docker 学习:多容器网络互通——从 host 模式到自定义 bridge 网络
网络·学习·docker