个人服务器上的服务越来越多以后,最难处理的通常不是“第一次启动”,而是半年后的升级、迁移和恢复。Docker Compose 的价值,是把端口、环境变量、数据卷、健康检查和启动策略集中记录在一个可审查的配置里。

下面是一份偏保守的最小实践,适合单机上的个人服务。

1. 推荐的目录结构

/opt/myapp/
├── compose.yaml
├── .env
├── data/
└── backups/

compose.yaml 可以进入私有代码仓库;.env 和业务数据不要提交。

2. 一份可维护的 Compose 配置

services:
  app:
    image: ghcr.io/example/myapp:1.2.3
    container_name: myapp
    restart: unless-stopped

    ports:
      - "127.0.0.1:3000:3000"

    env_file:
      - .env

    volumes:
      - ./data:/app/data

    read_only: true
    tmpfs:
      - /tmp

    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s

这里有几个关键选择:

  • 使用明确版本 1.2.3,避免 latest 在重建时悄悄改变行为;
  • 只绑定 127.0.0.1,公网访问交给 Nginx;
  • 数据放在宿主机明确目录,便于备份和迁移;
  • 根文件系统只读,临时文件写入 tmpfs;
  • 健康检查验证应用,而不是只看容器进程是否存在。

如果镜像内没有 wget,可以根据实际情况改用 curl,或者使用应用自带的健康检查命令。

3. 管理环境变量

.env 示例:

APP_ENV=production
TZ=Asia/Shanghai
DATABASE_URL=sqlite:////app/data/app.db

包含密码和令牌的 .env 应限制权限:

chmod 600 .env

不要把密钥直接写进 compose.yaml,也不要把真实 .env 粘贴到工单、聊天或公开仓库。

4. 首次启动与检查

docker compose config
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 app

docker compose config 会展开变量并验证配置结构。注意它可能输出敏感环境变量,不要把完整结果复制到公开日志。

5. 与 Nginx 配合

容器端口只监听本机后,Nginx 可以这样代理:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

这样只有 Nginx 暴露 80/443,应用容器不直接面对公网扫描。

6. 安全升级流程

不要直接执行不受控的全量更新。更稳妥的顺序是:

  1. 阅读目标版本的变更说明;
  2. 备份数据和当前 compose.yaml;
  3. 修改为明确的新版本标签;
  4. 拉取镜像并重建;
  5. 检查健康状态、日志和关键页面。
cp compose.yaml compose.yaml.bak
tar -czf backups/data-$(date +%F-%H%M).tar.gz data

docker compose pull
docker compose up -d
docker compose ps
docker compose logs --since=10m app

7. 如何回滚

如果新版本异常:

cp compose.yaml.bak compose.yaml
docker compose pull
docker compose up -d

如果升级同时修改了数据格式,只回滚镜像可能不够,因此数据库或数据目录备份必须与升级前版本对应。

8. 日常巡检

docker compose ps
docker compose logs --since=24h app
docker system df

不要随意使用 docker system prune -a。它可能清除仍有回滚价值的旧镜像;清理前应先检查对象和磁盘占用。

总结

一份好的 Compose 配置应该回答四个问题:服务如何启动、数据在哪里、故障如何发现、升级失败怎样回退。能在新服务器上仅凭配置和备份恢复服务,才算真正把部署过程管理起来。