Docker Compose 云原生的底座

一、简介

1-1、核心用途

Docker Compose 是 单台服务器 的 多容器 编排工具 ,专门用来定义、管理一台主机上一组相互关联的容器服务。

把应用的多个组件(前端、后端API、MySQL、Redis、MQ、Nginx等)写在一份 compose.yaml 配置文件里,一条命令完成整套应用的启动、停止、重启、日志查看、扩容、销毁。

定位:单机编排,不跨机器 。适合开发环境、测试环境、中小型公司单机生产部署;

它不替代K8s(K8s是多节点集群编排,太重,中小项目没必要)。

1-2、解决的痛点

只用 docker run 的致命问题:很难留痕,维护灾难。

很多新手直接写一堆 docker run 命令启动容器,最大痛点:配置无持久化留痕:

  1. 启动参数散落在命令行,没有版本记录
    docker run 的所有配置(端口映射、环境变量、挂载目录、网络、内存限制、容器名称、重启策略)全部写在shell命令里。容器删掉之后,你根本记不全当初启动加了哪些参数。

例子:docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=xxx -v /data/mysql:/var/lib/mysql --restart=always mysql:8.0

一旦容器误删、服务器重装,你要靠记忆复现整条命令,很容易漏掉环境变量、挂载目录,直接丢数据或者服务起不来。

  1. 多人协作时无法同步服务配置

    团队其他人不知道完整启动参数。A开发启动数据库加了特殊环境变量,B不知道,自己手动docker run,环境不一致,本地能跑、服务器报错。

  2. 多容器之间网络、依赖手动管理,极易出错

    多个服务(后端+Redis+MySQL),docker run 需要手动创建自定义网络,手动指定--network,还要人为控制启动顺序;每次部署都要敲多条命令,顺序错就启动失败。

  3. 不好做版本管理、变更审计

    docker run 命令没法提交到Git。修改容器配置没有记录,谁改了什么参数完全无迹可寻,线上出问题很难回溯。

  4. 批量运维麻烦

    启停整套应用,需要逐个docker stop、docker start;扩容、重建、查看日志要逐个容器操作。

一句话总结:docker run 是"临时手工启动",配置只存在当前终端历史里;一旦容器删除或者机器重建,配置就丢了,留不住完整环境定义。

1-3、为什么中小型公司普遍选择 Docker Compose

中小型项目(单体/少量微服务,单台或2~3台独立服务器),几乎不会上K8s:K8s学习成本高、资源开销大、运维复杂度高,需要专人维护,小团队扛不住。而 Compose 刚好匹配需求:

  1. 所有服务配置全部写在 compose.yaml,文件存入Git,永久留痕

    环境变量、挂载、端口、镜像、资源限制全部在yaml文件。

    容器删了、服务器重装,只需要拉取yaml,一条docker compose up -d就能原样恢复整套环境;所有变更可以看git提交记录,谁修改、什么时候改一目了然,解决docker run无留痕痛点。

  2. 一键启停整套服务

    up启动、down销毁整套服务,logs看全部服务日志,restart重启,不用逐个容器操作。

  3. 自动处理网络

    Compose会自动创建项目隔离网络,同项目内容器直接用服务名互相访问(后端直接连接mysql:3306),不用手动docker network create。

  4. 开发环境一致性(本地开发 + 测试机 + 轻量生产复用同一份配置)

    开发、测试、生产共用一套compose模板,通过.env文件区分不同环境变量,保证环境对齐,减少"我本地能跑,服务器不行"。

  5. 上手简单,运维成本极低

    只需要学会yaml基础和几条compose命令,普通后端开发就能维护,不需要专职容器运维。

  6. 支持多文件拆分、环境分组(profiles/include)

    可以把数据库、中间件拆成独立文件;开发环境启用调试服务,生产环境只启用核心业务服务。

二、历史版本 介绍

区分两个概念:

  • Compose CLI版本(v1/v2)
  • compose.yaml 文件格式版本(2.x/3.x/Compose Spec)

下面说的是CLI工具版本。

2-1、V1(docker-compose,中间短横线连接)

  1. 开发语言 :Python,独立项目,有一堆Python依赖包
  2. 发布时间 :2014年,最终版 1.29.2(2021-05),不再更新
  3. 维护状态 :EOL,2023-07起彻底停止维护,无安全补丁、无bug修复,不再随新版Docker Desktop打包
  4. 架构:独立二进制程序,和Docker CLI分开调用。
  5. 特点
    • 命令:docker-compose up
    • yaml文件顶层必须写 version: "3.8" 这类版本声明,用来指定旧文件格式
    • 容器命名:project_service_1(下划线分隔)
    • 性能弱:Python解释器开销,启动慢
    • 缺少现代能力:不支持 profiles、include、watch(热重载)、BuildKit完整集成、完善GPU支持
    • 兼容性:只能解析旧2.x、3.x文件格式
  6. 生产建议 :严禁生产使用 ,存在安全漏洞风险,官方不再支持。旧脚本可以临时用 --compatibility 模式在V2上模拟V1行为。

