别再手动敲三遍 docker run 了:学会使用 Docker Compose 多容器编排

别再手动敲三遍 docker run 了:学会使用 Docker Compose 多容器编排

摘要:跑一个 NestJS + MySQL + Redis 的项目,你得手动敲三遍 docker run,还要自己建网络让它们互通。Docker Compose 把这些步骤写进一个 YAML 文件,从此只需一条 docker compose up -d,所有服务一起上线。本文从 Compose 的核心定位出发,拆解 YAML 参数、多环境管理、与 Dockerfile 的协作,并以 Milvus 向量数据库的编排为例,展示生产级 Compose 的完整写法。

📑 目录

  • 为什么需要 Docker Compose?------ 从"一道菜"到"一桌宴席"
  • 核心认知:Compose 不是"合并镜像",而是"编排容器"
  • 安装方式:Docker Desktop 自带,Linux 需单独装
  • Compose YAML 核心参数全解
  • 实战案例:Milvus 向量数据库的 Compose 编排
  • 多环境管理:基准文件 + 环境覆盖
  • 书写规范与最佳实践
  • 互动讨论

为什么需要 Docker Compose?------ 从"一道菜"到"一桌宴席"

在之前的 Docker 学习中,你已经熟悉了 docker run 命令------它像"开火炒一道菜",启动一个容器。但在真实项目中,一个应用往往由多个服务组成:

  • 后端 API(NestJS / Express)
  • 数据库(MySQL / PostgreSQL)
  • 缓存(Redis)
  • 消息队列(RabbitMQ / Kafka)
  • 向量数据库(Milvus)

如果每个服务都手动敲一遍 docker run,还要自己创建网络让它们互通、管理数据卷、控制启动顺序......不仅繁琐,而且容易出错。

Docker Compose 就是来解决这个问题的。 它把"整桌宴席的配餐单"写进一个 YAML 文件,从此只需一条命令,所有服务一起启动、协同工作。

类比

  • Dockerfile 是"预制菜制作说明书"(做镜像)
  • docker run 是"开火炒一道菜"(跑容器)
  • Docker Compose 是"整桌宴席的配餐单"------每道菜独立装盘,但一起上桌

核心认知:Compose 不是"合并镜像",而是"编排容器"

这是最容易踩的坑。

误区 :以为 Compose 是把多个镜像"塞进"一个容器里。

真相一个容器只能跑一个镜像的运行时实例 ------这是 Docker 的物理边界,无法突破。Compose 的真正作用是 "一次性管理多个容器" ,让它们共享网络、数据卷,协同工作。

正确表述:Compose = 一次性拉起一组容器,让它们共享网络、数据卷,协同工作。

生活中的类比

想象你要办一桌宴席:

角色 Docker 对应物
每道菜 一个容器
每道菜的菜谱 一个镜像
上菜顺序 depends_on 启动顺序
共用调味料 数据卷共享
服务员传菜 网络互通
整桌宴席的配餐单 docker-compose.yml

安装方式:Docker Desktop 自带,Linux 需单独装

误区 :以为 Compose 总是随 Docker 一起安装。

真相:取决于安装方式。

场景 是否自带 Compose 命令
Windows/macOS + Docker Desktop ✅ 自带,无需额外安装 docker compose
Linux + Docker Engine ❌ 需单独安装(推荐插件方式) docker compose(新版)或 docker-compose(旧版)

Linux 插件安装命令(Ubuntu):

bash

csharp 复制代码
sudo apt-get update
sudo apt-get install docker-compose-plugin
docker compose version   # 验证

注意:新版的 Compose 命令是 docker compose(中间无横杠),旧版是 docker-compose。推荐使用新版。

Compose YAML 核心参数全解

一个标准的 docker-compose.yml 文件包含以下核心部分:

yaml

yaml 复制代码
version: '3.8'

services:
  # 服务定义
  backend:
    build: ...
    image: ...
    ports: ...
    environment: ...
    volumes: ...
    depends_on: ...
    restart: ...
    healthcheck: ...
    networks: ...

volumes:
  # 数据卷定义

networks:
  # 网络定义

关键参数速查表

