DockerCompose基础

总结摘要
DockerCompose基础

基础使用

Docker Compose 的作用是什么?解决了什么问题?

一句话原理 Docker Compose 是一个用于定义和运行多容器应用的工具,通过一个 YAML 文件(docker-compose.yml)将应用的架构、网络和数据卷配置“代码化”,实现一键构建、启动和销毁。

一句话源码 使用 docker-compose up -d 一键启动复杂架构,替代了原本需要编写数十行 Shell 脚本或冗长的 docker run 命令。

一句话项目/场景 在“微服务开发环境搭建”中,需要同时启动 API 服务、MySQL 数据库、Redis 缓存和 RabbitMQ 消息队列,使用 Compose 可以一键拉起整套环境,团队成员无需手动配置每个组件的 IP 和端口映射。


详细解析:解决了什么问题?

解决“命令行参数地狱”

如果不使用 Compose,启动一个典型的 Web 应用可能需要执行如下命令:

1
2
3
4
5
6
7
8
# 启动数据库
docker run -d --name db -v db-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=pass mysql

# 启动缓存
docker run -d --name cache redis

# 启动 Web 服务(需要连接 db 和 cache,还要映射端口)
docker run -d --name web -p 8080:80 --link db:db --link cache:cache -v /app/src:/var/www/html myweb

问题:命令冗长难记、参数容易写错、无法版本控制。 解决:Compose 将所有配置写入 docker-compose.yml,清晰易读,可提交到 Git 进行版本管理。

解决“容器编排与依赖顺序”

  • 问题:应用启动通常有顺序要求(如:必须先启动数据库,再启动 Web 服务)。手动启动需要人为判断或编写复杂的脚本等待。
  • 解决:Compose 支持 depends_on 字段,自动处理启动顺序。同时也通过 Health Check 机制确保服务真正就绪后再启动依赖服务。

解决“网络与环境的隔离与共享”

  • 问题:手动 docker run 创建的容器网络配置繁琐,服务间通过 --link 连接已过时,且容易冲突。
  • 解决:Compose 默认为项目创建一个隔离的网络,服务名称即为 DNS 主机名,容器内直接通过服务名(如 mysql)访问对方,无需关心 IP 地址。

docker-compose.yml 结构示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: '3.8'
services:
  # 服务 1:Web 前端
  web:
    build: .
    ports:
      - "5000:5000"
    depends_on:
      - redis  # 依赖 redis,确保 redis 先启动
  
  # 服务 2:Redis 数据库
  redis:
    image: "redis:alpine"
    volumes:
      - redis-data:/data

volumes:
  redis-data:  # 定义数据卷

docker-compose.yml 文件常用配置有哪些?(services、networks、volumes、environment)

一句话原理 docker-compose.yml 文件采用声明式语法,核心结构包含 services(定义服务容器)、networks(定义网络拓扑)、volumes(定义数据持久化),通过 environmentimage/build 等子参数填充具体配置。

一句话源码version: '3.8' 下定义 services 列表,为每个服务指定 imagebuild,配置 ports 映射端口、volumes 挂载数据、environment 注入变量。

一句话项目/场景 在“部署 WordPress 博客系统”时,定义 wordpressmysql 两个服务,通过 volumes 持久化数据库文章,通过 networks 实现互联,通过 environment 配置数据库密码。


详细配置清单与解释

顶级配置项

文件最外层的三个核心关键字。

  • version:指定 Compose 文件格式版本(如 ‘3.8’),不同版本支持的功能略有差异。
  • services:定义应用的一组容器(这是必填项)。
  • networks:定义自定义网络,默认 Docker 会为每个 Compose 项目创建一个 default 网络。
  • volumes:定义数据卷,便于跨服务共享或持久化。

Services 常用配置(核心)

这是配置最密集的区域,定义了容器的具体属性。

配置项作用示例
image指定基础镜像image: nginx:latest
build指定 Dockerfile 路径进行构建build: ./dircontext: .
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 服务、数据库和网络配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
version: '3.8'

services:
  # --- Web 服务 ---
  webapp:
    image: python:3.9-slim
    container_name: my-python-app
    working_dir: /app
    command: python main.py
    ports:
      - "5000:5000"
    volumes:
      - ./code:/app          # Bind Mount:挂载本地代码
      - app-data:/data       # Named Volume:持久化数据
    environment:
      - DEBUG=True
      - DB_HOST=db           # 关键:这里直接用服务名 'db' 作为主机名
    depends_on:
      - db                   # 确保 db 先启动
    networks:
      - app-net

  # --- 数据库服务 ---
  db:
    image: mysql:5.7
    container_name: my-mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: mydb
    volumes:
      - db-data:/var/lib/mysql # 持久化数据库文件
    networks:
      - app-net

# --- 定义数据卷 ---
volumes:
  app-data:  # 声明式定义,Docker 会自动创建
  db-data:

# --- 定义网络 ---
networks:
  app-net:   # 自定义网络,webapp 和 db 在同一网段
    driver: bridge

