目录
[① 导读卡片](#① 导读卡片)
[② 背景与目标](#② 背景与目标)
[为什么需要 Compose?](#为什么需要 Compose?)
[③ 概念与原理](#③ 概念与原理)
[Compose 的本质是什么?](#Compose 的本质是什么?)
[④ 逻辑与对比](#④ 逻辑与对比)
[用 vs 不用 Compose 的对比](#用 vs 不用 Compose 的对比)
[什么时候该用 Compose?](#什么时候该用 Compose?)
[⑤ 核心详解](#⑤ 核心详解)
[5.1 基础结构 ------ 一切从 docker-compose.yml 开始](#5.1 基础结构 —— 一切从 docker-compose.yml 开始)
[5.2 五大核心字段详解](#5.2 五大核心字段详解)
[① services ------ 容器定义](#① services —— 容器定义)
[② depends_on + healthcheck ------ 真正的依赖管理](#② depends_on + healthcheck —— 真正的依赖管理)
[③ volumes ------ 数据持久化三兄弟](#③ volumes —— 数据持久化三兄弟)
[④ environment / env_file ------ 配置管理](#④ environment / env_file —— 配置管理)
[⑤ networks ------ 容器通信](#⑤ networks —— 容器通信)
[5.3 常用命令一览](#5.3 常用命令一览)
[⑥ 案例实战](#⑥ 案例实战)
[实战一:本地开发一个 Node.js + Redis + MySQL 应用](#实战一:本地开发一个 Node.js + Redis + MySQL 应用)
[实战二:多环境配置分离(开发 / 生产)](#实战二:多环境配置分离(开发 / 生产))
[实战三:微服务本地联调(A 调用 B)](#实战三:微服务本地联调(A 调用 B))
[⑦ 避坑 & 最佳实践](#⑦ 避坑 & 最佳实践)
[⑧ 总结 & 路线图](#⑧ 总结 & 路线图)
① 导读卡片
一句话定位 :Docker Compose 是 Docker 官方容器编排工具,通过一个 YAML 文件定义和运行多容器应用,从此告别手敲无数条
docker run命令。
-
适合人群 :已掌握
docker run基本操作,希望管理多容器应用的开发者 -
难度:⭐⭐☆☆☆(入门进阶)
-
阅读时长:15 分钟
-
前置知识 :Docker 基础命令(
docker run、docker network、docker volume)
② 背景与目标
为什么需要 Compose?
先看一个真实场景:你要跑一个 Nginx + Tomcat 集群,传统方式需要手动敲:
docker network create mynet
docker run -d --name tomcat1 --network mynet tomcat
docker run -d --name tomcat2 --network mynet tomcat
docker run -d --name nginx -p 80:80 --network mynet -v ./nginx.conf:/etc/nginx/nginx.conf nginx
docker network create mynet
docker run -d --name tomcat1 --network mynet tomcat
docker run -d --name tomcat2 --network mynet tomcat
docker run -d --name nginx -p 80:80 --network mynet -v
./nginx.conf:/etc/nginx/nginx.conf nginx
这条命令链暴露了四大痛点:
| 痛点 | 后果 |
|---|---|
| 命令长、参数多 | 容易写错,排查半天才发现少了个参数 |
| 缺乏一键操作 | 启动/停止/重启每个容器都得单独操作 |
| 依赖顺序靠猜 | Nginx 可能在 Tomcat 之前启动,日志乱飘但无法保证顺序 |
| 跨环境难复现 | 换台机器,同一套命令又得重新敲一遍 |
学完你能做什么?
-
✅ 用一个 YAML 文件定义整个应用栈(Web + 数据库 + 缓存 + Nginx)
-
✅ 一行
docker compose up启动全部服务 -
✅ 实现开发/生产多环境配置分离
-
✅ 掌握微服务本地联调的正确姿势
-
✅ 熟练使用 Compose 生命周期管理命令
③ 概念与原理
Compose 的本质是什么?
Compose 是一个容器编排工具,它的核心工作流极其简单:
你写 YAML 描述需求 → Compose 解析 YAML → 调用 Docker API 创建/管理资源
Compose 引入了一个重要概念------项目(Project) 。每个 Compose 文件对应一个项目,项目内的资源(网络、卷、容器)以 项目名_资源名 的方式隔离,不同项目之间互不干扰。
💡 核心认知 :你理解了
docker run的各个参数,就理解了 90% 的 Compose 配置项。Compose 不会魔法,它只是把命令行参数变成了结构化 YAML。
底层工作流程
当执行 docker compose up 时,Compose 内部做了这些事情:
-
解析 YAML → 读取
docker-compose.yml,合并override文件,替换环境变量 -
创建网络 → 为项目创建默认网络(
项目名_default),所有服务加入此网络 -
创建卷 → 检查命名卷是否存在,不存在则创建
-
构建/拉取镜像 → 根据
build或image字段构建或拉取 -
按依赖顺序启动容器 → 根据
depends_on确定启动顺序 -
健康检查 → 如果配置了
healthcheck,等待服务就绪后才启动依赖它的服务
④ 逻辑与对比
用 vs 不用 Compose 的对比
| 维度 | 手写 docker run |
Docker Compose |
|---|---|---|
| 定义方式 | 命令行参数 | 声明式 YAML |
| 启动全部服务 | 写脚本或一个个敲 | docker compose up |
| 停止全部 | docker stop c1 c2 c3... |
docker compose down |
| 依赖管理 | 手动保证顺序 | depends_on 自动处理 |
| 环境复现 | 需手动记录所有参数 | 一个 YAML 文件搞定 |
| 多环境支持 | 改参数或脚本 | 多文件组合 + 变量替换 |
什么时候该用 Compose?
| 场景 | 建议 |
|---|---|
| 本地开发一个 Web + 数据库的应用 | ✅ 强烈推荐 |
| 多服务微服务联调 | ✅ 必须用 |
| 生产环境单机部署 | ✅ 可以,但 deploy 字段仅 Swarm 生效 |
| 生产环境多机集群 | ❌ 改用 Kubernetes 或 Docker Swarm |
| 只跑一个 Nginx 代理静态文件 | ⚠️ 用 docker run 就够了,不必杀鸡用牛刀 |
⑤ 核心详解
5.1 基础结构 ------ 一切从 docker-compose.yml 开始
一个标准 Compose 文件的结构:
XML
version: '3.8' # 版本号,3.8 是目前最稳定的版本
services: # 定义所有服务(容器)
service_name:
image: nginx:alpine # 使用的镜像
build: ./path # 或通过 Dockerfile 构建
ports:
- "80:80" # 端口映射
volumes:
- ./data:/app # 卷挂载
environment: # 环境变量
- KEY=VALUE
depends_on: # 依赖关系
- other_service
networks: # 网络
- mynet
networks: # 定义网络
mynet:
volumes: # 定义命名卷
mydata:
5.2 五大核心字段详解
① services ------ 容器定义
每个 service 对应一个或多个容器。核心写法分为两类:
用已有镜像启动:
XML
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
从 Dockerfile 构建:
XML
services:
backend:
build: ./backend # 指定 Dockerfile 目录
# build:
# context: ./backend # 构建上下文
# dockerfile: Dockerfile.prod # 指定 Dockerfile
image: myapp:latest # 构建后镜像名
build和image同时存在时,Compose 会先构建镜像,并将其命名为项目名_服务名。
② depends_on + healthcheck ------ 真正的依赖管理
depends_on 只能保证启动顺序,不能保证服务就绪。
错误用法 ------ 以为启动了就等于可用了:
XML
services:
backend:
depends_on:
- db # db 容器启动了,但 PostgreSQL 可能还在初始化
正确用法 ------ 配合 healthcheck:
XML
services:
db:
image: postgres:14
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s # 每 10 秒检查一次
timeout: 5s # 单次检查超时
retries: 5 # 连续失败 5 次算不健康
backend:
depends_on:
db:
condition: service_healthy # 等 db 健康后才启动
③ volumes ------ 数据持久化三兄弟
XML
services:
app:
volumes:
- db_data:/var/lib/postgresql/data # 命名卷(Docker 管理)
- ./src:/app # 绑定挂载(热加载)
- /app/node_modules # 匿名卷(防止覆盖)
三种卷的区别:
| 类型 | 写法特征 | 宿主位置 | 使用场景 |
|---|---|---|---|
| 命名卷 | 名字开头(无/) |
/var/lib/docker/volumes/卷名/_data/ |
数据库数据、持久化核心数据 |
| 绑定挂载 | /或./开头 |
你指定的路径 | 开发时代码热加载、配置文件注入 |
| 匿名卷 | 只有容器路径 | Docker 随机生成 | 屏蔽容器内目录不被宿主机覆盖 |
④ environment / env_file ------ 配置管理
XML
services:
app:
# 方式一:直接写(适合测试、非敏感)
environment:
- DB_HOST=db
- DB_PORT=5432
# 方式二:从 .env 文件引用变量
environment:
- DB_PASSWORD=${DB_PASSWORD}
# 方式三:从外部文件加载(推荐敏感信息)
env_file:
- ./config/db.env
- ./config/app.env
.env 文件(与 docker-compose.yml 同级):
DB_PASSWORD=MyStrongPass123
DB_USER=admin
⑤ networks ------ 容器通信
XML
services:
web:
networks:
- frontend
- backend
api:
networks:
- backend
db:
networks:
- backend
networks:
frontend:
backend:
关键知识点:
-
默认情况下,Compose 为每个项目创建名为
项目名_default的网络 -
同一网络内的服务可以通过 服务名 互相通信(内置 DNS 解析)
-
extra_hosts可以添加自定义 hosts 映射(如访问宿主机)
5.3 常用命令一览
| 需求 | 命令 |
|---|---|
| 启动所有服务(前台) | docker compose up |
| 启动所有服务(后台) | docker compose up -d |
| 停止并删除容器、网络 | docker compose down |
| 停止并删除一切(含卷) | docker compose down -v |
| 查看运行中的服务 | docker compose ps |
| 查看日志(实时) | docker compose logs -f |
| 查看某个服务日志 | docker compose logs -f backend |
| 进入容器调试 | docker compose exec backend bash |
| 重建某个服务 | docker compose up -d --build backend |
| 查看最终配置 | docker compose config |
docker compose config这个命令非常实用------它会输出变量替换后的最终 YAML,排错利器。
⑥ 案例实战
实战一:本地开发一个 Node.js + Redis + MySQL 应用
需求:代码修改自动重启,数据持久化,日志实时查看。
项目结构:
project/
├── docker-compose.yml
├── .env
├── backend/
│ ├── Dockerfile
│ └── app.js
docker-compose.yml:
XML
version: '3.8'
services:
mysql:
image: mysql:8.0
env_file:
- ./config/mysql.env
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
ports:
- "3307:3306" # 避免本地已有 MySQL 冲突
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "3000:3000"
volumes:
- ./backend:/app # 代码热加载
- /app/node_modules # 不覆盖容器内的 node_modules
environment:
- NODE_ENV=development
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
mysql_data:
redis_data:
.env:
MYSQL_ROOT_PASSWORD=devpass123
MYSQL_DATABASE=myapp
实战二:多环境配置分离(开发 / 生产)
核心思路:基础配置 + 环境覆盖文件。
docker-compose.yml(基础配置):
XML
version: '3.8'
services:
app:
build: ./app
ports:
- "8080:8080"
environment:
- DB_HOST=db
depends_on:
- db
db:
image: postgres:14
volumes:
- pg_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
pg_data:
docker-compose.dev.yml(开发覆盖):
XML
services:
app:
volumes:
- ./app:/app # 开发时挂载代码
- /app/node_modules
environment:
- NODE_ENV=development
- DEBUG=true
ports:
- "9229:9229" # 调试端口
db:
ports:
- "5432:5432" # 暴露数据库端口方便客户端连接
docker-compose.prod.yml(生产覆盖):
XML
services:
app:
# 生产环境去掉代码挂载,使用 build 镜像
environment:
- NODE_ENV=production
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
使用方式:
XML
# 开发环境
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
实战三:微服务本地联调(A 调用 B)
场景:两个服务互相调用,都需要访问数据库。
关键知识点验证:
Q:两个服务不加 links 能互相访问吗? A:能。Compose 默认网络已提供 DNS 解析,服务名就是主机名。
XML
version: '3.8'
services:
service-a:
image: myapp/service-a
environment:
- SERVICE_B_URL=http://service-b:8081 # 直接用服务名
service-b:
image: myapp/service-b
ports:
- "8081:8081"
Q:容器怎么访问宿主机服务(比如本地跑的数据库)? A:使用 extra_hosts:
XML
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway" # Docker 20.10+ 自动解析
Mac/Windows 可直接用 host.docker.internal;Linux 用 172.17.0.1(Docker 网关 IP)。
⑦ 避坑 & 最佳实践
常见坑点
坑 1:depends_on 不等于服务就绪
# ❌ 错误:以为 depends_on 后数据库就能连上
depends_on:
- db
# ✅ 正确:配 healthcheck + condition
depends_on:
db:
condition: service_healthy
坑 2:宿主机目录覆盖了容器内的依赖
# ❌ 错误:宿主机的空目录覆盖了整个 /app
volumes:
- ./backend:/app # 如果 ./backend 没有 node_modules...
# ✅ 正确:用匿名卷"保护"容器目录
volumes:
- ./backend:/app
- /app/node_modules # 匿名卷,确保容器内的 node_modules 不被覆盖
坑 3:deploy 字段在单机 Compose 中被忽略
# ❌ 错误:单机运行,deploy.replicas 不生效
services:
app:
deploy:
replicas: 3
# ✅ 替代:使用 --scale
docker compose up -d --scale app=3
坑 4:忘记声明命名卷导致难以管理
# ❌ 不声明顶级 volumes,Compose 也工作,但不好管理
services:
db:
volumes:
- db_data:/var/lib/postgresql/data
# ✅ 显式声明顶级 volumes 是好习惯
volumes:
db_data:
最佳实践清单
-
总是显式声明顶级
volumes和networks--- 即使可以隐式创建,显式声明更方便管理 -
敏感信息走
env_file--- 密码写在.env或环境中,.gitignore排除 -
开发环境用多文件组合 --- 基础配置 + 覆盖文件,避免一个文件改来改去
-
健康检查是必须的 --- 没有 healthcheck 的
depends_on约等于没用 -
用
docker compose config排错 --- 变量替换后是否符合预期,一跑就知道 -
为项目命名 --- 用
--project-name或name:字段显式指定,避免目录名影响 -
限制日志大小 --- 默认日志无限增长会撑爆磁盘
-
docker compose down -v慎用 ----v会删除命名卷,数据全没了
⑧ 总结 & 路线图
本文要点速记
| 模块 | 关键结论 |
|---|---|
| 核心原理 | Compose = 把 docker run 参数写成 YAML + 自动编排 |
| 五大字段 | services networks volumes depends_on environment |
| 依赖管理 | depends_on 只保证启动顺序,healthcheck 保证服务就绪 |
| 卷的类型 | 命名卷(Docker 管)、绑定挂载(用户管)、匿名卷(屏蔽覆盖) |
| 多环境 | 基础文件 + 覆盖文件,变量替换 + .env |
下一步学习路线
第 1 周:掌握 Compose 五大核心字段
用 Compose 跑 WordPress(MySQL + WordPress)
↓
第 2 周:学会开发/生产分离模式
写 dev.yml 和 prod.yml,理解覆盖机制
↓
第 3 周:真实项目迁移
把个人项目从 docker run 改写成 Compose
要求:热加载 + 日志持久化 + 一键启动
↓
第 4 周:深入最佳实践
阅读开源项目(GitLab、Nextcloud)的 docker-compose.yml
学习 secrets 管理、日志驱动、网络隔离等
推荐的三个小实验(做完就是实战派)
-
加健康检查 :在你之前的 Nginx + Tomcat Compose 文件中,给 Tomcat 添加
healthcheck,让 Nginx 只在 Tomcat 就绪后才启动 -
水平扩展 :
docker compose up -d --scale tomcat=3,观察 Nginx 的 upstream 变化 -
日志持久化 :将 Tomcat 的日志目录通过卷挂载到本地
./logs
Compose 不复杂,它就是把你会的手动操作自动化了。每学一个字段,想想"如果不写 Compose,我该怎么用
docker run做到",理解了这层对应关系,你就真正掌握了。