参数 作用 关键注意
build 现场构建镜像 需指定 context(构建上下文路径)和 dockerfile
image 直接拉取或指定镜像 可与 build 共存,为构建产物打标签
ports 端口映射 "宿主机:容器" 生产环境建议去掉或改成 "80:3000"
volumes 挂载数据卷或目录 开发用 bind mount,生产用 named volume
environment 设置环境变量 敏感信息用 .env 引用,不要硬编码
depends_on 声明启动顺序依赖 仅保证顺序,不保证服务就绪;需配合 healthcheck
restart 重启策略 生产常用 unless-stopped
healthcheck 健康检查 让 Compose 知道容器"真的"可用了
container_name 固定容器名 ⚠️ 固定后无法扩展(scale),慎用
networks 指定容器加入的网络 默认会创建并加入 default 网络,服务名可互相访问

区分 imagebuild

yaml

yaml 复制代码
# 方式一:直接拉取现成镜像
mysql:
  image: mysql:8.0

# 方式二:现场构建
backend:
  build:
    context: ./backend
    dockerfile: Dockerfile
    args:
      NODE_ENV: production

# 方式三:两者结合(构建后打标签)
backend:
  build: ./backend
  image: my-nest-app:latest

depends_on + healthcheck:真正的就绪检查

默认的 depends_on 只保证容器进程启动了,不代表服务已经准备好接受请求(比如 MySQL 需要几秒初始化)。正确的做法是配合 healthcheck

yaml

yaml 复制代码
mysql:
  image: mysql:8.0
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 10s
    timeout: 5s
    retries: 5

backend:
  depends_on:
    mysql:
      condition: service_healthy

实战案例:Milvus 向量数据库的 Compose 编排

项目中提供的 milvus-standalone-docker-compose.yml 是一个真实的生产级编排文件,展示了多服务协同的完整写法。

文件解读

yaml

bash 复制代码
version: '3.5'