重点配置详解

  1. environment vs env_file

    • environment:适合少量、敏感(配合 Docker Secrets)或需明文展示的变量。
    • env_file:适合变量多、且需要根据环境切换配置文件的场景,文件内容格式为 KEY=VALUE
  2. depends_on 的局限性

    • 它只保证启动顺序,不保证应用“就绪”。例如,MySQL 进程启动了,但还没准备好接受连接,Web 服务连接可能会报错。
    • 解决方案:应用层需要实现重试机制,或使用 healthcheck 配合 depends_oncondition: service_healthy(Compose v2/v3 部分版本支持)。
  3. short syntax vs long syntax

    • 许多配置(如 ports, volumes)支持简写和详写。
    • 简写:- "80:80"
    • 详写:可以指定 modeprotocol 等更多属性,适合复杂场景。

如何通过 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 会等待数据库“准备好接受连接”,这是一个常见的误区。

  • 实际情况
    1. db 容器启动 -> 状态变为 Running
    2. Compose 发现 db 运行了,立即启动 app 容器。
    3. 此时 db 内的 MySQL 进程可能还在执行初始化脚本,尚未监听 3306 端口。
    4. 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) 也支持,但语法略有不同。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
version: '3.8'

services:
  web:
    image: myapp
    depends_on:
      db:
        condition: service_healthy # 关键:等待 db 变为健康状态
    restart: on-failure

  db:
    image: mysql:5.7
    healthcheck:                  # 定义如何判断数据库“就绪”
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
方案三:使用 wait-for-it 脚本(Shell 脚本控制)

在容器启动命令前,插入一个等待脚本,轮询检测目标端口是否开放。

  • 原理:阻塞容器主进程,直到依赖服务端口通了才退出脚本,启动应用。
1
2
3
4
5
6
services:
  web:
    image: myapp
    command: ["./wait-for-it.sh", "db:3306", "--", "python", "app.py"]
    depends_on:
      - db

总结

  • 简单依赖:只用 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 updocker-compose start
核心动作创建 + 启动仅启动
对象状态适用于镜像不存在或容器未创建的情况。仅适用于容器已存在但状态为 Exited 的情况。
资源创建会创建容器、网络、数据卷等资源。不创建任何新资源,仅改变容器状态。
配置更新支持。如果配置文件变更,会重建容器。不支持。配置变更后执行 start 无效,必须先 rm 或 down。
常用参数-d (后台运行), --build (强制重建镜像)无特殊参数,通常直接运行。

深度解析

docker-compose up 的行为逻辑

这是最常用的命令,它非常“智能”:

  1. 检查镜像:如果镜像不存在,自动构建或拉取。
  2. 检查容器:如果容器不存在,根据 YAML 配置创建。
  3. 检查配置:如果 YAML 文件有变动(如端口变了),会自动销毁旧容器并创建新容器。
  4. 启动:将容器状态置为 Running。

常见用法

1
2
# 首次部署或更新部署(后台运行)
docker-compose up -d

docker-compose start 的行为逻辑

这是一个“轻量级”命令,假设环境已经就绪:

  1. 它不会读取 docker-compose.yml 中的构建指令。
  2. 它不会创建新容器。
  3. 它仅仅相当于对所有已停止的服务执行 docker start

常见用法

1
2
# 假设你之前用 docker-compose stop 停止了服务,现在想恢复
docker-compose 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: VALUEKEY=VALUE 格式直接声明;env_file 指定 .env.env.prod 文件路径,将文件中的键值对注入容器环境。

一句话项目/场景 在“多环境部署”场景中,使用 env_file 区分开发环境(.env.dev)和生产环境(.env.prod)的数据库地址,避免修改代码;在“临时调试”时,使用 environment 快速覆盖某个配置参数。


详细配置方法

1. environment(直接声明)

适合变量较少、或者需要明文展示在 Compose 文件中的场景。

语法格式(两种写法):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
services:
  web:
    image: nginx
    environment:
      # 写法一:字典格式(YAML 风格,推荐,可读性好)
      DEBUG: "true"
      DB_HOST: "192.168.1.100"
      
      # 写法二:字符串格式(Shell 风格)
      - DB_PORT=3306
      - API_KEY=abcdefg
  • 优点:直观,一目了然。
  • 缺点:敏感信息(如密码)会暴露在 Compose 文件中,不适合提交到 Git。

2. env_file(文件加载)

适合变量众多、或者需要根据环境切换配置的场景。

步骤:

  1. 创建环境变量文件(如 .envconfig.list):
    1
    2
    3
    
    # .env 文件内容
    MYSQL_ROOT_PASSWORD=secret_pass
    MYSQL_DATABASE=mydb
  2. docker-compose.yml 中引用:
    1
    2
    3
    4
    5
    6
    
    services:
      db:
        image: mysql
        env_file:
          - .env             # 引用当前目录下的 .env 文件
          - ./config/app.env # 也可以指定路径
  • 优点
    • 安全性:可以将 .env 加入 .gitignore,避免敏感信息泄露。
    • 解耦:修改配置只需改文件,无需动 Compose 文件。
    • 多环境:可通过 -f 参数配合不同的 env 文件切换环境。

