← 返回博客首页

Docker Compose 入门教程:服务编排实战

为什么需要 Docker Compose?

当你的应用只有单个容器时,用 docker run 命令就够了。但真实项目通常是多组件的:

  • 一个 Nginx 前端
  • 一个 Node.js / Python / Java 后端 API
  • 一个 PostgreSQL / MySQL 数据库
  • 一个 Redis 缓存

手动用 docker run 管理这些容器会变成噩梦:端口映射、数据卷挂载、网络互联、环境变量、启动顺序……每条命令几十个参数,还无法版本化。

Docker Compose 用一个声明式的 YAML 文件描述整个多容器应用栈,一条命令即可启动、停止、重建所有服务。它是本地开发、CI 流水线、小型生产部署的标配工具。

安装与版本

Docker Desktop(Windows / macOS)已内置 Compose。Linux 需单独安装 docker-compose-plugin,用 docker compose(V2,推荐)而非 docker-compose(V1,已停止维护)。

# 验证安装
docker compose version
# Docker Compose version v2.27.0

核心概念

服务(Services)

每个 service 定义一个容器。最简单的例子:

# docker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html

网络(Networks)

Compose 默认会为每个项目创建一个网络,同一 docker-compose.yml 中的服务可用服务名互相访问,无需知道对方 IP:

services:
  web:
    image: nginx:alpine
    depends_on:
      - api
  api:
    image: node:20-alpine
    # web 容器内可访问 http://api:3000

也可自定义网络以做隔离:

services:
  web:
    networks:
      - frontend
  api:
    networks:
      - frontend
      - backend
  db:
    networks:
      - backend

networks:
  frontend:
  backend:

此时 web 无法直接访问 db,因为它们不在同一网络。

数据卷(Volumes)

容器是临时的,删除后数据消失。持久化数据用 volumes:

services:
  db:
    image: postgres:16
    volumes:
      # 命名卷:由 Docker 管理,删除容器不丢数据
      - pgdata:/var/lib/postgresql/data
      # 绑定挂载:直接映射宿主机路径,适合开发时热加载代码
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
  pgdata:

两种挂载区别

类型 语法 用途
命名卷 name:/path/in/container 持久化数据库等运行时数据
绑定挂载 ./host/path:/container/path 开发时同步源代码、配置文件

环境变量(Environment)

三种方式配置环境变量:

services:
  api:
    image: myapp:latest
    environment:
      - NODE_ENV=production
      - LOG_LEVEL=info
    # 或用 map 形式
    env_file:
      - .env
    # 文件中:KEY=value,每行一个

推荐用 .env 文件,且加入 .gitignore 避免泄漏密钥。Compose 也会自动读取项目根目录的 .env 文件用于变量插值:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}  # 从 .env 读取

.env 文件:

DB_PASSWORD=supersecret

实战:典型 Web 应用栈

下面是一个生产级 Web 应用(Nginx + Node API + PostgreSQL + Redis)的完整配置:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - api
    restart: unless-stopped

  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/app
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redisdata:/data
    restart: unless-stopped

volumes:
  pgdata:
  redisdata:

常用命令速查

# 启动所有服务(后台)
docker compose up -d

# 查看运行状态
docker compose ps

# 查看日志(实时跟踪)
docker compose logs -f api

# 重新构建并启动(代码变更后)
docker compose up -d --build

# 仅重启某个服务
docker compose restart api

# 进入容器
docker compose exec api sh

# 停止并删除容器、网络(保留数据卷)
docker compose down

# 停止并删除包括数据卷(慎用!数据库数据会丢失)
docker compose down -v

# 查看资源占用
docker compose stats

常见模式与最佳实践

1. 开发环境热加载

挂载源码并启用文件监听:

services:
  api:
    build: ./backend
    volumes:
      - ./backend:/app
      - /app/node_modules  # 匿名卷避免宿主覆盖
    command: npm run dev
    environment:
      - CHOKIDAR_USEPOLLING=true  # 解决 Docker Desktop for Windows 文件监听问题

2. 多环境配置

用多个 compose 文件叠加:

# 基础 + 开发覆盖
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

# 基础 + 生产覆盖
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

docker-compose.dev.yml 示例(覆盖命令、暴露调试端口):

services:
  api:
    command: npm run dev
    ports:
      - "9229:9229"  # Node.js 调试端口
    environment:
      - NODE_ENV=development

3. 健康检查与启动顺序

depends_on 默认只保证容器启动,不保证服务就绪。用 healthcheck + condition

services:
  api:
    depends_on:
      db:
        condition: service_healthy

这避免了"API 启动时数据库还没准备好"的经典坑。

4. 资源限制

生产环境限制 CPU 和内存,防止单个容器拖垮宿主机:

services:
  api:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          memory: 256M

5. 日志轮转

避免日志撑爆磁盘:

services:
  api:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

常见坑

.env 文件不被读取

  • 必须放在执行 docker compose 命令的目录,而非 compose 文件所在目录
  • 变量插值 ${VAR} 在 YAML 解析时替换,与容器内 environment 注入是两回事
  • 调试用 docker compose config 查看最终合并后的完整配置

绑定挂载权限问题(Linux)

宿主机文件挂载到容器后,容器内进程 UID 与宿主机不一致会导致写权限错误。两种解法:

# 方案一:构建镜像时切换到非 root 用户并匹配 UID
services:
  api:
    user: "${UID}:${GID}"

# 方案二:挂载后用 init 容器 chown

端口占用

docker compose upport is already allocated,但 docker ps 看不到占用容器:

# 检查宿主机端口占用
# Windows
netstat -ano | findstr :8080

# Linux/Mac
lsof -i :8080

可能是其他程序占用,改用其他端口即可。

Windows 下文件挂载性能差

Docker Desktop for Windows 使用 WSL2 后端时,挂载 Windows 磁盘(/c/...)到容器有较大 IO 损耗。建议把项目放在 WSL2 文件系统内(\\wsl$\Ubuntu\home\...),性能可提升 5-10 倍。

Compose vs Swarm vs Kubernetes

场景 工具
单机开发 / 小型部署 Docker Compose ✅
多机集群 / 高可用 Docker Swarm(轻量)或 Kubernetes(生态完善)
企业级生产编排 Kubernetes

Compose 的 docker-compose.yml 可直接被 Docker Swarm 部署(docker stack deploy),但 K8s 需要转换为 Kubernetes manifests(可用 Kompose 工具辅助)。

总结

Docker Compose 把多容器应用从命令行脚本升级为可版本化、可复现的声明式配置。掌握 servicesvolumesnetworksenvironment 四大核心概念,配合健康检查、多环境覆盖、资源限制等模式,足以覆盖绝大多数本地开发和小型生产场景。当规模扩大到多节点时,再平滑迁移到 Kubernetes。

要点 说明
services 定义每个容器,包括镜像、端口、依赖
volumes 命名卷持久化数据,绑定挂载同步代码
networks 默认按服务名互通,自定义网络做隔离
environment .env 文件 + 变量插值是最佳实践
depends_on + healthcheck 保证启动顺序与服务就绪
多文件覆盖 dev.yml / prod.yml 分环境配置
← 返回博客首页