docker-compose.yml 是 Docker Compose 工具用来描述多容器应用的核心配置文件。它采用 YAML 语法,把原本需要多次执行 docker run 才能启动的容器、网络、数据卷和依赖关系集中到一个文件中。当团队需要共享开发环境或快速拉起测试栈时,一份清晰可读的 compose 文件能显著减少配置错误。本文将通过一个完整的 Web 应用示例,逐项拆解常用的配置字段和写法。

在开始编写之前,需要明确 Compose 规范已经不再强制要求 version 字段。较旧的教程经常会在文件顶部写 version: "3.8",但现代 Docker Compose v2 会忽略或不再推荐该字段。除非你需要兼容非常老的工具链,否则可以直接从 services 开始配置。文件顶层通常包含 services、networks、volumes 三个块,其中 services 是必填项,networks 和 volumes 在需要自定义网络或持久化数据时才会出现。
一、Compose 文件的基础结构
一个最小的 docker-compose.yml 可以只包含一个服务。例如启动单个 Nginx 容器并映射端口,文件内容如下所示。
services:
web:
image: nginx:1.25
ports:
- "8080:80"
这个文件声明了一个名为 web 的服务,使用官方 nginx:1.25 镜像,将宿主机的 8080 端口映射到容器内部的 80 端口。YAML 的层级关系通过缩进表达,services 是顶层键,web 是它的子项,image 和 ports 则属于 web 服务。缩进必须保持一致,通常使用两个空格,不能混用制表符和空格,否则 Compose 会抛出解析错误。
除了 services 之外,顶层还可以包含 networks 和 volumes。默认情况下,Compose 会自动创建一个网络,所有服务都会加入其中,并且可以通过服务名互相访问。但当你需要隔离流量、设置外部网络或者声明命名卷时,就需要显式定义 networks 和 volumes 块。下面的结构展示了三个顶层块的常规布局。
services:
app:
image: myapp:latest
networks:
backend:
driver: bridge
volumes:
db_data:
其中 networks 下的 backend 是网络名称,driver 指定了网络驱动,bridge 是最常见的单机桥接网络。volumes 下的 db_data 是一个命名卷,由 Docker 管理存储位置。如果没有在服务中引用这些网络或卷,它们只是被声明而不会实际创建或连接。因此下一步需要在 services 内部把它们关联起来。
二、服务定义:镜像、构建、端口与环境变量
services 是 Compose 文件中最核心的部分,每个服务对应一个容器。声明服务的常用指令包括 image、build、ports、environment、env_file、command 和 restart。如果服务需要从本地 Dockerfile 构建,可以使用 build 字段指定上下文目录,同时也可以通过 image 指定构建后的镜像标签。
services:
app:
build: ./app
image: myapp:dev
ports:
- "3000:3000"
environment:
NODE_ENV: development
DB_HOST: db
DB_PORT: "5432"
restart: unless-stopped
上面的配置表示 app 服务会从项目下的 app 目录构建镜像,并将构建结果标记为 myapp:dev。ports 短语法使用宿主机端口:容器端口的形式,environment 使用键值对映射,值可以用数字或字符串。这里特意把 DB_PORT 写成 "5432" 并加上引号,是为了避免 YAML 将端口识别为数字,虽然 Compose 会转换为字符串,但明确写成字符串可以减少歧义。restart: unless-stopped 表示容器在退出后自动重启,除非管理员手动停止。
environment 还有另一种列表写法,适合从 shell 环境直接复制变量。例如:
environment: - NODE_ENV=development - DB_HOST=db - DB_PORT=5432
如果需要加载一个包含大量变量的文件,可以使用 env_file 指令,而不必在 environment 中逐个列举。env_file 可以指定一个或多个 .env 文件,Compose 会将文件中的键值对注入容器。不过需要注意,env_file 中的变量不会自动用于 Compose 文件本身的插值,即无法直接在 docker-compose.yml 里用 ${VAR} 读取 env_file 的内容。如果需要在 Compose 文件中使用变量替换,应该使用 .env 文件放在项目根目录,Compose 会自动读取该文件并用于 ${VAR} 插值。
对于数据库或缓存这类第三方服务,通常直接使用官方镜像,并通过 environment 传递初始化参数。例如 PostgreSQL 服务可以这样声明:
services:
db:
image: postgres:16
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db_data:/var/lib/postgresql/data
这里的 volumes 短语法将命名卷 db_data 挂载到容器内的数据目录,使数据库数据持久化到 Docker 管理的卷中。如果没有这一行,容器重建后数据会丢失。volumes 支持短语法与长语法,短语法适合大多数场景,长语法则可以指定卷驱动、只读挂载等高级选项。
三、服务依赖与健康检查
多容器应用通常存在启动顺序问题。例如 Web 应用需要等待数据库完全就绪后才能正常处理请求。depends_on 指令可以控制服务的启动顺序,但它只能保证先启动依赖服务,不能保证依赖服务已经可以接受连接。所以仅靠 depends_on 可能仍然会遇到数据库尚未完成初始化的情况。
services:
app:
depends_on:
- db
db:
image: postgres:16
上面的配置会让 Compose 先启动 db,再启动 app。但如果 db 的初始化脚本需要几十秒,app 启动时数据库可能还没准备好。为了解决这个问题,可以给 db 添加 healthcheck,然后让 app 依赖 db 的健康状态。健康检查可以定义为列表形式或字符串形式。列表形式使用 CMD-SHELL 执行 shell 命令,字符串形式则直接执行命令。
services:
db:
image: postgres:16
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 10s
timeout: 5s
retries: 5
healthcheck 中的 test 是必须项,interval 表示检查间隔,timeout 是单次检查的超时时间,retries 是连续失败多少次后判定为不健康。当 db 被标记为 healthy 后,app 可以使用长语法依赖这个状态,从而确保启动时数据库已经可用。
services:
app:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
condition 支持三种值:service_started 表示依赖服务已启动,service_healthy 表示依赖服务通过健康检查,service_completed_successfully 表示依赖服务以成功状态退出。service_healthy 是最常用的选项,尤其适合数据库和消息队列。需要注意的是,并非所有镜像都自带健康检查命令,对于没有提供现成命令的服务,可以在 healthcheck 中自行编写 test 命令。健康检查仅影响 depends_on 的 condition 判断,并不会自动重启不健康的容器;如果希望自动重启,还需要配合 restart 策略。
四、网络、数据卷与多环境配置
默认情况下,Compose 创建一个名为项目名_default 的桥接网络,所有服务通过该网络通信,并且可以直接使用服务名作为主机名。但如果需要将某些服务隔离到不同网络,或者连接一个预先创建的外部网络,就要在 networks 块中显式声明。下面的示例定义了 frontend 和 backend 两个网络,app 同时加入两个网络,db 只加入 backend。
services:
app:
networks:
- frontend
- backend
db:
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
通过这种划分,只有 app 可以同时访问前端和后端网络,db 不会暴露给 frontend 中的服务。如果网络已经通过 docker network create 在宿主机上创建,可以使用 external 字段引用它。external 告诉 Compose 不要创建该网络,而是使用现有的网络。
networks:
existing_network:
external: true
数据卷同样支持命名卷和绑定挂载两种主要方式。命名卷由 Docker 管理,适合数据库等需要持久化的数据;绑定挂载直接映射宿主机目录,适合开发环境中的代码热更新。短语法用冒号分隔源和目标,长语法则可以额外指定只读、传播方式等属性。
services:
app:
volumes:
- type: bind
source: ./app
target: /usr/src/app
- type: volume
source: node_modules
target: /usr/src/app/node_modules
上面的长语法中,第一个卷是绑定挂载,把宿主机当前目录下的 app 目录挂载到容器内的 /usr/src/app。这样修改本地代码后,容器内能立即看到变化。第二个卷是命名卷,用于保存 node_modules,避免宿主机目录中的依赖覆盖容器内安装的依赖,这是 Node.js 开发环境中常见的处理方式。
当项目有多个环境时,可以通过多个 Compose 文件叠加来覆盖配置。默认情况下,运行 docker compose up 时会自动读取 docker-compose.yml 和可选的 docker-compose.override.yml。override 文件可以覆盖端口、卷、环境变量等字段,适合本地开发与生产部署的差异管理。例如生产文件可以去掉绑定挂载,改用镜像中的构建产物,并关闭调试端口。使用 docker compose -f docker-compose.yml -f docker-compose.prod.yml up 可以按顺序合并多个文件,后面的文件优先级更高。
综合来看,一份可用的 docker-compose.yml 应该先明确服务边界,再逐个补齐端口、环境变量、依赖和存储。每次修改后使用 docker compose config 检查最终生效的配置,可以提前发现 YAML 语法错误和变量插值问题。借助健康检查和自定义网络,开发环境的多容器协作会更加稳定,也能减少团队之间的配置差异。
docker-compose.ymldocker compose容器编排修改时间:2026-08-28 03:15:52