进阶:变量优先级与替换

1. 变量替换

这是最强大的功能。你可以从宿主机 Shell 环境.env 文件中读取变量,动态传递给 Compose 配置。

场景:端口不想写死,由外部决定。

1
2
3
4
5
6
# docker-compose.yml
services:
  web:
    image: nginx
    ports:
      - "${HOST_PORT}:80"  # 引用外部变量

操作: 在宿主机创建 .env 文件:

1
HOST_PORT=8080

执行 docker-compose up -d 时,Compose 会自动读取 .env 文件中的 HOST_PORT 并替换。如果宿主机环境变量中有 export HOST_PORT=9000,则宿主机环境变量优先级更高

2. 优先级机制(由高到低)

当同一个变量在多处定义时,Docker Compose 按以下顺序解析(后面的被前面的覆盖):

  1. Compose 文件中的 environment 定义。
  2. Shell 环境变量(如 export DB_PASS=123)。
  3. .env 文件(项目根目录下)。
  4. 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 个容器。

1
2
# 将 web 服务扩展到 3 个实例
docker-compose up -d --scale web=3

执行结果

  • Docker 会创建 2 个新的 web 容器。
  • 容器名称格式为:项目名_web_1项目名_web_2项目名_web_3

2. 缩容操作

将服务实例数减少。

1
2
# 将 web 服务缩减到 1 个实例
docker-compose up -d --scale web=1

执行结果

  • Docker 会停止并删除多余的容器(如 web_2web_3),只保留 web_1

关键限制与避坑指南(必看)

直接使用 --scale 经常会遇到 “端口冲突” 问题,必须提前在配置文件中做好处理。

问题复现

如果 docker-compose.yml 中定义了固定的宿主机端口映射:

1
2
3
4
5
services:
  web:
    image: nginx
    ports:
      - "8080:80"  # 宿主机 8080 端口固定映射到容器的 80 端口

执行 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 等负载均衡器反向代理到这些容器。

1
2
3
4
5
6
services:
  web:
    image: my-api
    # 不要写 ports 映射,或者只映射容器端口
    expose:
      - "80"  # 仅暴露给 Docker 内部网络

此时 --scale 完全正常工作,因为不占用宿主机端口。

方案二:动态分配端口 只指定容器端口,让 Docker 自动分配宿主机的随机高位端口。

1
2
3
4
5
services:
  web:
    image: nginx
    ports:
      - "80"  # 宿主机随机端口(如 32768) -> 容器 80

执行 --scale 后,可以使用 docker-compose ps 查看每个容器分配到的随机端口。

最佳实践架构

在实际生产中,单纯使用 docker-compose scale 比较少见,通常架构如下:

  1. Nginx (LB):运行 1 个实例,映射宿主机 80/443 端口。
  2. Web App:运行 N 个实例,不映射宿主机端口。
  3. 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 文件“一套配置,多处运行”。

  • 操作流程

    1. 初始化 Swarmdocker swarm init
    2. 部署栈:使用 docker stack deploy 命令替代 docker-compose up
      1
      
      docker stack deploy -c docker-compose.yml my_app_stack
    3. 管理:使用 docker stack services my_app_stack 查看状态。
  • 兼容性注意: Compose 文件中的部分配置在 Swarm 模式下会被忽略或有不同解释:

    • restart: always 会被忽略,改用 deploy.restart_policy
    • 需要添加 deploy 配置块来定义副本数、更新策略和资源限制。
  • 配置示例

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    version: '3.8'
    services:
      web:
        image: nginx
        deploy:
          replicas: 3          # Swarm 特有:定义副本数
          update_config:
            parallelism: 1     # 滚动更新配置
          resources:
            limits:
              cpus: '0.5'

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
    • 转换
      1
      2
      
      kompose convert -f docker-compose.yml
      # 会生成 deployment.yaml, service.yaml 等文件
    • 部署
      1
      
      kubectl apply -f deployment.yaml,service.yaml
    • 适用场景:想从 Compose 迁移到 K8s,但不想手写复杂的 K8s YAML 文件的场景。
  • 方式二:Docker Desktop 集成 在安装了 Docker Desktop 的本地开发环境中,开启 Kubernetes 支持。虽然不能直接运行 docker-compose 命令,但可以使用上述 kompose 流程快速在本地 K8s 集群验证。

对比总结:Swarm vs K8s 结合 Compose

维度Docker SwarmKubernetes (via Kompose)
兼容性原生支持,极好需转换,部分高级特性不支持(如 depends_on)
部署命令docker stack deploykubectl apply
学习曲线低(延续 Docker 命令习惯)高(需理解 Pod, Deployment 等概念)
配置复杂度低(主要复用 Compose 文件)高(生成后的 K8s YAML 细节很多)
适用场景中小规模集群、快速迭代大规模生产环境、云原生架构

核心结论

  • 如果你的目标是轻量级编排或快速上手生产环境,选择 Docker Swarm + Compose
  • 如果你的目标是云原生标准化或大规模集群迁移,使用 Kompose 将 Compose 转换为 K8s 资源作为起点,再逐步优化 K8s 配置。