2-2、V2(docker compose,中间空格连接,Docker CLI插件)

  1. 开发语言 :Golang,和Docker Engine/Moby同语言,复用Docker底层代码库(compose-go)
  2. 发布时间 :2020发布,2022 GA,持续活跃开发(后续还有v5版本,CLI命令不变,主要新增Go SDK)
  3. 维护状态 :官方主力维护,安全更新、bug修复、新特性持续迭代 ,现代Docker Engine默认自带插件包 docker-compose-plugin
  4. 架构:Docker CLI插件,作为docker子命令,不是独立程序。
  5. 特点
    • 命令:docker compose up
    • yaml顶层 version: 字段直接忽略 ,统一遵循 Compose Specification(Compose规范),不再区分2.x/3.x文件版本
    • 容器命名:project-service-1(横杠,符合DNS命名规范;加--compatibility切回下划线)
    • 性能:Go原生编译,启动速度相比V1快2~3倍,BuildKit原生集成,镜像构建更快
    • 新增能力:profiles(环境分组)、include(拆分多compose文件)、watch开发热重载、GPU设备支持、云上下文(ECS/ACI)部署、更好的secret/config支持
    • 跨平台:Linux/Windows/macOS(含Apple Silicon)原生支持,无Python依赖
  6. 生产推荐版本
    • 生产环境:使用随Docker Engine配套的 docker-compose-plugin(V2稳定版),跟随docker引擎版本一起升级,不要单独装独立二进制。
    • 版本选择策略:优先使用Docker官方源自带的版本,不要追最新预览版;稳定基线推荐 v2.20+,当前主流稳定 v2.24 ~ v2.30。
    • 安装包名(Debian/Ubuntu):docker-compose-plugin;不要安装旧包 docker-compose(V1)。

核心对比简表

项目 V1 docker-compose V2 docker compose
语言 Python Go
命令 docker-compose(横线) docker compose(空格)
维护 EOL,停止维护 持续官方维护
yaml version字段 必须写,用来指定格式 忽略,统一Compose Spec
容器名称分隔符 _ 下划线 - 横杠(可兼容模式切换)
性能 慢,Python开销 快,原生Go
新特性 冻结,不再新增 profiles/include/watch/BuildKit/GPU
生产环境 ❌ 不推荐 ✅ 官方唯一推荐

版本查看:

复制代码
[root@VM-0-14-opencloudos ~]# docker-compose --version
Docker Compose version 5.4.0

# Docker CLI插件 插件的形式查看版本信息
[root@VM-0-14-opencloudos ~]# docker compose version
Docker Compose version 5.4.0

Docker Compose 5.4.0 = 新版V2(Go语言)稳定正式版,生产完全可用

版本号跳到5.x只是为了避开旧yaml 2.x/3.x文件格式的名字混淆 ,

CLI能力、底层都是V2一脉相承,新增Go SDK,功能上等价V2,官方持续维护。

  • 发布时间:2026-08-03,官方稳定Release,不是预览版,有安全和bug维护,后续有5.5、5.6迭代
  • yaml不用写version: "3.x",写了也会被忽略,统一Compose Spec规范。
  • 缺点:属于较新分支,极少数边缘场景(复杂多include、大量网络定义)可能遇到资源规划逻辑新bug;如果你的业务极度保守,可锁定在v2.24~v2.30

2-3、迁移小提示

  1. 旧yaml文件不需要大规模重写,V2可以直接读取旧2.x/3.x compose文件
  2. 脚本中把 docker-compose → docker compose,或者配置alias alias docker-compose="docker compose"
  3. 如果依赖容器名下划线,启动时加 --compatibility 或者环境变量 COMPOSE_COMPATIBILITY=true

三、CLI 常用参数

说明文章采用 Docker Compose V2 ,命令格式:docker compose [COMMAND] [OPTIONS]。

3.1 全局通用参数(所有子命令都可使用)

全局参数写在 compose 之后、子命令之前,控制项目基础行为。

-f, --file FILE 指定 compose 配置文件

作用:指定 compose yaml 文件,支持多文件合并(覆盖/叠加配置),多 -f 时后面文件会合并覆盖前面 。

默认:优先读取 compose.yml > compose.yaml > docker-compose.yml(向后兼容)。

示例:

bash 复制代码
# 指定自定义文件名
docker compose -f app.yml up -d

# 多文件合并:基础配置 + 开发覆盖配置
docker compose -f base.yml -f dev.override.yml up -d

原理:多个yaml合并,后文件的同key会覆盖前文件;数组会追加。常用于区分开发/生产环境。

-p, --project-name NAME 指定项目名称

作用:自定义项目名,用于隔离网络、卷、容器前缀;

默认:取yaml文件所在目录名作为项目名。

Compose 所有资源(容器、network、volume)都会带上项目名前缀。

可理解为 Compose 层 面的 业务逻辑隔离 的 命名空间

示例:

bash 复制代码
# 项目名 myblog,容器名称 myblog-web-1
docker compose -p myblog up -d

