Docker Compose 教程(2026):compose.yaml 配置详解与实战模板

从零学 Docker Compose:逐字段讲透 compose.yaml 的 services、volumes、networks、healthcheck,附 WordPress+MySQL 实战案例和生产级最佳实践模板。

Bruce

DockerDocker Compose容器化入门教程YAML

AI实战

1604 Words

2026-01-24 00:00 +0000


Docker Compose 是目前最流行的多容器编排工具,而 docker-compose.yml(新版推荐命名为 compose.yaml)就是它的核心配置文件。无论你是刚接触容器化的新手,还是想系统梳理配置细节的老手,这篇 Docker Compose 教程都适合你。

本文将逐字段讲解 compose.yaml 中 services、volumes、networks、ports、environment、healthcheck 等所有配置项的含义和用法,并通过 WordPress + MySQL 等实战案例帮你快速上手。配合 Docker 常用命令速查 一起使用效果更佳。

很多人一看到 compose.yaml 就头大,一堆冒号、缩进,不知道从何下手。其实它没那么复杂,下面我用最通俗的方式,带你彻底搞懂这个文件。

重要提示:旧版 docker-compose(带连字符)是用 Python 写的独立工具,已于 2023 年 7 月停止维护。现在应该使用 docker compose(空格分隔),它是 Docker CLI 的内置插件,用 Go 重写,性能更好,功能更全。本文所有命令均使用新版语法。


一、compose.yaml 是什么?

用盖房子来理解

想象你要盖一栋房子:

  • Docker 镜像 = 建材(砖头、水泥、钢筋)
  • Docker 容器 = 盖好的房间
  • compose.yaml = 设计图纸

设计图纸上写着:

  • 要盖几个房间
  • 每个房间多大
  • 房间之间怎么连通
  • 水电怎么接

compose.yaml 就是告诉 Docker:

  • 要启动几个容器
  • 每个容器用什么镜像
  • 容器之间怎么通信
  • 端口和数据怎么映射

没有它会怎样?

没有 compose.yaml,你要一个个手动启动容器:

# 先启动数据库
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -v mysql_data:/var/lib/mysql mysql:8.0

# 再启动 Redis
docker run -d --name redis redis:7

# 最后启动应用,还要连接上面两个
docker run -d --name app --link mysql --link redis -p 8080:8080 my-app

三个容器就要敲三行命令,参数一堆。有了 compose.yaml,一个命令搞定:

docker compose up -d

文件名的变化

时代文件名说明
旧版 (V1)docker-compose.yml已弃用
新版 (V2)compose.yaml(推荐)也兼容 compose.ymldocker-compose.ymldocker-compose.yaml

新项目建议直接用 compose.yaml


二、文件长什么样?

先看一个最简单的例子:

services:
  web:
    image: nginx
    ports:
      - "80:80"

就这么几行,就能启动一个 Nginx 服务器。注意:不需要写 version,Compose V2 已经废弃了 version 字段。

再看一个稍微复杂点的:

services:
  web:
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    depends_on:
      - api

  api:
    build: ./api
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=123456
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

别被吓到,我们一块块拆解。


三、YAML 嵌套规范

在讲具体配置之前,先搞懂 YAML 的嵌套规则,否则写配置时会一头雾水。

核心规则

  1. 用空格缩进,禁止用 Tab — 建议统一用 2 个空格
  2. 同级元素缩进相同 — 同一层级的 key 必须对齐
  3. 子级比父级多缩进 2 个空格 — 表示从属关系
  4. 冒号后面要有空格key: value,不是 key:value
  5. 列表项用 - 开头 — 短横线后面要有空格

嵌套层级图解

以你给的实际配置为例,逐层拆解:

# 第 0 层(顶层):services 是根 key
services:
  # 第 1 层:服务名(backend、frontend)
  backend:
    # 第 2 层:服务的配置项
    build:
      # 第 3 层:build 的子配置
      context: ./backend
      dockerfile: Dockerfile.dev
    container_name: octomira-backend
    ports:
      # 第 2 层的列表项
      - "8000:8000"
    volumes:
      - ./backend:/app
    environment:
      - DEBUG=True
      - DB_HOST=host.docker.internal
    extra_hosts:
      - "host.docker.internal:host-gateway"

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    container_name: octomira-frontend
    ports:
      - "8087:8087"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    environment:
      - VITE_API_PROXY_TARGET=http://backend:8000
    depends_on:
      - backend

