Docker Compose 部署多容器应用实战:环境变量、数据卷与重启策略配置要点

    发布时间:2026-09-15 12:31 更新时间:2026-09-15 12:31 阅读量:0

    单容器跑个 Nginx 或 MySQL 不难,难的是把 Web 应用、数据库、缓存这几类服务组织成一套能一键起停、重启后数据还在、配置不写死在镜像里的组合。Docker Compose 就是干这件事的工具:用一个 YAML 文件描述服务、网络和存储,然后 docker compose up -d 一把拉起。本文按实际部署顺序,把环境变量、数据卷、重启策略这三处最容易踩坑的地方讲透,代码可以直接改成自己的项目。

    一、先把 compose 文件骨架搭起来

    下面这份示例包含三个服务:Nginx 作为 Web 入口、MySQL 作为数据库、Redis 作为缓存。它同时演示了本文要讲的三个要点,请对照后面的小节逐项理解。

    services:
      web:
        image: nginx:1.27-alpine
        ports:
    
    • "8080:80"
    environment: TZ: Asia/Shanghai APP_ENV: ${APP_ENV} env_file:
    • .env
    volumes:
    • ./nginx/conf.d:/etc/nginx/conf.d:ro
    • ./html:/usr/share/nginx/html:ro
    depends_on:
    • db
    • cache
    restart: unless-stopped db: image: mysql:8.4 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} TZ: Asia/Shanghai volumes:
    • db_data:/var/lib/mysql
    • ./mysql/init:/docker-entrypoint-initdb.d:ro
    restart: unless-stopped cache: image: redis:7-alpine command: ["redis-server", "--appendonly", "yes", "--save", ""] volumes:
    • redis_data:/data
    restart: unless-stopped volumes: db_data: redis_data:

    注意两个细节:一是 web 服务没有把配置文件 COPY 进镜像,而是用挂载的方式映射进去,改配置不用重新构建;二是顶层单独声明了 volumes,命名卷必须在这里登记,否则 Compose 会报错。具体字段含义以 Docker 官方 Compose 文件规范为准,不同 Compose 版本关键字略有差异。

    二、环境变量:别把密码写进 YAML 提交到仓库

    环境变量在 Compose 里有三个来源,优先级从低到高大致是:Compose 文件同级目录下自动加载的 .env 文件、env_file 指定的文件、environment 里显式写的键值。理解这个顺序,才能解释「我明明改了 .env 怎么没生效」这类问题。

    推荐的做法是:敏感值(数据库密码、API Key)放独立文件,代码只引用变量名。Compose 文件里写 ${MYSQL_ROOT_PASSWORD},同目录建一个 .env:

    # .env(不要提交到 git,记得写进 .gitignore)
    MYSQL_ROOT_PASSWORD=换成你自己的强密码
    MYSQL_DATABASE=appdb
    APP_ENV=production
    

    启动前先验证变量是否被正确替换,不要等容器起来才发现连不上数据库:

    # 打印 Compose 最终解析出的配置,检查 environment 与 volumes 是否符合预期
    docker compose config
    
    

    只看某个服务的环境变量

    docker compose config --format json | grep -A5 MYSQL

    几个容易忽略的点:.env 里等号两边不要加空格,值含空格或特殊字符时用引号包起来;变量未定义又没给默认值时,Compose 会给出告警并替换为空字符串,可以用 ${VAR:-默认值} 兜底。另外,容器内的环境变量在 docker inspect 里是明文可见的,真正的密钥管理建议配合 Docker secrets 或外部配置中心,别用环境变量存放长期高敏感凭据。

    三、数据卷与重启策略:数据不丢、进程自己爬起来

    容器是无状态的,删掉重建是常态,所以数据库、上传目录这类需要留存的数据必须落到卷上。上例用的是命名卷 db_data,Docker 会把它放在宿主机的数据目录下(默认在 /var/lib/docker/volumes/,具体路径以实际安装方式为准)。相比直接绑宿主机目录,命名卷的好处是权限由 Docker 管理,跨平台一致,也不容易被误删。

    选择哪种卷,可以按这个思路:配置文件、网站源码这类需要直接用编辑器改的,用绑定挂载(./html:/usr/share/nginx/html);数据库数据、Redis 持久化文件这类由程序自己读写的,用命名卷。MySQL 的 /var/lib/mysql 如果绑到宿主机空目录,初始化会因权限失败,新手优先用命名卷。

    重启策略决定容器退出后 Docker 是否自动拉起,常用的几种:no(默认,不重启)、on-failure(非零退出码才重启)、always、unless-stopped(除手动 stop 外都重启)。生产环境一般推荐 unless-stopped:服务器重启后容器自动恢复,而你自己 docker compose stop 掉的不会莫名其妙又起来。

    部署完成后,用这几条命令确认状态:

    # 启动并后台运行
    docker compose up -d
    
    

    查看服务状态与重启次数(RestartCount 持续增长说明进程在反复崩溃)

    docker compose ps docker inspect --format '{{.Name}} {{.RestartCount}}' $(docker compose ps -q)

    查看某个服务日志

    docker compose logs -f --tail=100 db

    restart 只能让容器反复重启,解决不了启动即失败的问题。如果发现某个容器重启次数不断上涨,先看日志里的报错,常见原因有:数据卷权限不对、端口被占用、依赖的数据库还没就绪。depends_on 只保证启动顺序,不代表被依赖服务已经可用,应用侧要有重试或健康检查。需要更严格的就绪判断,可以给数据库加 healthcheck,再让 web 服务通过 depends_on 的条件写法等待其健康。

    最后提醒一句:docker compose down 默认会删除容器和网络但保留命名卷,而 docker compose down -v 会连卷一起删。执行带 -v 的命令前,务必确认数据已经备份,这个参数是新手最容易误操作导致数据全丢的地方。

    把这三个点配置到位,一套多容器应用基本就具备了「重启能自愈、数据能留存、配置可切换」的运维基础。下一步可以在此基础上补充健康检查、资源限制(deploy.resources 或 mem_limit)以及日志轮转配置,让整套服务更贴近生产要求。

    继续阅读

    📑 📅
    Nginx 日志按天切割与过期清理:logrotate 实战 2026-09-15
    Let's Encrypt 证书自动续期失败排查:日志、80端口校验与 reload 钩子 2026-09-13
    Docker 磁盘占用过高清理实战:overlay2、容器日志与悬空镜像 2026-09-12
    Nginx 502/504 排查实战:从 upstream 超时到 php-fpm 进程池 2026-09-12
    MySQL 出现 Too many connections 怎么办:定位、连接池与超时调优 2026-09-11
    MySQL慢查询日志开启与优化入门 2026-09-15
    服务器定时任务实战:crontab 语法与不生效排查 2026-09-15
    Nginx 499 状态码排查实战:客户端断连与 upstream 超时的区别 2026-09-16
    容器内存超限被 OOM Kill 排查:指标、cgroup 与堆参数对应 2026-09-16
    MySQL 主从延迟排查:Seconds_Behind_Master 忽高忽低怎么定位 2026-09-16