从 docker run 到 docker compose:手把手改造与多环境配置管理

举报
yd_248971855 发表于 2026/11/07 14:15:45 2026/11/07
【摘要】 从 docker run 到 docker compose:手把手改造与多环境配置管理摘要:当服务器上的 docker run 命令越积越多,参数越改越长,"哪个容器是谁起的、为什么挂了不自动重启"变成解不开的谜团。本文以一套真实的数据库加应用双容器架构为例,完整演示从 docker run 到 docker compose 的改造过程,覆盖启动顺序控制、环境变量分层、多环境配置切换与升级...

从 docker run 到 docker compose:手把手改造与多环境配置管理

摘要:当服务器上的 docker run 命令越积越多,参数越改越长,"哪个容器是谁起的、为什么挂了不自动重启"变成解不开的谜团。本文以一套真实的数据库加应用双容器架构为例,完整演示从 docker run 到 docker compose 的改造过程,覆盖启动顺序控制、环境变量分层、多环境配置切换与升级回滚,最后给出一份迁移对照表,适用于把零散容器统一为可维护编排的实战场景。

一、场景引入:一条长命令的崩塌

系列前面几篇里出现过这样的启动命令:

docker run -d \
  --name timescaledb \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=xxx \
  -v /data/pgdata:/var/lib/postgresql/data \
  --restart always \
  timescale/timescaledb:2.28.0-pg16
docker run -d \
  --name backend \
  -p 9018:8080 \
  -e SPRING_DATASOURCE_URL=jdbc:postgresql://timescaledb:5432/mydb \
  --restart unless-stopped \
  backend:runtime

单容器时勉强能用,问题出在规模化之后:

  1. 配置不可追溯——半年后想改密码,得先 docker inspect 翻历史再看 shell history,运气差一点就找不回完整参数;
  2. 启动顺序失控——机器重启后 timescaledb 还在初始化,backend 已经起来连库报错,靠 Spring 重试机制侥幸扛过去,但没人知道能扛几次;
  3. 环境差异靠手工——测试机、预发、生产三套环境,靠三个运维各自维护一份 run 命令,差异点只能口口相传。
    docker compose 把这三个问题一次性收编:所有配置进 yml、启动顺序靠声明、环境差异靠文件切换。

二、第一步:把 run 命令翻译成 compose

改造过程是一对一的参数映射,先看完整可用的 yml:

services:
  timescaledb:
    image: timescale/timescaledb:2.28.0-pg16
    container_name: timescaledb
    restart: always
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - /data/pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 30s
    networks:
      - app-net
  backend:
    image: backend:runtime
    container_name: backend
    restart: unless-stopped
    ports:
      - "9018:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://timescaledb:5432/mydb
      SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
    depends_on:
      timescaledb:
        condition: service_healthy
    networks:
      - app-net
networks:
  app-net:
    driver: bridge

几个关键映射关系:

  • --restart always → restart: always;
  • -v 宿主机:容器 → volumes: 下的同一写法;
  • -e KEY=value → environment: 字段;
  • 隐式默认 bridge 网络里无法用容器名解析的问题,被顶层 networks: 一并解决——两服务都进了自定义网络,backend 直接用 timescaledb:5432 连库,JDBC 配置从上一篇文章的"绕宿主机 IP"优雅升级为"服务名直连"。
    改造完一条 docker compose up -d 拉起全栈,docker compose down 一键停掉并清理网络,比逐个 run/stop 简洁得多。

三、关键陷阱:depends_on 不等于服务就绪

这是 compose 迁移中最经典的翻车点。
只写:

depends_on:
  - timescaledb

只保证容器启动顺序,不保证数据库真正可连接——compose 只等 timescaledb 容器状态变成 running,而 PG 这时候可能还在初始化数据目录,backend 立刻连库就是 Connection refused。
正确姿势是给数据库配 healthcheck,让依赖方声明 condition: service_healthy:

depends_on:
  timescaledb:
    condition: service_healthy

compose 会一直等 pg_isready 探测通过才启动 backend,不需要 sleep 也不需要 wait-for-it sh 脚本。start_period 参数容易被忽略——它表示容器启动后的前 30 秒内 healthcheck 失败不计入重试,给慢启动服务留缓冲,MySQL/PG 这类一定要给够。
三种 condition 的适用场景:

