Docker Compose 语法

适用版本:Docker Compose v2(v5.3.0+),基于 Compose Specification


一、核心概念

1.1 是什么

Docker Compose 是 Docker 官方的多容器编排工具,通过一个 YAML 配置文件定义、启动和管理多个容器。

1.2 与旧版(v1)的核心区别

对比项 旧版(v1) 新版 v2(Compose Spec)
version 字段 必须指定(如 version: "3.9" 删除,不再需要
命令格式 docker-compose(带横杠) docker compose(无横杠)
配置文件 支持多个文件合并 支持多个文件合并
部署(deploy) Swarm 模式专用 扩展了更多配置能力

1.3 语法规则

规则 说明
缩进 2 个空格,禁用 Tab
冒号 键后必须加空格image: nginx ✔)
注释 使用 # 开头

二、配置文件结构(新版)

# version: "3.9"     新版中直接删除,不再需要

services:                           # 服务定义(必填)
  web:
    # 服务配置...

networks:                           # 自定义网络(可选)
  frontend:

volumes:                            # 数据卷(可选)
  db-data:

configs:                            # 配置(可选,Swarm 模式)
secrets:                            # 密钥(可选,Swarm 模式)

三、顶层字段详解

3.1 services — 服务定义

所有容器的定义都在这里:

services:
  web:
    image: nginx:alpine
  db:
    image: mysql:8.0

3.2 networks — 自定义网络

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true          # 内部网络,不对外暴露

3.3 volumes — 数据卷声明

volumes:
  db-data:
    driver: local
  app-logs:
    external: true          # 使用已存在的外部卷

四、服务配置字段

4.1 image / build — 镜像来源

services:
  # 直接使用镜像
  web:
    image: nginx:1.24-alpine

  # 从 Dockerfile 构建
  app:
    build: ./app

  # 构建详细配置
  app:
    build:
      context: ./app
      dockerfile: Dockerfile.prod
      args:
        BUILD_ENV: production
      target: production       # 多阶段构建目标
      cache_from:
        - myapp:latest

4.2 container_name — 容器命名

services:
  web:
    image: nginx
    container_name: my-nginx-server

生产环境建议不写,让 Compose 自动生成名称,避免扩容时冲突。

4.3 ports / expose — 端口配置

services:
  web:
    ports:
      - "8080:80"
      - "127.0.0.1:8080:80"
      - "80"                          # 随机宿主机端口
      - "443:443/tcp"
      - "53:53/udp"
      
      # 长语法(推荐)
      - target: 80
        published: 8080
        protocol: tcp
        mode: host

  db:
    expose:
      - "3306"

4.4 environment / env_file — 环境变量

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret123
      MYSQL_DATABASE: myapp

  app:
    image: myapp
    env_file:
      - .env
      - .env.production

优先级environment > env_file > Dockerfile 中的 ENV

4.5 volumes — 数据卷挂载

services:
  web:
    image: nginx
    volumes:
      # 命名卷(推荐)
      - app-data:/app/data
      
      # 宿主机目录
      - ./html:/usr/share/nginx/html
      
      # 只读挂载
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      
      # 长语法
      - type: volume
        source: app-data
        target: /app/data
        read_only: false

4.6 depends_on + healthcheck — 依赖与健康检查

services:
  web:
    image: nginx
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

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

depends_on 的 condition 可选值

说明
service_started 等待容器启动(默认)
service_healthy 等待健康检查通过
service_completed_successfully 等待容器正常退出

4.7 restart — 重启策略

services:
  web:
    image: nginx
    restart: unless-stopped
