WEBKT

5人小团队实战:用 Docker Compose 管好开发、测试、生产三套环境

4 0 0 0

创业初期就我们几个开发,没钱买 GitLab CI 服务器,也不想折腾 Jenkins,每次改完代码手敲命令部署,一不小心就在生产环境翻车。直到用了 Docker Compose + 环境分层的思路,才把这事管明白。

小团队的真正痛点是什么?

不是工具不够用,是流程太重。大厂那套 CI/CD Pipeline 对五个人来说完全是杀鸡用牛刀。我们要的是:改一行代码 → 两分钟搞定所有环境更新

三层架构:一主多用的设计思路

核心思路很简单:docker-compose.yml 只写公共部分,环境差异全部抽离出来。

your-project/
├── docker-compose.base.yml    # 所有环境的共同配置
├── docker-compose.dev.yml     # 开发专用覆盖层
├── docker-compose.prod.yml    # 生产专用覆盖层
├── .env                       # 默认变量(gitignore)
├── .env.dev                   # 开发环境变量(gitignore)
└── .env.prod                  # 生产环境变量(gitignore)

第一层:公共配置 base

# docker-compose.base.yml
version: '3.8'

services:
  app:
    build: .
    restart: unless-stopped
    
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    
volumes:
  postgres_data:

第二层:开发环境的差异化

# docker-compose.dev.yml (开发热重载模式)
services:
  app:
    command: npm run dev          # 热重载启动命令
    ports:
      - "3000:3000"
      - "9229:9229"               # 开个调试端口方便 node --inspect 连上去断点调试
  
  db:
    environment:
      POSTGRES_DB: myapp_dev      # 每个开发者本地独立数据库名,不会互相污染
  
networks:
  default:
    name: myapp_dev_network      # 本地网络隔离,防止端口冲突影响其他项目

# env_file 自动加载,不需要手动指定,compose 会识别同名的 .env.dev 或通过 -f 指定时自动关联行为,但注意需要显式指定 env_file 参数才能生效,这里我们通过命令行参数来处理,见下方完整示例。

第三层:生产环境的加固

# docker-compose.prod.yml (生产安全模式)
services:
  app:
    command: npm start            # 生产直接跑编译好的产物,不开热重载省资源
    
volumes:
  postgres_data:

# 生产不暴露多余端口,外网流量统一走 nginx 反向代理,数据库不对外暴露连接,只允许内网访问。

命令行入口:一个脚本解决所有问题

每次 docker compose up 带一堆参数太容易出错,写个简单的 shell 别名或者脚本更实在:

#!/bin/bash
ENV=${1:-dev}

case $ENV in
  dev)
    COMPOSE_FILES="-f docker-compose.base.yml -f docker-compose.dev.yml"
    ENV_FILE=".env.dev"
    ;;
  prod)
    COMPOSE_FILES="-f docker-compose.base.yml -f docker-compose.prod.yml"
    ENV_FILE=".env.prod"
    ;;
esac

echo "==> Loading environment: $ENV"

docker compose $COMPOSE_FILES --env-file $ENV_FILE "$@"

保存为 ./dc,加上执行权限,之后的操作就变成了:

./dc dev up           # 起开发环境,带热重载和调试端口  
./dc prod up -d       # 后台起生产服务  
./dc prod logs -f     # 看生产日志实时滚动  
./dc dev down         # 关掉开发服务  

数据库迁移的注意事项

很多新手卡在这个环节——每次重新 up 数据库数据就丢了。在 docker-compose.base.yml 里加 volumes 就解决了:

db:  
  image: postgres:15-alpine  
  volumes:  
    - postgres_data:/var/lib/postgresql/data  

volumes:  
  postgres_data:

# 但注意!如果想每次全新干净的环境,跑这个清理旧卷:
docker volume rm your-project_postgres_data   # 卷名字是 项目名_卷名 的格式,可以用 `docker volume ls` 查看实际名字。

一键初始化脚本的完整形态

给新来的同事看的,让他 clone 代码后只需要跑一条命令就能干活:

#!/bin/bash  
set -e  

echo "==> Initializing development environment..."  

if [ ! -f .env ]; then cp .env.example .env; fi  

echo "==> Pulling latest images..." && ./dc pull || true  

echo "==> Starting services..." && ./dc dev up --build  

echo ""  
echo "✅ Done! App should be running at http://localhost:3000"   

首次 clone 的同事只要保证机器上装了 Docker Desktop,点两下就跑起来了。不用装 Node、不用装 Postgres、不用配各种全局依赖。

我们踩过的坑总结

问题一:macOS 上 Docker Desktop 的文件挂载性能差得要命,开发时 npm install 卡死。
解法:在 Dockerfile 里预先安装依赖,本地只挂源代码目录,或者干脆把 node_modules 也做 volume,但更推荐的做法是在容器内完成构建后同步回本地,或者接受第一次慢后面快的缓存逻辑。

问题二:Windows 和 macOS 上换行符不一致导致 shell script 执行报错。
解法:克隆仓库时强制使用 LF:git config --global core.autocrlf input,或者 Windows 用户装 WSL2 把项目放 Ubuntu 子系统里跑,体验接近原生 Linux。

问题三:多人同时修改 .env.prod 导致 git merge 一团糟。
解法.env.* 文件全部加进 .gitignore,每个人本地自己维护一份副本,通过密码管理器或者私聊传递敏感信息。更正规的做法是用 sops/gopass secrets management 管理密钥,但这对五人小队来说已经属于过度工程化了,根据实际情况来。

这套方案的适用边界在哪里?

如果你的项目变成十几二十个人,或者出现了多个微服务需要相互调用,那这套文件的堆叠方式就会变得难以维护,建议升级到 Kubernetes 或者 Nomad。但在那之前,这套东西足够让你撑过 MVP 到产品 PMF 这个阶段了,省下来的精力用来写业务代码它不香吗?

阿明 多环境部署小团队DevOps

评论点评