condition 等待条件 典型场景
service_started 容器启动(默认) 真正无依赖关系的并行服务
service_healthy 健康检查通过 数据库、缓存等持续服务
service_completed_successfully 一次性任务退出码为 0 数据库迁移、初始化脚本

四、环境变量分层:三层配置策略

环境变量管理是 compose 迁移的第二大深水区,三种来源名字长得像但作用完全不同:

  • .env 文件:用于 compose 文件本身的变量插值,解决 ${DB_PASSWORD} 这种占位符;
  • env_file: 字段:把指定文件里的变量注入容器运行时环境;
  • environment: 字段:在 yml 里硬编码,优先级最高。
    推荐的三层结构:
# docker-compose.yml 里只放结构,不放真实值
environment:
  POSTGRES_PASSWORD: ${DB_PASSWORD}
# .env.common(提交到 git,不含敏感信息)
DB_HOST=timescaledb
DB_PORT=5432
DB_NAME=mydb
# .env.prod(不提交 git,敏感信息)
DB_PASSWORD=prod_real_password

启动时切换:

docker compose --env-file .env.prod up -d

优先级口诀:命令行 -e > environment 字段 > env_file > .env 文件 > 系统环境变量。同名变量按这个顺序覆盖,排错时按这个顺序倒查即可。
一个高频坑:改了环境变量后 docker compose restart 不会让新变量生效——restart 只是重启进程,配置注入发生在容器创建阶段。必须 docker compose up -d(compose 会检测配置变化重建容器)。

五、多环境配置的标准姿势

三套环境的最小差异配置方案:

project/
├── docker-compose.yml        # 基础结构(必放)
├── docker-compose.override.yml  # 本地开发覆盖(自动加载)
├── .env.common               # 公共变量
├── .env.dev                  # 开发环境变量
├── .env.prod                 # 生产环境变量(gitignore)
└── .env.example              # 模板文件,给新同事参考

两种组织思路:

  • 单文件 + 多 env(推荐小中型):所有环境共用一份 yml,差异全部抽到 env 文件,启动时 --env-file 切换;
  • 多 yml 覆盖(适合结构差异大的环境):base 文件加 override 文件,compose 自动合并后者。预发环境端口不同、生产环境挂载路径不同这类结构性差异用这种方式更清晰。
    .env.example 是团队协作的关键——新人入职照着模板建自己的 env 文件,不用追着问"这个变量哪儿来的"。

六、迁移不是一次性动作:升级与回滚

compose 化之后,日常运维从"翻命令"变成"改文件":
升级服务:

# 改 yml 中的 image 版本
docker compose pull
docker compose up -d

compose 自动比对配置差异,只重建受影响的服务,依赖它的服务也会跟着重建。
回滚:

# yml 改回上一版本
git checkout HEAD~1 -- docker-compose.yml
docker compose up -d

yml 进 git 就是天然的回滚凭证,这是 docker run 时代无法想象的便利。
查看实际生效的配置(排错时必用):

docker compose config

它把变量插值后的最终 yml 打印出来,环境变量没生效时第一个命令就是它。

七、迁移对照表

docker run 参数 compose 字段 备注
--name container_name: 同项目内建议保留
-p ports: 列表形式,注意加引号防 YAML 解析坑
-v volumes: bind 与命名卷写法一致
-e environment: 敏感值用 ${VAR} 插值
--restart always restart: always 完全对应
--network 顶层 networks: + 服务级引用 自定义网络才能容器名解析
--memory/--cpus deploy.resources.limits: compose v2 下直接生效
--add-host extra_hosts: 数组形式
启动顺序 depends_on + condition 配合 healthcheck 使用

八、总结

docker run 到 compose 的改造,本质是把命令行的命令式操作升级为文件的声明式管理——配置可追溯(进 git)、依赖可声明(healthcheck + condition)、环境可切换(env 分层)、回滚可执行(git checkout + up)。
迁移决策点只有两个:单容器简单场景维持 docker run 即可;一旦出现多服务、有启动顺序依赖、需要在多环境间切换,就该 compose 化。改造时把 healthcheck、env 分层、networks 三件事一次做对,后面所有运维操作都会顺畅得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。