技术博客

docker-compose 入门:多容器应用编排、网络与服务依赖管理

从零掌握 docker-compose:compose.yml 文件结构与核心字段(services/networks/volumes)、服务间网络通信与 DNS 解析、depends_on 与 healthcheck 组合实现有序启动、常用命令(up/down/logs/exec/scale)详解,以及 WordPress + MySQL、Flask + Redis + Nginx 完整多容器应用编排实战。

Dockerdocker-compose编排多容器进阶服务依赖网络

单个容器能运行一个服务,但真实应用通常由多个服务组成:Web 服务 + 数据库 + 缓存 + 消息队列……用 docker run 手动启动每个容器不仅繁琐,还难以管理依赖关系。docker-compose 用一个 YAML 文件描述整个应用的多容器结构,一条命令启动全部服务。

docker-compose 基础概念

compose.yml(或 docker-compose.yml)描述:
  - 服务(Services):每个容器是一个服务
  - 网络(Networks):服务间如何通信
  - 数据卷(Volumes):数据如何持久化

文件名约定(按优先级):
  compose.yaml          ← 最新推荐(Docker Compose V2)
  compose.yml
  docker-compose.yaml
  docker-compose.yml    ← 旧版本常见

docker-compose vs Docker Compose:
  docker-compose(V1):独立工具,需单独安装(python 写的)
  docker compose(V2):Docker CLI 插件,随 Docker Engine 安装
  推荐用 V2:docker compose up

一、compose.yml 基本结构

# compose.yml 顶层结构
version: "3.9"   # 已废弃,新版本不需要写(自动检测)

services:         # 服务定义(必须)
  web:            # 服务名称(自定义)
    image: nginx  # 使用现有镜像
    # 或:build 指令构建镜像

  db:
    image: mysql:8.0

networks:         # 网络定义(可选,不写会自动创建默认网络)
  frontend:
  backend:

volumes:          # 数据卷定义(可选)
  mysql_data:

二、services 字段详解

services:
  web:
    # 方式1:使用现有镜像
    image: nginx:1.25-alpine

    # 方式2:从 Dockerfile 构建
    build:
      context: ./app            # Dockerfile 所在目录
      dockerfile: Dockerfile.prod
      args:
        APP_VERSION: "1.2.0"

    # 容器名称(不设置则自动生成 project_service_1)
    container_name: web-prod

    # 端口映射
    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:8080:80"    # 绑定特定 IP

    # 环境变量
    environment:
      - NGINX_HOST=example.com
      - NGINX_PORT=80
    # 或从文件读取
    env_file:
      - .env
      - .env.production

    # 数据卷
    volumes:
      - ./html:/usr/share/nginx/html:ro    # bind mount
      - nginx_logs:/var/log/nginx           # named volume
      - type: bind                          # 详细格式
        source: ./nginx.conf
        target: /etc/nginx/nginx.conf
        read_only: true

    # 网络
    networks:
      - frontend

    # 重启策略
    restart: unless-stopped

    # 资源限制
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
        reservations:
          memory: 128M

    # 健康检查
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

    # 日志配置
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

    # 依赖关系
    depends_on:
      db:
        condition: service_healthy    # 等 db 健康后再启动

三、服务间网络通信

# 自定义网络(推荐:不同功能用不同网络)
services:
  nginx:
    networks:
      - frontend
      - backend

  api:
    networks:
      - backend

  db:
    networks:
      - backend

networks:
  frontend:     # nginx ↔ 用户
  backend:      # nginx ↔ api ↔ db(db 不暴露给 frontend)
# 服务间通信用服务名作为主机名
# api 服务内访问 db:
curl http://db:3306
# nginx 服务内访问 api:
curl http://api:8080

# DNS 解析规则:
# 服务名 → 该服务的容器 IP
# 多副本时(scale)→ 轮询负载均衡

四、depends_on 与启动顺序