#查看 要指定 命名空间名称
docker compose -p myblog ps -a

--env-file FILE 指定环境变量文件

作用:加载自定义 .env 文件,优先级高于默认目录下 .env 。

默认:读取当前目录 .env。

示例:

bash 复制代码
# 生产环境独立env
docker compose --env-file .env.prod up -d

--profile NAME 启用配置分组(V2 新增核心特性,V1不支持)

作用:启用 yaml 中用 profiles 标记的服务,实现按需启停可选服务。

核心规则:

  1. 没有写 profiles 的服务,属于默认分组,默认一定会启动;
  2. 写了 profiles 的服务,默认不启动 ;只有命令行通过 --profile 指定对应分组名称,才会被拉起;
  3. 一个服务可以归属多个分组 profiles: ["tools", "debug"]。

compose.yaml 示例:

yaml 复制代码
services:
  # 无 profiles:默认服务,只要执行 up 就启动
  web:
    image: nginx:alpine

  # 归属 tools 分组,默认不会启动
  adminer:
    image: adminer
    profiles: ["tools"]

  # 归属 debug 分组,默认不会启动
  busybox-debug:
    image: busybox
    profiles: ["debug"]
    command: sleep 3600
场景1:不指定 --profile,只启动默认服务 web
bash 复制代码
docker compose up -d

✅ 启动:web

❌ 不启动:adminer、busybox-debug

场景2:启用 tools 分组,启动默认服务 + tools组的adminer
bash 复制代码
docker compose --profile tools up -d

✅ 启动:web、adminer

❌ 不启动:busybox-debug

场景3:同时启用多个分组,用多次 --profile
bash 复制代码
docker compose --profile tools --profile debug up -d

✅ 启动:web、adminer、busybox-debug

场景4:只启动分组内服务,不启动默认服务(加 --no-deps,极少用)
bash 复制代码
docker compose --profile tools up -d --no-deps adminer

3.2 up 子命令(最核心:创建+启动容器)

up:创建网络、卷,构建镜像,启动服务;服务存在则重启,已运行则不操作。

核心:docker compose up 会先检查当前项目内各个服务容器的当前状态,分3种情况处理:

  1. 容器不存在(从未创建,或者已经被down删除):新建容器、创建网络/命名卷、构建镜像(如果配置build),然后启动容器。
  2. 容器已经存在,但处于停止状态(exited) :执行重启容器。
  3. 容器已经存在,并且正在运行中(running) :什么都不做,不会重复创建、不会重启。
    ⚠️ 重要补充区分:

如果yaml配置文件发生修改 ,再次执行up,行为会变化:

Compose检测到容器配置(镜像、端口、环境变量、security_opt等)和现有容器不一致 → 销毁旧容器,基于新配置重建新容器,再启动 。

只有yaml完全没变,容器还在running,才会直接跳过,不操作。

-d, --detach 后台运行(守护进程)

作用:后台启动容器,不占用当前终端,不打印实时日志。生产标配。

bash 复制代码
docker compose up -d

不加 -d 会前台运行,终端直接输出所有容器日志,Ctrl+C停止所有服务。

--build 启动前强制重新构建镜像

作用:每次up前执行build,即使Dockerfile无变化;适合开发环境代码频繁修改。

bash 复制代码
docker compose up -d --build

执行流程:

  1. 强制执行构建:读取 build 上下文,重新 build 镜像(哪怕 Dockerfile、上下文文件一点没变,依然会跑一遍构建流程,生成新镜像层 / 镜像 ID)
  2. Compose 对比:当前容器使用的镜像 和 刚刚新构建的镜像 ID 不一致
  3. 动作:停止正在运行的旧容器 → 删除旧容器 → 使用新构建镜像创建新容器并启动

--force-recreate 强制重建容器

作用:无论配置是否变化,停止并删除旧容器,新建容器。

⚠️ 注意:不会重建镜像;卷如果是named volume,数据保留;匿名卷会丢失。

bash 复制代码
docker compose up -d --force-recreate

作用:忽略配置比对,强制重新创建容器。

  • 前提:yaml 完全没改动,容器正在 running
  • 执行流程:停止运行中的旧容器 → 删除旧容器 → 使用当前已存在的镜像,新建容器并启动
  • 镜像不会重新构建,镜像 ID 保持不变
  • 适用场景:单纯想重建容器(刷新容器运行环境、重置容器内临时状态),不需要重新构建镜像

重点:--force-recreate 和镜像构建无关,没有 build 动作。

组合使用:

sh 复制代码
docker compose up -d --build --force-recreate

两条规则同时生效:

  1. 强制重新构建镜像
  2. 强制重建容器(就算构建完镜像 ID 没变,依然会杀掉重建容器)

--no-build 不构建镜像,直接拉取/使用已有镜像

作用:跳过build阶段,镜像缺失直接报错。适合生产,防止意外重新构建。

bash 复制代码
docker compose up -d --no-build

--no-recreate 不重建容器,只启动停止的容器