YAML 的三种数据类型

# 1. 键值对(映射 / Map)
container_name: octomira-backend
# key: value

# 2. 列表(序列 / Sequence)
ports:
  - "8000:8000"
  - "8001:8001"
# 短横线 + 空格 表示列表的每一项

# 3. 嵌套映射(映射套映射)
build:
  context: ./backend
  dockerfile: Dockerfile.dev
# build 下面的 context 和 dockerfile 是 build 的子属性

两种列表写法

# 写法一:短横线格式(常用)
environment:
  - DEBUG=True
  - DB_HOST=localhost

# 写法二:键值对格式(也可以)
environment:
  DEBUG: "True"
  DB_HOST: localhost

两种等价,但同一个文件里建议统一风格。

常见缩进错误

# ❌ 错误:缩进不一致
services:
  web:
    image: nginx
     ports:        # 多了一个空格,会报错
      - "80:80"

# ❌ 错误:用了 Tab
services:
	web:             # Tab 缩进,YAML 不允许
    image: nginx

# ❌ 错误:冒号后缺空格
services:
  web:
    image:nginx     # 冒号后没空格

# ✅ 正确
services:
  web:
    image: nginx
    ports:
      - "80:80"

建议:用 VS Code 装 YAML 插件,实时检查格式错误。


四、配置字段全览:哪些必选,哪些可选

顶层结构

compose.yaml 有这些顶层 key

顶层 key必选说明
services定义要运行的容器,唯一必填项
volumes声明命名卷(用了命名卷才需要)
networks声明自定义网络(不声明则所有服务共享默认网络)
configs声明配置文件(Swarm 模式用得多)
secrets声明敏感数据(如密码、证书)

服务配置字段

每个服务(services 下的子项)可以使用以下字段:

