2026 年还能用 Docker Compose 跑生产环境吗?实操指南与避坑大全
发布: 2026-05-05 • 阅读: 12 分钟 • 标签: Docker, Docker Compose, DevOps, 生产部署, Kubernetes一个老问题的新答案
2026 年问"能不能用 Docker Compose 跑生产" 就像问 "能不能用摩托车跑高速"
答案取决于你的场景 如果你的业务是单节点部署 — 给客户分发一套自托管应用、跑一个内部工具、部署边缘节点 — Docker Compose 完全没问题 事实上很多公司在用 而且运行得很稳
但关键在于 Docker Compose 本身只是一个声明式容器编排工具 它不会帮你处理运维层面的问题 你需要自己填上那些缺口
这篇文章整理了 7 个最常见的 Docker Compose 生产坑和对应的解决方案 每个方案都配有实际可用的 docker-compose.yml 片段和命令
坑一:Orphan 容器 —— 删除服务后容器还在跑
从 docker-compose.yml 里删掉一个服务 跑 docker compose up -d 后旧的容器并不会自动停止 它变成了 "orphan"(孤儿容器) 仍然占用端口和内存 而 docker compose ps 不会显示它
半年后你可能会发现一个早已删除的工作进程在悄悄消耗资源
# 正确的启动方式
docker compose up -d --remove-orphans
# 停止时也清理
docker compose down --remove-orphans
建议把这个 flag 写入你的部署脚本或 CI/CD pipeline 的每一步 Compose 操作中 另外可以用一个定时任务做定期清理
# 每天凌晨清理所有项目的 orphan 容器
0 3 * * * docker ps -q --filter "status=exited" | xargs -r docker rm
注意 named volume 不会被 --remove-orphans 删除 需要手动清理 dangling volume
docker volume ls --filter dangling=true
docker volume prune
坑二:健康检查只检测不处理
Docker Compose 支持 healthcheck 配置 但它默认只告诉你容器是否健康 不会自动重启不健康的容器
很多人的配置是这样的
services:
web:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
问题在于如果健康检查连续失败 3 次 容器被标记为 unhealthy 但它仍然在运行 Compose 什么也不会做
解决方案是用 restart: always 配合 Docker 引擎的自动重启策略 但这只对容器崩溃有效 对健康检查失败无效
真正的解法
services:
web:
image: myapp:latest
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 用一个 sidecar 容器做自动修复
health-watcher:
image: docker:27
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: >
sh -c "while true; do
if [ \"$(docker inspect --format='{{json .State.Health.Status}}' web_1)\" = '\"unhealthy\"' ]; then
docker restart web_1;
fi;
sleep 60;
done"
或者更简单的方式:在宿主机上配置 systemd timer + Docker API 检查 或者使用 Distr 等专门的 Docker 运维 agent 来自动处理
坑三::latest 标签 —— 镜像漂移
用 :latest 标签部署 某天重新 pull 后获取了一个不兼容的新版本 导致服务崩溃
这是生产环境中最常见也最容易避免的问题
# ❌ 不推荐
image: myapp:latest
# ✅ 推荐:使用具体版本
image: myapp:2.5.1
# ✅ 也可以用 SHA256 摘要(最精确)
image: myapp@sha256:a1b2c3d4e5f6...
在 CI/CD 中构建镜像时 确保同时打上版本标签和 :latest 但在生产部署时只用版本标签
# GitHub Actions 示例
- name: Build and push
run: |
docker build -t registry.example.com/myapp:${{ github.sha }} .
docker tag registry.example.com/myapp:${{ github.sha }} registry.example.com/myapp:latest
docker push --all-tags registry.example.com/myapp
部署时使用明确的 commit SHA 或语义化版本 这样回滚时也知道精确的版本号
坑四:磁盘写满 —— Compose 不会帮你清理
Docker Compose 不管理磁盘空间 日志文件、旧镜像、匿名 volume 会无限增长直到磁盘写满
你需要主动配置 Docker 引擎层面的日志限制和清理策略
# 在 /etc/docker/daemon.json 中配置全局日志限制
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2"
}
# 重启 Docker 生效
sudo systemctl restart docker
在 docker-compose.yml 中也可以为每个服务单独配置
services:
web:
image: myapp:2.5.1
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
定期清理脚本
# 每周日凌晨清理
0 4 * * 0 docker image prune -af
0 4 * * 0 docker builder prune -af
0 5 * * 0 docker volume prune -f
建议设置磁盘告警 在磁盘使用率达到 80% 时通知你
# 简单的磁盘检查脚本(配合 crontab 每 5 分钟跑一次)
#!/bin/bash
THRESHOLD=80
USAGE=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
echo "Disk usage at ${USAGE}% on $(hostname)" | mail -s "Disk Alert" admin@example.com
fi
坑五:Socket 挂载 —— 容器逃逸风险
把 /var/run/docker.sock 挂载进容器(Portainer、Watchtower 等工具需要) 本质上就是把宿主机的 root 权限给了容器 如果该容器被攻破 攻击者可以直接控制宿主机 Docker 守护进程
这是一个严重的安全隐患 但很多人为了便利而忽略了
缓解方案
第一 如果可能 用 Docker API over TCP + TLS 替代 socket 挂载
第二 使用 docker:socket-proxy 镜像做一个只读的 socket 代理 只暴露必要的 API 端点
services:
socket-proxy:
image: tecnativa/docker-socket-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
CONTAINERS: 1 # 只允许容器相关操作
INFO: 1 # 允许只读 info
POST: 0 # 禁止写操作
NETWORKS: 0
VOLUMES: 0
第三 如果必须挂载 用 :ro(只读)模式 并且最小化容器内的用户权限
坑六:日志没有轮转 —— 单文件撑爆磁盘
默认的 json-file 日志驱动不会自动轮转 一个大流量的 Nginx 容器一天就能写几十 GB 日志
前面我们已经配置了全局日志限制 但还有一个更容易被忽略的问题:Compose 使用的 docker compose logs 命令默认不会轮转日志文件
推荐切换到 local 日志驱动 它自带轮转且性能更好
# daemon.json
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
或者使用 journald 驱动 把容器日志纳入 systemd journal 统一管理
services:
nginx:
image: nginx:1.26
logging:
driver: journald
options:
tag: "nginx-{{.Name}}"
这样可以用 journalctl CONTAINER_NAME=nginx-1 查看日志 也支持自动轮转
坑七:没有备份和恢复策略
Docker Compose 不提供数据备份功能 生产环境的数据库(PostgreSQL、MySQL、Redis)必须要有自动备份方案
# docker-compose.yml 中的数据库备份 sidecar
services:
postgres:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
- ./backup:/backup
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
pg-backup:
image: postgres:16
volumes:
- ./backup:/backup
entrypoint: |
sh -c '
while true; do
sleep 86400;
PGPASSWORD=${DB_PASSWORD} pg_dump -h postgres -U postgres mydb > /backup/db_$(date +%Y%m%d).sql;
find /backup -name "*.sql" -mtime +7 -delete;
done'
depends_on:
postgres:
condition: service_healthy
对于重要数据 建议同时备份到远程存储(S3、B2 等)
# 用 s3cmd 上传到 S3 兼容存储
#!/bin/bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
docker exec postgres pg_dump -U postgres mydb > /tmp/backup_$TIMESTAMP.sql
gzip /tmp/backup_$TIMESTAMP.sql
s3cmd put /tmp/backup_$TIMESTAMP.sql.gz s3://my-backups/db/
rm /tmp/backup_$TIMESTAMP.sql.gz
测试恢复流程和备份本身一样重要 每个月至少做一次恢复演练
生产级 Docker Compose 模板
结合以上经验 这里提供一个可直接投入生产的 docker-compose.yml 模板
version: "3.9"
services:
app:
image: registry.example.com/myapp:${APP_VERSION:-latest}
restart: unless-stopped
ports:
- "443:443"
volumes:
- app-data:/data
env_file: .env.production
logging:
driver: "local"
options:
max-size: "10m"
max-file: "5"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: "2"
memory: "2G"
reservations:
cpus: "0.5"
memory: "512M"
networks:
- internal
- public
postgres:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
env_file: .env.production
logging:
driver: "local"
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
cpus: "1"
memory: "1G"
networks:
- internal
nginx:
image: nginx:1.26-alpine
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
logging:
driver: "journald"
options:
tag: "nginx-{{.Name}}"
depends_on:
app:
condition: service_healthy
networks:
- public
volumes:
app-data:
driver: local
pgdata:
driver: local
networks:
internal:
driver: bridge
internal: true
public:
driver: bridge
这个模板涵盖了:资源限制、健康检查、日志轮转、网络隔离(数据库放在 internal 网络)、卷持久化
什么时候该升级到 Kubernetes
Docker Compose 最合适的场景是单节点部署 当出现以下信号时 可以考虑迁移到 Kubernetes
- 需要多节点高可用(单个宿主机挂了服务就不可用)
- 需要自动扩缩容(流量波动大 手动调整太慢)
- 需要更细粒度的资源调度和隔离
- 需要声明式的滚动更新和自动回滚
- 团队规模增长 需要标准化的编排平台
但注意 Kubernetes 本身也引入了新的复杂度 如果你的团队没有专职的 SRE 或 DevOps 工程师 维持一个小型 K8s 集群可能比管理 Docker Compose 更痛苦
Docker Swarm 是另一个选项 它在多节点场景下的复杂度介于 Compose 和 K8s 之间 但生态活跃度远不如 K8s
总结
2026 年的答案是:Docker Compose 完全可以跑生产 但你需要为它补上运维层面的功能
核心 checklist
- 始终使用
--remove-orphans - 配置
healthcheck加上自动恢复机制 - 固定镜像版本 不用
:latest - 设置日志轮转和磁盘清理
- 最小化 socket 挂载 优先用只读代理
- 单独配置数据库自动备份
- 设置磁盘使用率告警
- 定期测试恢复流程
Docker Compose 不是最时髦的工具了 但它足够简单、稳定 在合适的场景下它仍然是最好的选择