策略 说明
no 不重启(默认)
always 任何情况都重启
on-failure 异常退出时重启
unless-stopped 除手动停止外都重启(生产推荐

4.8 networks — 网络配置

services:
  web:
    image: nginx
    networks:
      - frontend
      - backend
    
    # 或指定详细配置
    networks:
      frontend:
        aliases:
          - web.local
      backend:
        ipv4_address: 172.20.0.10

4.9 resources — 资源限制

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1g
        reservations:
          cpus: '0.5'
          memory: 512m

非 Swarm 模式也支持

services:
  app:
    image: myapp
    mem_limit: 1g
    mem_reservation: 512m
    cpus: '1.0'

4.10 command / entrypoint

services:
  app:
    image: python:3.10
    entrypoint: ["/bin/sh", "-c"]
    command: ["python app.py --port 8080"]

4.11 logging — 日志配置

services:
  web:
    image: nginx
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"
        compress: "true"

4.12 profiles — 条件启动

services:
  web:
    image: nginx
    profiles:
      - production

  debug-tools:
    image: alpine
    profiles:
      - debug

  db:
    image: mysql   # 默认启动
docker compose --profile production up
docker compose --profile production --profile debug up

4.13 extends — 配置继承

# docker-compose.base.yml
services:
  web-base:
    image: nginx
    restart: always

# docker-compose.yml
services:
  web:
    extends:
      file: docker-compose.base.yml
      service: web-base
    ports:
      - "80:80"

4.14 其他常用字段

字段 说明
user 运行用户
working_dir 工作目录
hostname 容器主机名
privileged 特权模式
cap_add / cap_drop Linux 能力管理
sysctls 内核参数
tmpfs 内存文件系统
stdin_open 交互模式
tty 分配伪终端

五、变量替换与 .env 文件

5.1 基础用法

# docker-compose.yml
services:
  db:
    image: mysql:${MYSQL_VERSION:-8.0}
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
# .env
MYSQL_VERSION=8.0
DB_PASSWORD=secret123

5.2 变量优先级

Shell 环境变量 > .env 文件 > Dockerfile 中的 ENV

六、多文件组合

# 开发环境(自动加载 override)
docker compose up

# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

七、常用命令(v2 版)

注意:新版命令是 docker compose无横杠),不是 docker-compose

命令 说明
docker compose up -d 后台启动所有服务
docker compose down 停止并删除服务
docker compose down -v 删除数据卷
docker compose start/stop/restart 启停/重启
docker compose ps 查看状态
docker compose logs -f 服务名 查看日志
docker compose build --no-cache 构建镜像
docker compose exec web bash 进入容器
docker compose config 验证语法

八、完整生产级示例(新版)

# 注意:没有 version 字段

# ========== 网络 ==========
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

# ========== 数据卷 ==========
volumes:
  db-data:
  redis-data:
  app-logs:

# ========== 服务 ==========
services:
  # -------- Nginx --------
  nginx:
    image: nginx:1.24-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
      - app-logs:/var/log/nginx
    depends_on:
      - app
    restart: unless-stopped
    networks:
      - frontend
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 10s
      retries: 3
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

  # -------- 应用 --------
  app:
    build:
      context: ./app
      args:
        BUILD_ENV: production
    environment:
      - NODE_ENV=production
      - DB_HOST=db
      - REDIS_HOST=redis
    volumes:
      - app-logs:/app/logs
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - frontend
      - backend
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1g
        reservations:
          cpus: '0.5'
          memory: 512m
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

  # -------- MySQL --------
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d:ro
    ports:
      - "3306:3306"
    restart: unless-stopped
    networks:
      - backend
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 60s
    deploy:
      resources:
        limits:
          memory: 2g
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

  # -------- Redis --------
  redis:
    image: redis:7.2-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    restart: unless-stopped
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 256m
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "3"

九、常见踩坑

踩坑 1:version 字段警告

问题:新版 Docker Compose 提示 version 字段已弃用。

解决:直接删除 version 行。

踩坑 2:命令写错

问题:使用 docker-compose 命令报错。

解决:新版使用 docker compose(无横杠)。

踩坑 3:down -v 误删数据

解决:使用命名卷并声明在顶层 volumes 中。

踩坑 4:env_file 变量不被 Compose 自身解析

解决:变量放在同目录的 .env 文件中用于 Compose 替换,env_file 仅用于容器内环境变量。


十、快速检查清单

  • 所有镜像固定了版本(不用 latest
  • 有状态服务使用了命名卷(volumes: 顶层声明)
  • 敏感信息用了 .envenv_file,没有硬编码
  • 服务间依赖使用了 depends_on(如需就绪等待,配置了 healthcheck
  • 生产环境配置了 restart: unless-stopped
  • 生产环境配置了日志轮转(logging
  • 生产环境配置了资源限制(deploy.resources
  • 自定义网络已声明(如需要)
  • 端口映射没有冲突
  • container_name 没有写死(除非有特殊需求)
  • 执行 docker-compose config 验证语法通过