字段必选类型说明
image⚠️ 二选一字符串使用现成镜像
build⚠️ 二选一字符串或映射从 Dockerfile 构建镜像
ports列表端口映射
volumes列表数据卷/目录挂载
environment列表或映射环境变量
env_file字符串或列表从文件读取环境变量
depends_on列表或映射启动依赖顺序
networks列表加入的网络
restart字符串重启策略
command字符串或列表覆盖默认启动命令
entrypoint字符串或列表覆盖入口点
container_name字符串指定容器名称
healthcheck映射健康检查配置
extra_hosts列表添加额外的 hosts 映射
working_dir字符串容器内工作目录
user字符串容器内运行的用户
stdin_open布尔值保持 stdin 打开(等价于 docker run -i
tty布尔值分配伪终端(等价于 docker run -t
logging映射日志驱动配置
deploy映射部署相关配置(资源限制等)
profiles列表配置文件分组(按需启动)
platform字符串指定平台(如 linux/amd64

⚠️ imagebuild 二选一:每个服务必须指定 image(用现成镜像)或 build(自己构建),两者也可以同时写(构建后打上 image 指定的标签)。


五、逐个讲解核心配置

1. services(服务列表)— 必选

services:
  web:
    # web 服务的配置
  api:
    # api 服务的配置
  db:
    # 数据库服务的配置

是什么:你要运行的所有容器,每个服务就是一个容器。

命名规则

  • 用小写字母
  • 可以用下划线或短横线
  • 取个有意义的名字(backendfrontenddb
  • 服务名同时也是容器间互访的主机名

2. image(使用的镜像)

services:
  web:
    image: nginx:1.25

格式镜像名:标签

  • nginx → 默认用 latest 标签(不推荐生产环境用 latest)
  • nginx:1.25 → 指定版本,可复现
  • mysql:8.0 → MySQL 8.0

去哪找镜像Docker Hub 上搜索。


3. build(自己构建镜像)

简单写法:

services:
  api:
    build: ./api

完整写法:

services:
  api:
    build:
      context: ./api           # 构建上下文目录
      dockerfile: Dockerfile.dev  # 指定 Dockerfile 文件名
      args:                    # 构建参数
        - NODE_ENV=development
      target: dev              # 多阶段构建的目标阶段

image vs build

  • image:用别人做好的镜像(nginx、mysql、redis)
  • build:用自己写的代码 + Dockerfile 构建镜像

4. ports(端口映射)

services:
  web:
    ports:
      - "8080:80"

格式"宿主机端口:容器端口"

ports:
  - "80:80"                    # 外部 80 → 容器 80
  - "8080:80"                  # 外部 8080 → 容器 80
  - "127.0.0.1:3306:3306"     # 仅本机可访问
  - "8000-8010:8000-8010"     # 端口范围映射

注意:端口号建议加引号,因为 YAML 中 xx:yy 可能被解析为六十进制数值。


5. volumes(数据卷/目录挂载)

services:
  db:
    volumes:
      - db_data:/var/lib/mysql       # 命名卷
      - ./config:/etc/mysql/conf.d   # 绑定挂载
      - /app/node_modules            # 匿名卷

volumes:
  db_data:     # 顶层声明命名卷

三种类型

类型写法用途
命名卷name:/container/path持久化数据,Docker 管理存储位置
绑定挂载./host/path:/container/path开发时同步代码,修改实时生效
匿名卷/container/path排除某个目录不被绑定挂载覆盖

重要:用了命名卷,必须在顶层 volumes 里声明。


6. environment(环境变量)

两种等价写法:

# 列表格式
environment:
  - MYSQL_ROOT_PASSWORD=123456
  - MYSQL_DATABASE=myapp

# 映射格式
environment:
  MYSQL_ROOT_PASSWORD: 123456
  MYSQL_DATABASE: myapp

更安全的做法:用 env_file.env 文件

services:
  db:
    env_file:
      - ./db.env    # 从文件读取环境变量
# db.env(不要提交到 Git!加入 .gitignore)
MYSQL_ROOT_PASSWORD=super_secret_password

7. depends_on(启动顺序)

简单写法:

services:
  web:
    depends_on:
      - api
      - db

带健康检查的写法(推荐):

services:
  api:
    depends_on:
      db:
        condition: service_healthy    # 等 db 健康检查通过才启动
        restart: true                 # db 重启时 api 也跟着重启

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

注意:简单的 depends_on 只保证启动顺序,不保证服务"准备好了"。要等服务真正可用,用 condition: service_healthy


8. networks(网络)

services:
  web:
    networks:
      - frontend

  api:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend

networks:
  frontend:
  backend:

默认情况:不写 networks,所有服务自动在同一个网络,可以互相访问。只有需要网络隔离时才需要自定义。


9. restart(重启策略)

services:
  web:
    restart: unless-stopped
说明
no不自动重启(默认)
always总是重启,包括 Docker 启动时
on-failure只有非正常退出才重启
unless-stopped除非手动 docker compose stop,否则一直重启

生产环境推荐 unless-stoppedalways


10. extra_hosts(额外 hosts 映射)

services:
  backend:
    extra_hosts:
      - "host.docker.internal:host-gateway"

是什么:往容器的 /etc/hosts 文件里添加记录。

典型场景:容器要连接宿主机上的服务(如本地 MySQL),用 host.docker.internal 指向宿主机。

注:Docker Desktop(macOS/Windows)自带 host.docker.internal,Linux 上需要手动加 host-gateway


11. command 和 entrypoint

services:
  api:
    image: node:18
    command: npm run dev          # 覆盖 CMD
    # 或
    entrypoint: ["node", "app.js"]  # 覆盖 ENTRYPOINT

区别

  • command:覆盖 Dockerfile 中的 CMD,常用于切换启动方式
  • entrypoint:覆盖 Dockerfile 中的 ENTRYPOINT,改变程序入口

12. container_name(指定容器名)

services:
  db:
    container_name: my_mysql

不指定会怎样:Docker Compose 会自动生成,格式为 项目名-服务名-序号(如 myproject-db-1)。

注意:指定了 container_name 就不能做服务扩缩容(docker compose up --scale db=3 会冲突)。


13. deploy(资源限制)

services:
  api:
    deploy:
      resources:
        limits:
          cpus: '0.5'         # 最多用 0.5 个 CPU
          memory: 512M        # 最多用 512MB 内存
        reservations:
          cpus: '0.25'        # 预留 0.25 个 CPU
          memory: 256M        # 预留 256MB 内存

14. profiles(按需启动)

services:
  web:
    image: nginx

  debug-tools:
    image: busybox
    profiles:
      - debug      # 只在 debug profile 下启动
# 正常启动,debug-tools 不会启动
docker compose up -d

# 启动包含 debug profile 的服务
docker compose --profile debug up -d

六、理解你给的实战配置

回到你提供的例子,逐字段注释:

# 顶层 key:services(必选)
services:

  # 第一个服务:backend
  backend:
    build:                          # 自己构建镜像(不用 image 拉现成的)
      context: ./backend            #   构建上下文 = ./backend 目录
      dockerfile: Dockerfile.dev    #   使用的 Dockerfile 文件名
    container_name: octomira-backend  # 固定容器名
    ports:
      - "8000:8000"                 # 宿主机 8000 → 容器 8000
    volumes:
      - ./backend:/app              # 绑定挂载,代码修改实时同步到容器
    environment:                    # 环境变量
      - DEBUG=True
      - DB_HOST=host.docker.internal  # 连接宿主机上的 MySQL
      - DB_NAME=octomira
      - DB_USER=octomira
      - DB_PASSWORD=octomira00##
    extra_hosts:
      - "host.docker.internal:host-gateway"  # Linux 下让容器能找到宿主机

  # 第二个服务:frontend
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    container_name: octomira-frontend
    ports:
      - "8087:8087"
    volumes:
      - ./frontend:/app             # 同步前端代码
      - /app/node_modules           # 匿名卷:排除 node_modules,不被上一行覆盖
    environment:
      - VITE_API_PROXY_TARGET=http://backend:8000  # 用服务名 backend 作为主机名
    depends_on:
      - backend                     # frontend 等 backend 启动后再启动

关键点

  • - /app/node_modules 是匿名卷,作用是防止宿主机的绑定挂载覆盖容器里安装好的 node_modules
  • http://backend:8000 中的 backend 是服务名,Compose 自动解析为该容器的 IP

七、完整实战案例

案例:搭建一个博客系统

需求:WordPress + MySQL + phpMyAdmin(数据库管理工具)

services:
  # WordPress 主程序
  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress_password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  # MySQL 数据库
  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db_data:/var/lib/mysql
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  # phpMyAdmin 数据库管理
  phpmyadmin:
    image: phpmyadmin:latest
    ports:
      - "8081:80"
    environment:
      PMA_HOST: db
      PMA_USER: root
      PMA_PASSWORD: root_password
    depends_on:
      - db

# 声明命名卷
volumes:
  wordpress_data:
  db_data:

使用方法

# 启动所有服务
docker compose up -d

# 访问
# WordPress: http://localhost:8080
# phpMyAdmin: http://localhost:8081

# 查看运行状态
docker compose ps

# 查看日志
docker compose logs -f

# 停止所有服务
docker compose down

# 停止并删除数据
docker compose down -v

八、常见问题

Q1:缩进出错怎么排查?

YAML 对缩进敏感,必须用空格,不能用 Tab。

# 用 docker compose config 验证配置是否正确
docker compose config

# 如果有语法错误,会直接报错并指出行号

Q2:容器之间怎么互相访问?

直接用服务名作为主机名:

services:
  api:
    environment:
      - DB_HOST=db    # 直接用服务名 "db"

  db:
    image: mysql:8.0

在 api 容器里,ping db 就能通。

Q3:docker-compose 还能用吗?

如果你的系统还装着旧版,暂时能用,但建议尽快切换:

# 检查是否有新版
docker compose version

# 旧版(别再用了)
docker-compose --version

所有旧命令都有对应的新命令,只需把 docker-compose 换成 docker compose

旧命令新命令
docker-compose updocker compose up
docker-compose downdocker compose down
docker-compose psdocker compose ps
docker-compose logsdocker compose logs

Q4:文件名必须是 compose.yaml 吗?

Docker Compose V2 按以下优先级查找文件:

  1. compose.yaml
  2. compose.yml
  3. docker-compose.yaml
  4. docker-compose.yml

用其他名字要指定:

docker compose -f my-config.yaml up -d

Q5:怎么查看最终生效的配置?

# 合并所有配置文件和环境变量,输出最终结果
docker compose config

非常适合排查"为什么配置没生效"的问题。


九、常用命令速查表

命令作用
docker compose up -d后台启动所有服务
docker compose down停止并删除容器
docker compose down -v停止并删除容器和数据卷
docker compose ps查看运行状态
docker compose logs -f实时查看日志
docker compose logs -f web只看某个服务的日志
docker compose restart重启所有服务
docker compose exec web bash进入 web 容器
docker compose pull拉取最新镜像
docker compose build重新构建镜像
docker compose config验证并输出最终配置
docker compose up -d --build api重建某个服务并启动

十、官方参考

想查某个字段的完整用法?这些是权威来源:


十一、总结

compose.yaml 的核心就这几个:

  1. services(必选) — 定义要跑哪些容器
  2. image/build(二选一) — 用现成镜像还是自己构建
  3. ports — 端口映射,让外部能访问
  4. volumes — 数据持久化 + 开发时代码同步
  5. environment — 传配置参数
  6. depends_on — 控制启动顺序和依赖

记住这个公式:

compose.yaml = services(必选)+ 每个服务的配置(image/build 必选,其余按需)

嵌套规则只有一条:每深一层,多缩进 2 个空格。搞不清楚就跑 docker compose config 验证。


十二、docker-compose.yml 最佳实践

在实际项目中,以下几点经验可以帮你避免常见的坑:

  1. 镜像版本锁定 – 生产环境永远不要用 latest 标签,指定明确的版本号(如 nginx:1.25mysql:8.0),确保环境可复现
  2. 敏感信息外置 – 密码、密钥等不要直接写在 compose.yaml 中,使用 env_file 或 Docker Secrets,并把 .env 文件加入 .gitignore
  3. 健康检查必加 – 对数据库等基础服务配置 healthcheck,配合 depends_oncondition: service_healthy,确保服务真正就绪后再启动依赖服务
  4. 数据持久化 – 重要数据(数据库、上传文件等)必须使用命名卷或绑定挂载,否则容器删除后数据会丢失
  5. 资源限制 – 通过 deploy.resources 限制 CPU 和内存使用,防止单个容器耗尽宿主机资源
  6. 日志管理 – 配置 logging 驱动限制日志文件大小,避免磁盘被日志撑满
# 日志管理示例
services:
  web:
    image: nginx:1.25
    logging:
      driver: json-file
      options:
        max-size: "10m"    # 单个日志文件最大 10MB
        max-file: "3"      # 最多保留 3 个日志文件

十三、Docker Compose 常见问题 FAQ

Docker Compose 和 Docker Swarm 有什么区别?

Docker Compose 用于单机多容器编排,适合开发环境和小规模生产部署;Docker Swarm 是 Docker 原生的集群编排方案,用于跨多台主机部署容器。如果你的应用只在一台服务器上运行,用 Docker Compose 就够了。

docker compose up 和 docker compose run 有什么区别?

docker compose up 启动 compose.yaml 中定义的所有服务(或指定服务及其依赖),是最常用的启动命令。docker compose run 则是针对某个服务运行一次性命令,比如 docker compose run api npm test,执行完就退出,适合跑测试或数据库迁移。

如何在 Docker Compose 中使用 .env 文件?

在 compose.yaml 同级目录下创建 .env 文件,Docker Compose 会自动加载其中的变量。你可以在 compose.yaml 中通过 ${变量名} 引用这些变量。务必将 .env 加入 .gitignore 以避免泄露敏感信息。

Docker Compose 如何实现零停机更新?

可以使用 docker compose up -d --no-deps --build <服务名> 单独重建和重启某个服务,其他服务不受影响。对于更严格的零停机需求,建议结合反向代理(如 Nginx 或 Traefik)实现蓝绿部署或滚动更新。

compose.yaml 中的 volumes 数据在 docker compose down 后还在吗?

如果使用 docker compose down,命名卷(named volumes)中的数据会保留。只有加了 -v 参数(docker compose down -v)才会删除命名卷。绑定挂载(bind mounts)的数据始终在宿主机上,不受影响。


相关阅读