DockerCompose基础
基础使用
Docker Compose 的作用是什么?解决了什么问题?
一句话原理
Docker Compose 是一个用于定义和运行多容器应用的工具,通过一个 YAML 文件(docker-compose.yml)将应用的架构、网络和数据卷配置“代码化”,实现一键构建、启动和销毁。
一句话源码
使用 docker-compose up -d 一键启动复杂架构,替代了原本需要编写数十行 Shell 脚本或冗长的 docker run 命令。
一句话项目/场景 在“微服务开发环境搭建”中,需要同时启动 API 服务、MySQL 数据库、Redis 缓存和 RabbitMQ 消息队列,使用 Compose 可以一键拉起整套环境,团队成员无需手动配置每个组件的 IP 和端口映射。
详细解析:解决了什么问题?
解决“命令行参数地狱”
如果不使用 Compose,启动一个典型的 Web 应用可能需要执行如下命令:
问题:命令冗长难记、参数容易写错、无法版本控制。
解决:Compose 将所有配置写入 docker-compose.yml,清晰易读,可提交到 Git 进行版本管理。
解决“容器编排与依赖顺序”
- 问题:应用启动通常有顺序要求(如:必须先启动数据库,再启动 Web 服务)。手动启动需要人为判断或编写复杂的脚本等待。
- 解决:Compose 支持
depends_on字段,自动处理启动顺序。同时也通过 Health Check 机制确保服务真正就绪后再启动依赖服务。
解决“网络与环境的隔离与共享”
- 问题:手动
docker run创建的容器网络配置繁琐,服务间通过--link连接已过时,且容易冲突。 - 解决:Compose 默认为项目创建一个隔离的网络,服务名称即为 DNS 主机名,容器内直接通过服务名(如
mysql)访问对方,无需关心 IP 地址。
docker-compose.yml 结构示例
docker-compose.yml 文件常用配置有哪些?(services、networks、volumes、environment)
一句话原理
docker-compose.yml 文件采用声明式语法,核心结构包含 services(定义服务容器)、networks(定义网络拓扑)、volumes(定义数据持久化),通过 environment 和 image/build 等子参数填充具体配置。
一句话源码
在 version: '3.8' 下定义 services 列表,为每个服务指定 image 或 build,配置 ports 映射端口、volumes 挂载数据、environment 注入变量。
一句话项目/场景
在“部署 WordPress 博客系统”时,定义 wordpress 和 mysql 两个服务,通过 volumes 持久化数据库文章,通过 networks 实现互联,通过 environment 配置数据库密码。
详细配置清单与解释
顶级配置项
文件最外层的三个核心关键字。
- version:指定 Compose 文件格式版本(如 ‘3.8’),不同版本支持的功能略有差异。
- services:定义应用的一组容器(这是必填项)。
- networks:定义自定义网络,默认 Docker 会为每个 Compose 项目创建一个
default网络。 - volumes:定义数据卷,便于跨服务共享或持久化。
Services 常用配置(核心)
这是配置最密集的区域,定义了容器的具体属性。
| 配置项 | 作用 | 示例 |
|---|---|---|
| image | 指定基础镜像 | image: nginx:latest |
| build | 指定 Dockerfile 路径进行构建 | build: ./dir 或 context: . |
| container_name | 指定容器名称 | container_name: my-web |
| ports | 端口映射(宿主机:容器) | - "8080:80" |
| volumes | 挂载卷(宿主机路径或卷名:容器路径) | - ./html:/usr/share/nginx/html |
| environment | 设置环境变量 | - MYSQL_ROOT_PASSWORD=123456 |
| env_file | 从文件读取环境变量 | - .env |
| depends_on | 定义依赖关系(启动顺序) | - db |
| networks | 加入特定网络 | - frontend-net |
| restart | 重启策略 | always (总是重启) / on-failure |
| command | 覆盖容器默认启动命令 | command: python app.py |
完整配置文件示例(实战模板)
以下是一个典型的 Web 应用架构,包含 Web 服务、数据库和网络配置:
| |
重点配置详解
environment vs env_file:
environment:适合少量、敏感(配合 Docker Secrets)或需明文展示的变量。env_file:适合变量多、且需要根据环境切换配置文件的场景,文件内容格式为KEY=VALUE。
depends_on 的局限性:
- 它只保证启动顺序,不保证应用“就绪”。例如,MySQL 进程启动了,但还没准备好接受连接,Web 服务连接可能会报错。
- 解决方案:应用层需要实现重试机制,或使用
healthcheck配合depends_on的condition: service_healthy(Compose v2/v3 部分版本支持)。
short syntax vs long syntax:
- 许多配置(如 ports, volumes)支持简写和详写。
- 简写:
- "80:80" - 详写:可以指定
mode、protocol等更多属性,适合复杂场景。
如何通过 Compose 管理多个容器的启动顺序?(depends_on)
一句话原理
depends_on 控制的是容器启动顺序,而非应用就绪状态。它确保依赖容器先启动,但不会等待依赖容器内的应用完全初始化完成(如数据库完成建表)。
一句话源码
在 docker-compose.yml 中,服务 A 配置 depends_on: [服务B],Docker Compose 会先启动服务 B,待其处于 Running 状态后再启动服务 A。
一句话项目/场景
在“Spring Boot + MySQL”架构中,配置 app depends_on: db。虽然这保证了 MySQL 容器先于 App 容器启动,但 App 启动瞬间 MySQL 可能还在初始化握手,导致 App 报错连接失败,需配合重试机制或 Healthcheck 解决。
详细解析与进阶方案
depends_on 的局限性
很多开发者误以为 depends_on 会等待数据库“准备好接受连接”,这是一个常见的误区。
- 实际情况:
db容器启动 -> 状态变为Running。- Compose 发现
db运行了,立即启动app容器。 - 此时
db内的 MySQL 进程可能还在执行初始化脚本,尚未监听 3306 端口。 app尝试连接 -> 失败。
解决方案:如何真正控制启动顺序?
为了解决上述“应用未就绪”的问题,有以下三种主流方案:
方案一:应用层重试(最推荐,解耦)
在代码或配置中实现连接重试逻辑。这是微服务架构的最佳实践,因为服务随时可能重启,重试机制是必须的。
- 实现:后端代码使用
retry库,或 Spring Boot 配置spring.datasource.hikari.connection-timeout等。 - 逻辑:连不上 -> 等待 2 秒 -> 重试 -> 直到成功。
方案二:使用 Healthcheck(最佳 Docker 实践)
结合 Docker 的健康检查机制,让 depends_on 等待容器状态变为 healthy。
- 注意:旧版 Compose (v2.x) 支持
condition: service_healthy,新版 Compose (V2 rewrite) 也支持,但语法略有不同。
| |
方案三:使用 wait-for-it 脚本(Shell 脚本控制)
在容器启动命令前,插入一个等待脚本,轮询检测目标端口是否开放。
- 原理:阻塞容器主进程,直到依赖服务端口通了才退出脚本,启动应用。
总结
- 简单依赖:只用
depends_on,适合纯启动顺序控制(如先启动日志收集器,再启动应用)。 - 强依赖(数据库):推荐使用 方案一(应用重试) 或 方案二,避免服务因依赖未就绪而崩溃。
docker-compose up 和 docker-compose start 的区别?
一句话原理
docker-compose up 是**“创建并启动”的全流程操作(包含构建镜像、创建容器、启动),而 docker-compose start 仅是“启动已存在的停止容器”**,不会创建新资源。
一句话源码
docker-compose up 对应 docker run 的组合逻辑;docker-compose start 对应 docker start 的批量操作。
一句话项目/场景
在“首次部署应用”或“代码更新后重新部署”时使用 docker-compose up;在“服务器重启后恢复服务”或“临时停止后恢复运行”时,使用 docker-compose start。
详细区别对比
| 维度 | docker-compose up | docker-compose start |
|---|---|---|
| 核心动作 | 创建 + 启动 | 仅启动 |
| 对象状态 | 适用于镜像不存在或容器未创建的情况。 | 仅适用于容器已存在但状态为 Exited 的情况。 |
| 资源创建 | 会创建容器、网络、数据卷等资源。 | 不创建任何新资源,仅改变容器状态。 |
| 配置更新 | 支持。如果配置文件变更,会重建容器。 | 不支持。配置变更后执行 start 无效,必须先 rm 或 down。 |
| 常用参数 | -d (后台运行), --build (强制重建镜像) | 无特殊参数,通常直接运行。 |
深度解析
docker-compose up 的行为逻辑
这是最常用的命令,它非常“智能”:
- 检查镜像:如果镜像不存在,自动构建或拉取。
- 检查容器:如果容器不存在,根据 YAML 配置创建。
- 检查配置:如果 YAML 文件有变动(如端口变了),会自动销毁旧容器并创建新容器。
- 启动:将容器状态置为 Running。
常见用法:
docker-compose start 的行为逻辑
这是一个“轻量级”命令,假设环境已经就绪:
- 它不会读取
docker-compose.yml中的构建指令。 - 它不会创建新容器。
- 它仅仅相当于对所有已停止的服务执行
docker start。
常见用法:
关联命令对比
为了更好地理解,可以参考这组对比:
- 创建/重建:
docker-compose up(如果容器已存在且配置没变,则等同于 start;如果配置变了,则重建)。 - 停止:
docker-compose stop(暂停服务,保留容器,类似暂停虚拟机)。 - 销毁:
docker-compose down(停止并删除容器、网络,彻底清理)。 - 恢复:
docker-compose start(仅针对已停止的容器)。
最佳实践
- 日常开发/部署:始终使用
docker-compose up -d。它能自动处理镜像更新和配置变更。 - 服务器重启后:可以用
docker-compose start(如果 Docker 服务设为自启动,通常不需要手动执行此命令),也可以直接用docker-compose up -d(它会检测到容器已存在并自动 start)。
实战场景
如何通过 Compose 设置环境变量?(environment 字段、env_file)
一句话原理
Compose 提供了两种设置环境变量的方式:environment 用于在 YAML 中直接定义少量或非敏感变量,env_file 用于从外部文件批量加载大量或环境隔离的变量配置。
一句话源码
environment 使用 KEY: VALUE 或 KEY=VALUE 格式直接声明;env_file 指定 .env 或 .env.prod 文件路径,将文件中的键值对注入容器环境。
一句话项目/场景
在“多环境部署”场景中,使用 env_file 区分开发环境(.env.dev)和生产环境(.env.prod)的数据库地址,避免修改代码;在“临时调试”时,使用 environment 快速覆盖某个配置参数。
详细配置方法
1. environment(直接声明)
适合变量较少、或者需要明文展示在 Compose 文件中的场景。
语法格式(两种写法):
- 优点:直观,一目了然。
- 缺点:敏感信息(如密码)会暴露在 Compose 文件中,不适合提交到 Git。
2. env_file(文件加载)
适合变量众多、或者需要根据环境切换配置的场景。
步骤:
- 创建环境变量文件(如
.env或config.list): - 在
docker-compose.yml中引用:
- 优点:
- 安全性:可以将
.env加入.gitignore,避免敏感信息泄露。 - 解耦:修改配置只需改文件,无需动 Compose 文件。
- 多环境:可通过
-f参数配合不同的 env 文件切换环境。
- 安全性:可以将
进阶:变量优先级与替换
1. 变量替换
这是最强大的功能。你可以从宿主机 Shell 环境或 .env 文件中读取变量,动态传递给 Compose 配置。
场景:端口不想写死,由外部决定。
操作:
在宿主机创建 .env 文件:
| |
执行 docker-compose up -d 时,Compose 会自动读取 .env 文件中的 HOST_PORT 并替换。如果宿主机环境变量中有 export HOST_PORT=9000,则宿主机环境变量优先级更高。
2. 优先级机制(由高到低)
当同一个变量在多处定义时,Docker Compose 按以下顺序解析(后面的被前面的覆盖):
- Compose 文件中的
environment定义。 - Shell 环境变量(如
export DB_PASS=123)。 .env文件(项目根目录下)。- Dockerfile 中的
ENV指令。
最佳实践总结
| 方式 | 适用场景 | 推荐做法 |
|---|---|---|
| environment | 固定配置、开关项、无需保密的参数。 | 直接写在 YAML 中。 |
| env_file | 敏感信息、大量配置。 | 使用 .env 文件并加入 .gitignore。 |
| 变量替换 | 需要动态调整的端口、IP、镜像版本。 | 配合 Shell 脚本或 CI/CD 变量使用。 |
如何扩缩容某个服务?(docker-compose up –scale)
一句话原理
使用 docker-compose up --scale <服务名>=<数量> 命令,Docker Compose 会自动增减指定服务的容器副本数量,实现水平扩缩容。
一句话源码
docker-compose up -d --scale web=5 将 web 服务扩展为 5 个实例;docker-compose up -d --scale web=1 缩减为 1 个实例。
一句话项目/场景
在“电商大促活动”开始前,通过 --scale 快速将后端 API 服务从 2 个实例扩展到 10 个,以应对即将到来的流量高峰;活动结束后缩减回 2 个以节省资源。
详细操作步骤
1. 扩容操作
假设 docker-compose.yml 中定义了一个名为 web 的服务,当前运行 1 个容器。
执行结果:
- Docker 会创建 2 个新的
web容器。 - 容器名称格式为:
项目名_web_1、项目名_web_2、项目名_web_3。
2. 缩容操作
将服务实例数减少。
执行结果:
- Docker 会停止并删除多余的容器(如
web_2、web_3),只保留web_1。
关键限制与避坑指南(必看)
直接使用 --scale 经常会遇到 “端口冲突” 问题,必须提前在配置文件中做好处理。
问题复现
如果 docker-compose.yml 中定义了固定的宿主机端口映射:
执行 docker-compose up --scale web=3 会报错:
Error: starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in use。
原因:一个宿主机端口只能绑定给一个容器,三个容器都争抢宿主机的 8080 端口必然冲突。
解决方案
方案一:不映射宿主机端口(推荐,配合负载均衡) 让 Web 服务运行在 Docker 内部网络中,不直接暴露给宿主机。通过 Nginx/HAProxy 等负载均衡器反向代理到这些容器。
此时 --scale 完全正常工作,因为不占用宿主机端口。
方案二:动态分配端口 只指定容器端口,让 Docker 自动分配宿主机的随机高位端口。
执行 --scale 后,可以使用 docker-compose ps 查看每个容器分配到的随机端口。
最佳实践架构
在实际生产中,单纯使用 docker-compose scale 比较少见,通常架构如下:
- Nginx (LB):运行 1 个实例,映射宿主机 80/443 端口。
- Web App:运行 N 个实例,不映射宿主机端口。
- Nginx 配置:将请求反向代理到
web:80(Docker 内部 DNS 会自动轮询解析到所有 web 实例)。
这样只需对内层的 Web App 服务进行 --scale,外层入口保持不变。
Compose 如何与 Docker Swarm 或 K8s 结合使用?
一句话原理 Compose 主要通过 Docker Swarm 实现“原生兼容与无缝切换”,或者通过 转换工具(如 Kompose) 将 YAML 文件转换为 Kubernetes 资源清单,实现从开发到生产的平滑过渡。
一句话源码
在 Swarm 模式下直接使用 docker stack deploy -c docker-compose.yml;在 K8s 环境下使用 kompose convert 将 Compose 文件转为 K8s 的 Deployment/Service yaml。
一句话项目/场景
在“初创团队敏捷开发”中,本地使用 docker-compose up 开发调试,生产环境直接复用同一份 YAML 文件,通过 docker stack deploy 部署到 Swarm 集群,无需编写复杂的 K8s 配置。
详细结合方式
1. 与 Docker Swarm 结合(原生集成)
这是 Docker 官方提供的最紧密的结合方式,旨在让 Compose 文件“一套配置,多处运行”。
操作流程:
- 初始化 Swarm:
docker swarm init - 部署栈:使用
docker stack deploy命令替代docker-compose up。1docker stack deploy -c docker-compose.yml my_app_stack - 管理:使用
docker stack services my_app_stack查看状态。
- 初始化 Swarm:
兼容性注意: Compose 文件中的部分配置在 Swarm 模式下会被忽略或有不同解释:
restart: always会被忽略,改用deploy.restart_policy。- 需要添加
deploy配置块来定义副本数、更新策略和资源限制。
配置示例:
2. 与 Kubernetes (K8s) 结合(转换与映射)
Compose 不是 K8s 的原生管理工具,但可以作为 K8s 的“引导程序”。
方式一:Kompose 转换工具 将 Compose 文件“翻译”成 K8s 的 YAML 文件。
- 安装:
curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-linux-amd64 -o kompose - 转换:
- 部署:
1kubectl apply -f deployment.yaml,service.yaml - 适用场景:想从 Compose 迁移到 K8s,但不想手写复杂的 K8s YAML 文件的场景。
- 安装:
方式二:Docker Desktop 集成 在安装了 Docker Desktop 的本地开发环境中,开启 Kubernetes 支持。虽然不能直接运行
docker-compose命令,但可以使用上述kompose流程快速在本地 K8s 集群验证。
对比总结:Swarm vs K8s 结合 Compose
| 维度 | Docker Swarm | Kubernetes (via Kompose) |
|---|---|---|
| 兼容性 | 原生支持,极好 | 需转换,部分高级特性不支持(如 depends_on) |
| 部署命令 | docker stack deploy | kubectl apply |
| 学习曲线 | 低(延续 Docker 命令习惯) | 高(需理解 Pod, Deployment 等概念) |
| 配置复杂度 | 低(主要复用 Compose 文件) | 高(生成后的 K8s YAML 细节很多) |
| 适用场景 | 中小规模集群、快速迭代 | 大规模生产环境、云原生架构 |
核心结论
- 如果你的目标是轻量级编排或快速上手生产环境,选择 Docker Swarm + Compose。
- 如果你的目标是云原生标准化或大规模集群迁移,使用 Kompose 将 Compose 转换为 K8s 资源作为起点,再逐步优化 K8s 配置。