services:
  etcd:
    container_name: milvus-etcd
    image: quay.io/coreos/etcd:v3.5.25
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_SNAPSHOT_COUNT=50000
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
    command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
    healthcheck:
      test: ["CMD", "etcdctl", "endpoint", "health"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio:
    container_name: milvus-minio
    image: minio/minio:RELEASE.2024-05-28T17-19-04Z
    environment:
      MINIO_ACCESS_KEY: minioadmin
      MINIO_SECRET_KEY: minioadmin
    ports:
      - "9001:9001"
      - "9000:9000"
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 30s
      timeout: 20s
      retries: 3

  standalone:
    container_name: milvus-standalone
    image: milvusdb/milvus:v3.0.0
    command: ["milvus", "run", "standalone"]
    security_opt:
    - seccomp:unconfined
    environment:
      MINIO_REGION: us-east-1
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      interval: 30s
      start_period: 90s
      timeout: 20s
      retries: 3
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - "etcd"
      - "minio"

networks:
  default:
    name: milvus

架构分析

这个 Compose 编排了三个服务:

服务 角色 说明
etcd 元数据存储 分布式键值存储,Milvus 用它保存元数据
minio 对象存储 S3 兼容存储,Milvus 用它保存向量数据文件
standalone Milvus 主服务 向量数据库服务,依赖 etcd 和 minio

关键设计亮点

  1. 数据持久化 :三个服务都挂载了 named volumes(通过 ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/*),确保容器重启数据不丢失
  2. 健康检查 :每个服务都配置了 healthcheckstandalone 还设置了 start_period: 90s(给 Milvus 启动留出足够时间)
  3. 网络隔离 :所有服务加入 milvus 网络,通过服务名互相访问(如 ETCD_ENDPOINTS: etcd:2379
  4. 依赖管理standalonedepends_on 显式声明依赖 etcd 和 minio

启动命令

bash

bash 复制代码
docker compose -f ./milvus-standalone-docker-compose.yml up -d

启动后,Milvus 服务暴露在宿主机的 19530 端口,可供应用连接。在 index.mjs 中,连接地址就是 192.168.2.147:19530

多环境管理:基准文件 + 环境覆盖

误区 :为开发、测试、生产维护三份完全独立的 docker-compose.yml

纠正:用"基准文件 + 覆盖文件"的方式,一份通用配置 + 每环境专属差异配置。

标准做法

bash

bash 复制代码
# 开发环境(自动加载 docker-compose.override.yml,无需 -f)
docker compose up -d

# 生产环境(显式指定叠加文件)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

文件分工

文件 作用 是否提交 Git
docker-compose.yml 基准文件,存放所有环境通用的配置 ✅ 提交
docker-compose.override.yml 开发环境覆盖(挂载本地代码、开启调试) ❌ 不提交
docker-compose.prod.yml 生产环境覆盖(资源限制、关闭调试工具) ✅ 提交

备选方案:Profiles

在同一份文件中用 profiles 属性给服务打标签,启动时按需启用:

yaml

yaml 复制代码
services:
  debug-tool:
    profiles: ["debug"]
    image: my-debug-image

bash

css 复制代码
docker compose --profile debug up -d

书写规范与最佳实践

  1. 缩进 :YAML 强制使用空格(2 个空格是社区惯例),禁止使用 Tab。
  2. 敏感信息 :永远用 ${VAR} 引用 .env 文件中的变量,且 .env 必须加入 .gitignore
  3. 容器名 :除非有特殊原因,否则不写 container_name,让 Compose 自动生成,以便后期扩展(如 docker compose scale)。
  4. 依赖检查depends_on 默认只等容器启动,不等服务就绪。正确做法:为数据库配置 healthcheck,然后用 condition: service_healthy
  5. 区分挂载方式 :开发用 ./code:/app(bind mount),生产用 data-volume:/app/data(named volume)。
  6. 端口映射:开发环境可暴露端口,生产环境建议只暴露网关(如 Nginx)的端口,内部服务通过网络互相访问。

互动讨论

💬 Compose 是把多个镜像合并到一个容器里吗?

不是。一个容器只能运行一个镜像的实例。Compose 的真正作用是管理一组容器,让它们共享网络、数据卷,协同工作。

💬 docker compose updocker run 有什么区别?

docker run 启动单个容器,docker compose up 根据 YAML 文件启动一组容器,并自动创建网络、挂载数据卷、处理依赖顺序。Compose 是 docker run 的"批量自动化版本"。

💬 depends_on 能保证服务完全就绪吗?

不能。depends_on 只保证容器进程启动了,不代表服务已经准备好接受请求。要确保服务就绪,必须配合 healthcheck,使用 condition: service_healthy

💬 开发环境和生产环境怎么区分配置?

使用"基准文件 + 覆盖文件"的方式。docker-compose.yml 存放通用配置,docker-compose.override.yml 存放开发环境覆盖(自动加载),docker-compose.prod.yml 存放生产环境覆盖(手动指定 -f 叠加)。

💬 Compose 文件中的 container_name 该不该写?

除非有特殊原因(比如需要固定名称供其他工具引用),否则不建议写 。固定名称后无法使用 docker compose scale 水平扩展,限制了部署灵活性。

相关推荐
xixiaoyunya2 小时前
Docker 容器化部署实战:从零搭建一套完整的 Nginx + Node.js + MySQL + Redis 项目环境
nginx·docker·node.js
全栈攻略14 小时前
Docker 中 ROS2 工作流 Topic 验证与常用命令指南
运维·docker·容器
名字还没想好☜16 小时前
Docker 数据卷实战:volume、bind mount、tmpfs 到底怎么选,数据持久化与权限坑
运维·docker·容器·kubernetes
唐青枫17 小时前
Docker diff 详解:看清容器里到底改了什么
docker
lpfasd12318 小时前
Docker存储清理与防膨胀实践
运维·docker·容器
weixin_4445793019 小时前
Docker(下):镜像仓库管理
运维·docker·容器
DreamLife☼20 小时前
Agent开发环境搭建完全指南
python·docker·typescript·node·工业知识点
探索云原生20 小时前
Kueue + HAMi vGPU 实战:显存与算力配额管理
docker·ai·云原生·kubernetes·gpu
hoho_1221 小时前
docker中升级mysql到最新版本
mysql·docker·容器