docker compose up -d 回车,容器状态 Running,浏览器一开页面也出来了——很多人到此就宣布“部署完成”。但开发机上能跑和公网上能稳定跑,中间差着五件事:入口暴露、数据持久化、凭据管理、日志与重启、升级回滚。漏掉任何一件,出问题时你都会在半夜重新学一遍。下面这份清单,就是上线前要逐项打勾的五件事。
第一件:只公开真正需要的入口
生产环境的标准姿势是:反向代理(Nginx / Caddy / Traefik)在前面接收公网请求,应用和数据库待在内部网络里。Compose 里做到这点很简单——需要公开的服务才写 ports,不需要公开的不要写,同一网络内的服务本来就能用服务名互相访问:
services:
web:
image: myapp:1.4.2
ports:
- "127.0.0.1:8080:80" # 只绑本机,再由反向代理转发
db:
image: postgres:16
# 没有 ports:外部连不进来,但 web 仍可通过 db:5432 访问
发布端口前,先确认宿主机端口没被占用:ss -tlnp | grep 8080。宿主机端口和容器内部端口不必相同,改宿主机那一侧就能避开冲突。
还有一个容易被忽视的点:Docker 发布出去的端口,默认不受系统防火墙(ufw)那套规则约束——这是 Docker 网络模型的行为,不是 bug。所以“数据库不写 ports”才是真正的安全边界。核验的唯一标准是 ss -tlnp 看到的实际监听情况,而不是防火墙界面长什么样。
第二件:数据要持久化,凭据不能进仓库
数据库文件、上传目录必须挂载数据卷,否则容器一重建数据就没了。镜像、容器和持久化数据是三样东西,别混为一谈:
services:
db:
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
密码、密钥不要写进 compose 文件,更不要提交到公开仓库。用 env_file 或环境变量引用:
services:
db:
env_file:
- .env
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
chmod 600 .env # 仅自己可读,且不加入版本库
镜像一律写明确的 tag(postgres:16 而不是 postgres:latest),并记下每次变更:改了哪个服务的版本、为什么改。latest 的意思是“每次 pull 都可能变”,出兼容性问题时你连对照的基线都说不清。
第三件:重启策略和日志上限,别等磁盘满了才想起来
services:
web:
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
重启策略按业务选:unless-stopped 适合长期运行的服务;on-failure 适合跑完就退出的任务型容器。日志不设上限的话,json-file 会一直吃磁盘,半夜被告警叫醒时你就懂了。平时定期看两眼:docker compose logs --tail=100 web 看应用日志,docker system df 看磁盘占用。
第四件:生产差异用覆盖文件,别改同一份 compose
开发和生产共用一份文件,早晚会有人把调试端口带上生产。推荐做法:基础 compose.yml 放公共部分,生产差异单独放 compose.prod.yml,启动时合并:
docker compose -f compose.yml -f compose.prod.yml up -d
改完先跑 docker compose -f compose.yml -f compose.prod.yml config 看合并后的实际配置,确认无误再 up。这条命令是免费的“上线前彩排”,能发现九成手误。
第五件:升级前备份,升级后做一次真实验收
把升级流程固定下来,每次都走同一套动作:
# 1. 先备份(数据卷打包,或用专业备份工具) # 2. 拉新镜像并启动该服务 docker compose pull web docker compose up -d web # 3. 看状态和日志 docker compose ps docker compose logs --tail=50 web
注意:容器状态 Running 不等于业务正常。数据库迁移可能失败了,应用可能在报 500。验收至少做一次真实请求:打开网站走一遍核心流程,或者请求健康检查端点:
curl -s -o /dev/null -w "%{http_code}\n" https://你的域名/health
升级失败要有回退方案:因为镜像版本是写死的,只要把 compose 指回旧 tag 再 up -d 就能回退——前提是第二件里的“版本写死”你做到了。
写一张上线验收卡
每次上线前回答这五个问题,答不上来就别 up:① 公网只能访问到反向代理吗?② 删掉容器重建,数据还在吗?③ 凭据文件权限是 600 且没进仓库吗?④ 日志有上限、重启策略设了吗?⑤ 升级失败时,我知道怎么回退吗?把答案和变更记下来,这就是你的上线记录,比收藏一条启动命令有用得多。
这张卡还有一个好处:当你以后要把运维交接给别人,或者半夜被叫醒处理故障时,它能让你在五分钟内回答“这个服务是怎么部署的”。很多小团队的“祖传服务没人敢动”,根因就是当初上线时只记住了 up 命令,没留下任何验收记录。从第一次上线就养成这个习惯,成本几乎为零。
常见坑
把数据库端口 publish 出来“方便调试”。方便了你,也方便了全网的扫描器。调试用 docker compose exec db sh 进容器里做。
用 latest 标签上线。某天 pull 到大版本升级,迁移脚本直接报错,而你甚至不知道上次跑的是哪个版本。
容器 Running 就等于没事。健康检查和一次真实业务请求才是验收标准,状态灯只是参考。
备份了卷目录就以为万事大吉。运行中的数据库要按官方方法导出后再备份,直接拷贝数据文件可能得到一份恢复时打不开的备份。
常见问题
compose.yml 和 docker-compose.yml 用哪个?新版 Compose 优先识别 compose.yml,老名字也兼容。新项目建议用 compose.yml。
改了 compose 文件,是 restart 还是 up?多数场景 docker compose up -d 就够了,它会按新配置重建有变化的容器;只有改了构建上下文才需要加 --build。
怎么确认多文件合并后的最终配置?docker compose config,用 -f 指定多个文件时它会显示合并结果,up 之前看一眼。
down 和 stop 有什么区别?stop 只停容器,网络和卷都在;down 还会删掉容器和网络(数据卷默认保留)。生产环境别手滑 down 掉还没备份的栈。
参考资料
资料核查:2026年10月8日。本文根据公开官方资料独立整理撰写;封面为 AI 生成的概念插画,不是产品实拍。