作用:如果容器已经存在,不做重建;仅启动处于停止状态的容器。

bash 复制代码
docker compose up -d --no-recreate

--remove-orphans 清理孤立容器

作用:删除项目中在yaml里已经移除、但还存在的旧容器 。

场景:删除yaml中的某个服务后,旧容器残留,加上该参数自动清理。

bash 复制代码
docker compose up -d --remove-orphans

3.3 down 子命令(停止并删除项目资源)

down:停止容器,删除容器、默认删除网络;默认不会删除命名卷(保护业务数据)。

-v, --volumes 删除关联命名卷

⚠️ 高危!作用:删除compose定义的命名卷。会清空数据库等持久化数据,生产慎用。

bash 复制代码
# 停止服务并删除容器、网络、卷
docker compose down -v

--rmi all 删除项目使用的镜像

bash 复制代码
docker compose down --rmi all

--rmi 必须带参数:local / all,
不能单独写 --rmi
Docker

复制代码
# 语法
docker compose down --rmi local
docker compose down --rmi all
  1. --rmi local(本地构建镜像)
    只删除本项目 build 出来、没有手动自定义 tag 的镜像;
    从远程拉取的镜像(如 nginx:alpine)保留不删。
  2. --rmi all
    删除当前 compose 项目用到的全部镜像,不管是本地构建还是远程拉取。
  3. 不带任何 --rmi 时(docker compose down)
    默认只删:容器 + 项目自定义网络 ,镜像不会删除,卷也保留。

--remove-orphans 清理孤立容器

bash 复制代码
docker compose down --remove-orphans

3.4 logs 日志查看(日常排查高频)

-f, --follow 实时流式跟踪日志(tail -f)

bash 复制代码
# 实时查看所有服务日志
docker compose logs -f

# 只看web服务日志
docker compose logs -f web

--tail N 只读取末尾N行日志

bash 复制代码
# 查看最后200行,并且持续跟踪
docker compose logs --tail 200 -f web

-t, --timestamps 打印日志时间戳

bash 复制代码
docker compose logs -t --tail 100 web

3.5 ps 查看项目内服务状态

docker compose ps

列出当前项目所有容器,状态、端口映射。

bash 复制代码
# 只列出运行中的容器(默认)
docker compose ps

# -a / --all 显示全部(包含停止的容器)
docker compose ps -a

--services 仅列出yaml中定义的服务名称,不展示容器信息

bash 复制代码
docker compose ps --services

3.6 exec 进入正在运行的容器(调试)

等价 docker exec,但自动带上项目名,不用手写完整容器名称

bash 复制代码
# 进入web服务容器,打开bash
docker compose exec web bash

-it 交互式终端(大部分场景默认自带,可显式写)

bash 复制代码
docker compose exec -it web sh

-w, --workdir DIR 指定容器内工作目录

bash 复制代码
docker compose exec -w /app web pwd

--user USER 指定执行用户

bash 复制代码
docker compose exec --user www-data web id

重要:exec只能对运行中容器操作,停止容器无法exec。


3.7 其他高频子命令简要参数

pull:拉取镜像

bash 复制代码
# 拉取所有服务镜像
docker compose pull

# --ignore-buildable 跳过需要本地build的服务,只拉取镜像
docker compose pull --ignore-buildable

restart:重启服务

bash 复制代码
# 重启全部服务
docker compose restart

# 只重启web服务
docker compose restart web

stop / start

stop:优雅停止容器(默认10s超时)

bash 复制代码
# -t 指定优雅关闭超时时间,单位秒
docker compose stop -t 30 web

start:启动已存在但停止的容器,不会创建新容器(和up区别)


四、compose.yml 常用配置

前置说明:Compose V2 / Compose 5.x 统一遵循 Compose Spec(Compose规范) 。

✅ 不再强制要求文件顶部写 version: "3.x",写上也会被新版本CLI直接忽略;

✅ 文件推荐命名 compose.yaml / compose.yml,兼容旧 docker-compose.yml;

✅ 核心顶层关键字:services(必写)、networks、volumes、configs、secrets。
完整示例放在最前面,后面逐个字段拆解,方便博客读者复制测试。