# 问题:docker-compose up 默认并行启动所有服务
# 如果 api 在 db 准备好之前启动,会报"连接被拒绝"

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-psecret"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 30s

  redis:
    image: redis:7.2
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 5

  api:
    build: .
    depends_on:
      db:
        condition: service_healthy    # 等待 db 健康
      redis:
        condition: service_healthy    # 等待 redis 健康
    # ⚠️ depends_on 只控制启动顺序,不保证服务"完全就绪"
    # 应用代码中仍需实现连接重试逻辑

  nginx:
    image: nginx:alpine
    depends_on:
      api:
        condition: service_started    # 等 api 容器启动(不检查健康状态)
    ports:
      - "80:80"

五、常用 compose 命令

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

# 启动并强制重新构建镜像
docker compose up -d --build

# 只启动特定服务
docker compose up -d db redis

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f           # 所有服务
docker compose logs -f api       # 指定服务
docker compose logs --tail=100 api

# 进入容器
docker compose exec api bash
docker compose exec -u root db bash

# 在容器中执行命令(不进入交互模式)
docker compose exec db mysqladmin -u root -psecret status

# 停止服务(不删除容器和数据)
docker compose stop

# 停止并删除容器(保留 volumes)
docker compose down

# 停止并删除一切(包括 volumes)— 慎用!
docker compose down -v

# 重启特定服务
docker compose restart api

# 查看服务资源使用
docker compose top

# 拉取最新镜像(不重启)
docker compose pull

六、实战1:WordPress + MySQL

# compose.yml
services:
  db:
    image: mysql:8.0
    container_name: wordpress-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass123
      MYSQL_ROOT_PASSWORD: rootsecret
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - wordpress-net
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-u", "wpuser", "-pwppass123"]
      interval: 10s
      retries: 5
      start_period: 30s

  wordpress:
    image: wordpress:6.4-php8.2-apache
    container_name: wordpress-app
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db          # 用服务名!
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass123
    volumes:
      - wp_content:/var/www/html/wp-content
    networks:
      - wordpress-net
    depends_on:
      db:
        condition: service_healthy

volumes:
  mysql_data:
  wp_content:

networks:
  wordpress-net:
docker compose up -d
docker compose ps
# 访问 http://localhost:8080 完成 WordPress 安装

七、实战2:Flask + Redis + Nginx 三层架构

# compose.yml

services:
  redis:
    image: redis:7.2-alpine
    restart: unless-stopped
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 3

  api:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    environment:
      - REDIS_URL=redis://redis:6379   # 使用服务名
      - APP_ENV=production
    env_file:
      - .env
    networks:
      - backend
    depends_on:
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

  nginx:
    image: nginx:1.25-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    networks:
      - frontend
      - backend
    depends_on:
      api:
        condition: service_healthy

networks:
  frontend:
  backend:
# nginx/nginx.conf
events { worker_connections 1024; }

http {
    upstream api_backend {
        server api:5000;              # 使用服务名
    }

    server {
        listen 80;
        
        location / {
            proxy_pass http://api_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

八、compose.yml 中的变量替换

# .env 文件(自动被 docker compose 加载)
APP_VERSION=1.2.0
DB_PASSWORD=secret123
NGINX_PORT=80
# compose.yml 中使用变量
services:
  api:
    image: myapp:${APP_VERSION}         # 从 .env 读取
    environment:
      DB_PASSWORD: ${DB_PASSWORD}

  nginx:
    ports:
      - "${NGINX_PORT:-80}:80"          # 默认值语法:${VAR:-default}
# 覆盖 .env 变量
APP_VERSION=1.3.0 docker compose up -d

# 指定不同的 env 文件
docker compose --env-file .env.staging up -d

# 查看变量替换结果
docker compose config    # 输出完整解析后的 compose.yml

小结

docker-compose 的核心价值:用代码(YAML)描述多容器应用的拓扑结构,一条命令替代多个 docker run,服务间自动通过名称互通。

关键要点:depends_on + healthcheck 组合实现有序启动(单独 depends_on 不可靠);自定义网络比默认网络提供更好的隔离和 DNS 解析;.env 文件管理环境差异,docker compose config 验证最终配置。

下一篇讲 docker-compose 进阶:profiles 多环境、override 文件、扩缩容与健康检查联动。