docker compose up -d 只能说明容器已经启动。生产部署还要固定镜像、保存数据、隔离端口、限制资源,并验证容器和主机重启后的恢复结果。
Compose 适合什么样的生产服务
单台主机、服务数量不多、依赖关系清楚时,Compose 足以承担部署:
- 应用和数据库主要运行在一台服务器;
- 主机故障时允许人工或自动化脚本在另一台机器恢复;
- 容量可以通过单机扩容解决;
- 日志、监控和备份有明确去处。
已经需要多节点调度、跨可用区容错、自动扩缩容或大规模滚动更新时,应选择 Kubernetes、Nomad 或云平台的编排服务,而不是继续把所有能力塞进一个 Compose 文件。
Docker 官方生产环境说明(在新标签页打开)建议把开发环境中的源码挂载、调试端口和开发参数从生产配置中移除,并为生产环境设置独立的变量、重启策略和日志方式。
部署参数、应用配置和密钥分开保存
/opt/webapp/
├── compose.yaml
├── .env
├── app.env
└── secrets/
└── app_secret
.env 保存 Compose 展开配置时使用的镜像、端口和目录参数;app.env 保存需要注入容器的日志级别、公开 URL 和功能参数。两者都不放密码。密码、私钥和访问令牌放进 secrets/,权限只给实际需要读取的账户。
secrets 只把宿主文件以只读方式挂进容器,不负责加密、轮换或审计。需要这些能力时,使用专用密钥管理服务。
sudo install -d -m 700 -o root -g root /opt/webapp/secrets
sudo install -m 600 -o root -g root /dev/null /opt/webapp/secrets/app_secret
sudoedit /opt/webapp/secrets/app_secret
一份单机 Web 应用的 Compose 配置
name: ${COMPOSE_PROJECT_NAME:-webapp}
services:
app:
image: ${APP_IMAGE:?APP_IMAGE 必须固定为版本加 digest}
init: true
restart: unless-stopped
ports:
- "127.0.0.1:${APP_HOST_PORT:-8080}:${APP_CONTAINER_PORT:-8080}"
env_file:
- ./app.env
environment:
APP_SECRET_FILE: /run/secrets/app_secret
secrets:
- app_secret
volumes:
- app_data:${APP_DATA_DIR:-/var/lib/webapp}
networks:
- edge
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
cpus: "1.00"
mem_limit: 1g
security_opt:
- no-new-privileges:true
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
secrets:
app_secret:
file: ./secrets/app_secret
volumes:
app_data:
networks:
edge:
driver: bridge
镜像内的端口、数据目录和健康检查命令必须换成应用真实提供的值。如果镜像内没有 /usr/local/bin/healthcheck,健康检查会直接失败。
镜像不要只写 latest
可变 tag 可能在两次部署之间指向不同内容。生产配置应保存完整引用:
COMPOSE_PROJECT_NAME=webapp
APP_IMAGE=registry.example.com/webapp:2026.09.02@sha256:完整摘要
APP_HOST_PORT=8080
APP_CONTAINER_PORT=8080
APP_DATA_DIR=/var/lib/webapp
tag 方便阅读,digest 用来锁定实际制品。回滚配置还要保留上一版 digest,才能重新拉取同一制品。
docker compose --env-file .env config
docker compose --env-file .env pull
docker compose --env-file .env images
应用端口不要直接暴露到公网
反向代理和应用在同一主机时,把应用绑定到回环地址:
ports:
- "127.0.0.1:8080:8080"
代理也在同一个 Compose 网络中时,可以完全不发布宿主端口:
expose:
- "8080"
expose 不是防火墙。还要同时检查 Docker 端口映射、主机防火墙和云安全组:
docker compose ps
docker port webapp-app-1
sudo ss -lntp
重要数据必须离开容器可写层
容器删除后,它的可写层也会消失。数据库、上传文件和应用状态要放到 named volume 或明确的 bind mount。
named volume 由 Docker 管理实际路径;bind mount 的宿主路径更直观,但目录权限、UID 和 GID 需要自己维护。两者都不能代替备份。
确认数据不会随重建丢失,可以在隔离环境执行一次完整过程:
- 写入一条容易识别的数据;
- 备份数据和恢复所需配置;
- 执行
docker compose up -d --force-recreate; - 确认应用仍能读取原数据;
- 再把备份恢复到另一个空目录或空实例。
只看到备份文件存在,不足以证明它能够恢复。
healthcheck 不会触发 restart
healthcheck 负责告诉 Docker“应用现在是否健康”。它应该访问应用的就绪端点,或者执行镜像内确实存在的检查命令。
restart: unless-stopped 只处理容器进程退出和 Docker 守护进程重启。应用变成 unhealthy,但主进程仍在运行时,Docker 不会因为健康状态自动重启它。
持续 unhealthy 应由监控告警。应用本身则要为数据库和外部 API 设置超时、重连和有限重试,不能依靠无限重启掩盖故障。
depends_on 只负责启动顺序
应用依赖数据库时,可以等待数据库先进入健康状态:
services:
app:
depends_on:
database:
condition: service_healthy
restart: true
Docker 官方启动顺序说明(在新标签页打开)区分了 service_started、service_healthy 和 service_completed_successfully。
这项配置只解决启动时机。运行过程中数据库仍可能重启或断网,应用自己的连接恢复逻辑不能省略。
设置资源上限和日志轮转
cpus: "1.00"
mem_limit: 1g
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
CPU 和内存数值要根据实际负载调整,同时给操作系统、Docker、反向代理和突发流量留出余量。日志轮转只能防止本机文件无限增长;需要检索和告警时,仍要把结构化日志送到集中系统。
启动后检查配置、容器、健康端点和日志
# Compose 最终渲染出的内容
docker compose --env-file .env config --quiet
# 容器是否运行、端口是否符合预期
docker compose ps
# 应用是否可以响应
curl -fsS http://127.0.0.1:8080/healthz
# 启动阶段有没有报错
docker compose logs --tail 100 app
再查看镜像、重启策略、健康状态和挂载:
app_container_id="$(docker compose ps -q app)"
docker inspect "$app_container_id" --format 'ref={{.Config.Image}} id={{.Image}}'
docker inspect "$app_container_id" --format '{{json .State.Health}}'
docker inspect "$app_container_id" --format '{{.HostConfig.RestartPolicy.Name}}'
docker inspect "$app_container_id" --format '{{json .Mounts}}'
如果应用没有 /healthz,不要把示例路径硬抄进去。应先为应用提供一个轻量、无副作用的就绪端点,再让反向代理和监控使用它。
上线前验证容器重建、主机重启和备份恢复
在隔离环境分别验证:
- 强制重建应用容器,确认镜像引用、挂载、secret 和健康状态不变;
- 重启 Docker 和主机,确认服务能恢复,应用端口也没有意外暴露;
- 从备份恢复到空卷或空目录,确认当前镜像可以读取恢复后的数据。
常见问题
Docker Compose 适合生产环境吗?
适合边界清楚的单机或小规模服务。镜像固定、数据持久化、日志轮转、备份恢复和外部监控都要成为部署的一部分。
restart 会重启 unhealthy 容器吗?
不会。restart 策略处理容器进程退出和 Docker 重启;healthcheck 只记录健康状态。
depends_on 能保证数据库一直可用吗?
不能。service_healthy 主要控制初始启动顺序。应用仍要处理运行中的断连、超时和重连。
生产镜像固定 tag 还是 digest?
用 digest 保证不可变,同时保留版本 tag 方便阅读。配置、部署记录和实际容器应指向同一个 digest。
密码放在 env_file 里安全吗?
普通 env 文件仍是明文,而且环境变量更容易出现在诊断信息中。支持 _FILE 或文件参数的应用,应从只读 secret file 获取密码,并限制宿主文件权限。
应用稳定运行后,再把 HTTPS 和域名入口接进来;三种常见选择见 Caddy、Nginx 和 Nginx Proxy Manager 怎么选。