yaml 复制代码
# compose.yaml 完整示例(可直接运行)
services:
  web:
    image: nginx:1.25
    container_name: web-demo
    ports:
      - "8080:80"
    environment:
      TZ: Asia/Shanghai
      REDIS_HOST: redis
    env_file: .env.prod
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - web-static:/usr/share/nginx/html
    depends_on:
      - redis
    restart: always
    networks:
      - app-net
    profiles: ["default"]
    healthcheck:
      test: ["CMD", "curl", "-f", "[http://127.0.0.1](http://127.0.0.1)"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data
    networks:
      - app-net
    restart: always

  milvus:
    image: milvusdb/milvus:v2.4.0
    security_opt:
      - seccomp:unconfined
    volumes:
      - milvus-data:/var/lib/milvus

volumes:
  web-static:
  redis-data:
  milvus-data:

networks:
  app-net:
    driver: bridge

4.1 services:服务定义(最核心,必用)

services 下每一个key代表一个服务,对应一个容器实例。

下面逐个拆解高频配置项。

image:指定镜像

作用:指定容器镜像名称+标签。优先拉取镜像,和build二选一;同时写build+image代表构建镜像并打tag。

yaml 复制代码
services:
  app:
    image: myapp:v1.0

小贴士:生产强烈建议写固定版本,不要用 latest,latest每次部署镜像版本不可控。

build:本地构建镜像

作用:从Dockerfile构建镜像,开发环境常用。

yaml 复制代码
services:
  app:
    build:
      context: ./ # Dockerfile所在目录
      dockerfile: Dockerfile.prod # 指定非默认文件名
      args:
        VERSION: "1.0" # 构建参数

生产最佳实践:不在生产服务器现场build,CI流水线提前打包镜像,生产直接使用image拉取。

container_name:固定容器名称

yaml 复制代码
services:
  app:
    container_name: my-app

⚠️ 限制:开启scale多副本时不能使用container_name,名字唯一,多实例会冲突。

不写该字段:Compose自动生成 项目名-服务名-序号,V2默认横杠分隔。

ports:端口映射(主机:容器)

两种写法:短语法(常用)、长语法(高级)

yaml 复制代码
services:
  app:
    # 短语法:主机端口:容器端口
    ports:
      - "8080:80"
      # 只指定容器端口,主机随机端口(scale多副本必备)
      - "80"
yaml 复制代码
ports:
  - "127.0.0.1:19530:19530"
  • 作用:宿主机端口转发到容器端口,控制外部访问。
  • 格式:宿主机IP:宿主机端口:容器端口
  • 示例说明:
    • "127.0.0.1:19530:19530":仅本机访问(生产推荐)
    • "0.0.0.0:19530:19530":所有网卡,公网可访问(危险)
    • "19530:19530"等价0.0.0.0:19530:19530

安全提示:不要写 0.0.0.0:8080:80 等价于公开监听所有网卡;内网服务可限制ip 127.0.0.1:8080:80,仅本机访问

environment 环境变量

两种写法:数组 / 对象形式。

yaml 复制代码
services:
  app:
    environment:
      TZ: Asia/Shanghai
      DB_HOST: mysql
    # 数组写法,支持特殊符号
    environment:
      - "PASSWORD=abc@123456"

env_file:加载外部环境变量文件

yaml 复制代码
services:
  app:
    env_file: .env.prod
    # 多个环境文件,后面会覆盖前面
    # env_file:
    #   - .env.base
    #   - .env.prod

优先级:environment > env_file > .env默认文件

volumes:数据挂载(持久化核心)

容器本身的文件系统是临时的:一旦容器被删除,容器内部所有数据全部丢失 。

volumes 挂载就是把数据存到容器外部,实现 数据持久化。

基础语法:

复制代码
源:容器内目标路径[:可选参数]

例子: 
docker run -v /host/data:/app/data:ro myimage
可选参数: rw:读写模式(默认)、ro:只读模式

例子 可选参数的 含义是:

  • 容器内 /app/data 这个路径只能读,不能写;
  • 容器内程序不能创建、修改、删除 /app/data 下的文件;
  • 宿主机上的 /host/data 不受影响,宿主机照样可以读写;
  • 容器内其他非挂载路径也不受这个 :ro 影响,仍然可写。
三种挂载类型:
1. 绑定挂载(bind mount):宿主机目录映射到容器,开发环境代码热更新、挂载配置文件

映射规则:

  • 文件 → 文件:可以 ✅
  • 目录 → 目录:可以 ✅
  • 文件 → 目录:不行
  • 目录 → 文件:不行
sh 复制代码
- ./conf:/etc/nginx/conf.d
  • 含义:把宿主机当前目录下的conf文件夹 ,直接映射到容器内的目录 /etc/nginx/conf.d
  • 数据位置:宿主机你自己指定的目录,你可以直接在宿主机打开、编辑里面的文件
  • 演示流程:
    1. 在compose.yaml同级新建conf目录,新建default.conf nginx 配置
    2. 启动容器后,容器读取的配置文件就是宿主机conf/default.conf
    3. ✅ 热更新:宿主机修改这个配置文件,容器内立刻生效,不需要重建容器
  • 适用场景:开发时代码挂载、挂载配置文件、静态资源
2. 命名卷(named volume):由docker自动管理,不用手动收入宿主机目录路径
复制代码
- redis-data:/data

必须在 yaml 文件最顶部写 volumes: 声明卷名称, 言外之意,使用之前 先定义一下。

复制代码
volumes:
  redis-data:
  • 含义:使用 Docker 托管的命名卷redis-data,挂载到容器内/data目录
  • 命名卷由 Docker 引擎全权管理 宿主机的存放路径,完全不用手动写宿主机路径。
  • 数据存放:一般存放在 Docker 根目录(Linux /var/lib/docker/volumes/redis-data/_data),一般不建议手动直接进去改文件
  • 关键特性:
    执行 docker compose down 删除容器,命名卷不会被删除,数据永久保留。下次 up 启动,挂载同一个卷,数据还在。
  • 场景举例:Redis/MySQL 数据库数据持久化。
    Redis 的数据存在redis-data命名卷,就算 redis 容器被删掉重建,数据不会丢失。
  • 和 bind 挂载对比:权限由 Docker 统一管理,不会出现宿主机 uid/gid 权限错乱问题,数据库生产环境首选。
3. 匿名卷:没有名字,容器删除自动清理,少用于生产
复制代码
services:
  redis:
    image: redis
    volumes:
      - /data   # 只有容器内路径,没有源目录/卷名 → 匿名卷
  • 含义:Docker 自动生成一串随机 ID 作为卷名(没有人类可读名称)
  • 关键特性:执行docker compose down删除容器时,匿名卷会跟着容器一起自动删除清理,数据丢失

❗ 生产几乎不用,容器销毁数据就丢,无法持久保存。

看上去 匿名卷 就是脱裤子放屁, 其实不然。匿名卷依然会创建独立的卷存储,它的意义主要在:

如果不创建 数据卷,那么 写入大量数据会 不断膨胀 容器。

depends_on 启动顺序依赖

⚠️ 重点大坑!depends_on 仅控制容器启动顺序,不会等待应用就绪 。

举例:mysql容器进程启动成功 ≠ mysql数据库可以接受连接,业务代码仍要自带重试逻辑。

yaml 复制代码
services:
  web:
    depends_on:
      - redis
      - mysql

Compose Spec新增高级语法(可选,V2支持),可以增加健康检查等待:

yaml 复制代码
services:
  web:
    depends_on:
      mysql:
        condition: service_healthy
  • 作用:定义容器启动先后顺序。
  • ⚠️ 默认行为:只等容器进程启动,不等应用就绪。
  • 可选condition:
    • service_started【默认】:只要容器进程启动就算完成,不等服务就绪
    • service_healthy:等待目标容器healthcheck检测为healthy,
      如果目标容器没有配置 healthcheck,service_healthy 等待条件直接无效
    • service_completed_successfully:一次性任务容器正常退出
  • 默认:无依赖,并行启动所有容器。

restart 重启策略(生产必配)

值 说明
no 不自动重启(默认)
always 容器退出永远重启,生产推荐
on-failure 仅异常退出(非0退出码)重启
unless-stopped 除非手动stop,否则一直重启

执行docker compose stop瞬间,always 和 unless-stopped 两者都不重启;当 Docker 守护进程重启时才体现差异:always 无视之前手动 stop,自动拉起;unless-stopped 记住手动 stop,保持停止。

yaml 复制代码
services:
  app:
    restart: always

healthcheck 健康检查(V2核心能力,V1支持不完善)

用于判断容器内业务是否正常,可配合 depends_on 的 service_healthy 等待服务就绪

yaml 复制代码
healthcheck:
  test: ["CMD", "curl", "-f", "[http://localhost:9091/healthz](http://localhost:9091/healthz)"]
  interval: 30s
  timeout: 20s
  retries: 3
  start_period: 90s
  • 作用:周期性执行检测命令,判断应用业务是否正常,而不只是看进程是否存活。
  • 参数明细:
    • test:检测命令,必填;数组形式["CMD", "cmdname","arg"]。
    • interval:检测间隔。默认 30s
    • timeout:单次检测超时时间,超时判定失败。默认 30s
    • retries:连续失败多少次标记unhealthy。默认 3
    • start_period:启动缓冲期 ,缓冲期内失败不算,给程序初始化时间。默认 0s
  • 默认:镜像自带healthcheck则继承,没有则不做健康检查。

profiles 配置分组(V2新增,V1不支持)

实现可选服务分组,只有命令行指定 --profile 才会启动该服务。适合监控、管理工具、开发辅助服务。

yaml 复制代码
services:
  adminer:
    image: adminer
    profiles: ["tools"]

命令:docker compose --profile tools up -d

networks 网络配置

多个服务(容器)可以加入同一个自定义网络,同一网络内的容器可以直接用【服务名】互相通信,DNS自动解析。

Compose 默认行为:如果你不写networks ,所有服务会自动加入Compose自动生成的项目默认bridge网络,所有容器天然互通。

手动定义networks,就是自己指定把哪些容器放到同一个隔离网络里。

举例
yaml 复制代码
services:
  # 服务1:后端应用
  app:
    image: nginx:alpine
    networks:
      - app-net

  # 服务2:redis缓存,加入同一个 app-net 网络
  redis:
    image: redis:alpine
    networks:
      - app-net

# 顶层声明自定义网络
networks:
  app-net:
  1. app 和 redis 两个容器都挂载到 app-net 这同一个bridge网络。
  2. 容器之间使用服务名称作为域名互相访问
    • 在app容器内部,可以直接用域名 redis:6379 访问redis服务。
    • 在redis容器内,可以直接用 app:80 访问nginx。
  3. 这个网络是项目隔离 的:别的compose项目的容器,默认无法接入这个app-net,实现网络隔离。

测试,进入app容器内部测试连通性:

bash 复制代码
# 进入app容器
docker compose exec app sh
# 测试连通redis,redis是服务名
ping redis
# 或者用nc测试端口
nc -zv redis 6379

security_opt:容器安全选项

yaml 复制代码
services:
  milvus:
    image: milvusdb/milvus:v2.4.0
    security_opt:
      - seccomp:unconfined
1. seccomp 是什么(简单理解)

seccomp 是 Linux 内核自带的安全机制:限制程序能调用哪些内核系统调用 。

Linux 有上百个系统调用(比如创建进程、访问内存、修改文件、socket 等)。

Docker 默认会加载一套 seccomp 配置:禁止容器里使用一部分高危系统调用,防止容器逃逸、内核漏洞利用。

默认策略:容器只能用 "安全子集" 的系统调用。

2. seccomp:unconfined 的含义
  • unconfined = 不施加任何 seccomp 过滤
  • 容器内进程可以调用全部 Linux 系统调用,和宿主机上普通进程权限一样,不再被 seccomp 拦截。

seccomp可选配置(作为了解):

  1. seccomp:unconfined:关闭seccomp系统调用过滤 。
    • 适用场景:部分数据库、向量数据库(Milvus)等底层需要大量系统调用的组件;
    • ⚠️ 安全风险:内核系统调用限制全部移除,容器逃逸风险上升,仅在业务必须时开启,不要全局滥用。
  2. seccomp:default:使用Docker内置默认seccomp策略,默认值,启用系统调用黑名单,生产推荐默认保持该项。
  3. seccomp:/path/profile.json:加载自定义的seccomp规则json文件,精细化管控允许/禁止的系统调用。

command / entrypoint

一句话:

entrypoint = 用哪个程序

command = 给这个程序传什么参数

如果没有entrypoint,command就变成"用哪个程序+参数"。

核心规则:

  1. ENTRYPOINT:容器入口可执行程序(优先级最高)
  2. command(Dockerfile里CMD / compose里command):传给entrypoint的参数
  3. 如果镜像没有ENTRYPOINT,那command就直接当做要运行的程序。

示例

复制代码
command: ["milvus", "run", "standalone"]
  • 含义:执行 milvus run standalone

  • 数组写法:不会经过shell,不会有引号、变量转义坑,生产首选。

  • 等价字符串写法(不推荐,会走shell)

    复制代码
    command: milvus run standalone

等价:

复制代码
entrypoint ["milvus"]
command ["run", "proxy"]

镜像里原生的 ENTRYPOINT / CMD 会被 compose 里的 配置覆盖

一句话记忆:

entrypoint: 在 compose 里写了 → 覆盖镜像的 ENTRYPOINT

command: 在 compose 里写了 → 覆盖镜像的 CMD

举例,原镜像中:

dockerfile 复制代码
# 镜像内
ENTRYPOINT ["/app/run.sh"]
CMD ["--env", "prod"]

容器启动时实际执行:/app/run.sh --env prod

  • ENTRYPOINT:主程序
  • CMD:传给 entrypoint 的默认参数

场景1:compose 只写 command:

yaml 复制代码
services:
  demo:
    image: myapp:v1
    command: ["--env", "dev"]

✅ 镜像 ENTRYPOINT 保留,镜像 CMD 被替换成 command 的值

实际运行:/app/run.sh --env dev

镜像原来的 CMD 不再生效;ENTRYPOINT 还是镜像自带的。

场景2:compose 只写 entrypoint:

yaml 复制代码
services:
  demo:
    image: myapp:v1
    entrypoint: ["/app/new.sh"]

✅ 镜像 ENTRYPOINT 被覆盖,镜像 CMD 保留,作为新 entrypoint 的参数

实际运行:/app/new.sh --env prod

场景3:compose 同时写 entrypoint + command

yaml 复制代码
services:
  demo:
    image: myapp:v1
    entrypoint: ["/app/new.sh"]
    command: ["--debug"]

✅ 镜像 ENTRYPOINT、镜像 CMD 全部失效,全部被 compose 配置替代

实际运行:/app/new.sh --debug

镜像里面写的任何自动执行脚本/命令,都不会跑。

deploy(资源限制)

deploy.resources 只支持 CPU、内存,不支持网络 IO、磁盘 IO 限制。

yaml 复制代码
deploy:
  resources:
    limits:
      cpus: "8"
      memory: 32G
    reservations:
      cpus: "2"
      memory: 8G
  • 作用:限制容器最大CPU、内存,防止OOM打满宿主机。
  • limits:上限,超过内存会被OOM kill,CPU会被限流。
  • reservations:预留资源,启动这个容器时,至少预留 2 核、8G 内存,调度优先分配(单机compose仅做声明,不强制预留)。
  • 默认:不限制资源,容器可以吃光宿主机全部CPU/内存(线上非常危险!)

单位:1=1核;内存G/M。

⚠️ 重点 :deploy 其实是为 Docker Swarm 设计的。

Docker Swarm 是 Docker原生集群编排工具 。把多台服务器(多个 docker 节点)组成一个集群,统一管理。

docker compose up(单机的compose):只有 resources.limits 能生效;reservations、replicas 等其他 deploy 子配置不生效!

  • 单机:一台机器跑 docker → 普通 compose(docker compose up)
  • 集群:多台服务器组成 docker 集群 → Swarm 模式,使用 docker stack 部署

logging 日志配置(线上重要)

yaml 复制代码
logging:
  driver: "json-file"
  options:
    max-size: "20m"
    max-file: "3"
json-file

是 docker 默认日志驱动。

docker 捕获的日志 = 容器内程序打印到标准输出 (stdout)、标准错误 (stderr) 的内容(就是你在命令行看到的打印内容)

json-file 的任务 就是 将 捕获 的日志 存储到 文件中,是 json 格式文件。

文件路径示例:

/var/lib/docker/containers/容器ID/容器ID-json.log

复制代码
{"log":"Milvus started successfully\n","stream":"stdout","time":"2026-10-16T08:30:00.123Z"}
  • log:原始输出文本
  • stream:区分是 stdout 正常输出 /stderr 错误输出
  • time:日志时间戳
max-size /max-file
  • max-size: "20m":单个 json 日志文件最大 20MB。文件达到 20MB 就触发切割。
  • max-file: "3":最多保留 3 个份。

tty & stdin_open

yaml 复制代码
tty: true
stdin_open: true
  • 作用:给容器分配伪终端、保持标准输入打开,用于交互式操作;普通后台中间件(Milvus、etcd)不需要开启。
  • 可选:true / false
  • 默认:两者默认都是 false

通俗例子

场景:启动一个ubuntu容器,要进去敲命令交互

yaml 复制代码
services:
  ubuntu-test:
    image: ubuntu:22.04
    tty: true
    stdin_open: true
  1. stdin_open: true:打开标准输入,可以向容器里输入文字。
  2. tty: true:分配终端界面,支持光标、颜色、上下翻命令,就像ssh连服务器。

普通 后台服务的容器( Mysql、etcd、Redis)不需要开启。

如果 后台服务的容器 强行打开 tty:true,会额外占用资源,日志输出格式也可能变化,不推荐。

4.2 顶层 volumes:命名卷声明

使用命名卷时,必须在顶层volumes节点声明,否则报错。

yaml 复制代码
volumes:
  redis-data: # 基础声明,使用docker默认驱动
  mysql-data:
    driver: local

命名卷数据由Docker托管,docker compose down 默认不会删除命名卷 ;加down -v才会删除。

4.3 顶层 networks:自定义 网络

  • 作用:定义 容器中使用的 网络变量,把当前容器加入指定自定义docker网络。
  • 默认:加入compose默认网络项目名_default。

在 docker compose 的 yaml 中,所有使用的 变量 (包括不限于 命名数据卷、网络),都需要在 顶层 对应的块 中 定义变量。

4.4 生产环境yaml编写最佳实践

  1. 镜像固定tag,禁止latest;
  2. 数据库等持久化数据使用命名卷,不要bind挂载;
  3. 所有业务容器配置 restart: always + healthcheck;
  4. 增加logging限制日志大小,避免磁盘打爆;
  5. 敏感密码不要硬编码写yaml,放到.env文件,并且git忽略.env;
  6. 多环境拆分:base.yml + dev.override.yml / prod.override.yml,用 -f 合并加载;
  7. 内网服务尽量不把端口映射到宿主机,利用compose内部网络互通;
  8. security_opt 最小权限原则 :默认保留seccomp:default;只有组件文档明确要求(如Milvus),才开启seccomp:unconfined,评估安全风险。
相关推荐
卜锦元1 小时前
Docker Compose 部署 Nginx 1.30.6 完整指南
nginx·docker·容器
分布式存储与RustFS3 小时前
训练集放在 RustFS 上,怎么像本地盘一样快?用 Curvine 加一层 POSIX 缓存
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
easy_coder3 小时前
Kubernetes 三大探针:Startup、Liveness 与 Readiness
云原生·容器·kubernetes
xrlfreedom3 小时前
MCP 面试高频考点:把知识库 RAG 检索封装为 Tool,Docker 与结构化输出怎么配合不踩坑
docker·结构化输出·mcp
刘胡子大叔3 小时前
Docker 容器名 ping 不通
运维·docker·容器
a5986523 小时前
Docker多开云手机,六边云独立端口隔离访问实战
android·运维·docker·容器·智能手机·云手机
吴声子夜歌4 小时前
Docker入门与实战——安全防护与配置
安全·docker
暖核4 小时前
kubeadm 搭建 K8s v1.30 集群:从系统模板到 Calico 网络全流程
网络·容器·kubernetes
KIDULT°5 小时前
Kubeadm 部署 Kubernetes v1.30.2 实战:Ubuntu24.04 三节点集群 + 排坑记录
ubuntu·云原生·容器·kubernetes·k8s