一、简介
1-1、核心用途
Docker Compose 是 单台服务器 的 多容器 编排工具 ,专门用来定义、管理一台主机上一组相互关联的容器服务。
把应用的多个组件(前端、后端API、MySQL、Redis、MQ、Nginx等)写在一份 compose.yaml 配置文件里,一条命令完成整套应用的启动、停止、重启、日志查看、扩容、销毁。
定位:单机编排,不跨机器 。适合开发环境、测试环境、中小型公司单机生产部署;
它不替代K8s(K8s是多节点集群编排,太重,中小项目没必要)。
1-2、解决的痛点
只用 docker run 的致命问题:很难留痕,维护灾难。
很多新手直接写一堆 docker run 命令启动容器,最大痛点:配置无持久化留痕:
- 启动参数散落在命令行,没有版本记录
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一旦容器误删、服务器重装,你要靠记忆复现整条命令,很容易漏掉环境变量、挂载目录,直接丢数据或者服务起不来。
-
多人协作时无法同步服务配置
团队其他人不知道完整启动参数。A开发启动数据库加了特殊环境变量,B不知道,自己手动
docker run,环境不一致,本地能跑、服务器报错。 -
多容器之间网络、依赖手动管理,极易出错
多个服务(后端+Redis+MySQL),
docker run需要手动创建自定义网络,手动指定--network,还要人为控制启动顺序;每次部署都要敲多条命令,顺序错就启动失败。 -
不好做版本管理、变更审计
docker run命令没法提交到Git。修改容器配置没有记录,谁改了什么参数完全无迹可寻,线上出问题很难回溯。 -
批量运维麻烦
启停整套应用,需要逐个
docker stop、docker start;扩容、重建、查看日志要逐个容器操作。
一句话总结:docker run 是"临时手工启动",配置只存在当前终端历史里;一旦容器删除或者机器重建,配置就丢了,留不住完整环境定义。
1-3、为什么中小型公司普遍选择 Docker Compose
中小型项目(单体/少量微服务,单台或2~3台独立服务器),几乎不会上K8s:K8s学习成本高、资源开销大、运维复杂度高,需要专人维护,小团队扛不住。而 Compose 刚好匹配需求:
-
所有服务配置全部写在
compose.yaml,文件存入Git,永久留痕环境变量、挂载、端口、镜像、资源限制全部在yaml文件。
容器删了、服务器重装,只需要拉取yaml,一条
docker compose up -d就能原样恢复整套环境;所有变更可以看git提交记录,谁修改、什么时候改一目了然,解决docker run无留痕痛点。 -
一键启停整套服务
up启动、down销毁整套服务,logs看全部服务日志,restart重启,不用逐个容器操作。 -
自动处理网络
Compose会自动创建项目隔离网络,同项目内容器直接用服务名互相访问(后端直接连接
mysql:3306),不用手动docker network create。 -
开发环境一致性(本地开发 + 测试机 + 轻量生产复用同一份配置)
开发、测试、生产共用一套compose模板,通过
.env文件区分不同环境变量,保证环境对齐,减少"我本地能跑,服务器不行"。 -
上手简单,运维成本极低
只需要学会yaml基础和几条compose命令,普通后端开发就能维护,不需要专职容器运维。
-
支持多文件拆分、环境分组(profiles/include)
可以把数据库、中间件拆成独立文件;开发环境启用调试服务,生产环境只启用核心业务服务。
二、历史版本 介绍
区分两个概念:
- Compose CLI版本(v1/v2)
- compose.yaml 文件格式版本(2.x/3.x/Compose Spec)
下面说的是CLI工具版本。
2-1、V1(docker-compose,中间短横线连接)
- 开发语言 :
Python,独立项目,有一堆Python依赖包 - 发布时间 :2014年,最终版
1.29.2(2021-05),不再更新 - 维护状态 :EOL,2023-07起彻底停止维护,无安全补丁、无bug修复,不再随新版Docker Desktop打包
- 架构:独立二进制程序,和Docker CLI分开调用。
- 特点
- 命令:
docker-compose up - yaml文件
顶层必须写version: "3.8"这类版本声明,用来指定旧文件格式 - 容器命名:
project_service_1(下划线分隔) - 性能弱:Python解释器开销,启动慢
- 缺少现代能力:不支持
profiles、include、watch(热重载)、BuildKit完整集成、完善GPU支持 - 兼容性:只能解析旧2.x、3.x文件格式
- 命令:
- 生产建议 :严禁生产使用 ,存在安全漏洞风险,官方不再支持。旧脚本可以临时用
--compatibility模式在V2上模拟V1行为。
2-2、V2(docker compose,中间空格连接,Docker CLI插件)
- 开发语言 :
Golang,和Docker Engine/Moby同语言,复用Docker底层代码库(compose-go) - 发布时间 :2020发布,2022 GA,持续活跃开发(后续还有v5版本,CLI命令不变,主要新增Go SDK)
- 维护状态 :官方主力维护,安全更新、bug修复、新特性持续迭代 ,现代Docker Engine默认自带插件包
docker-compose-plugin - 架构:Docker CLI插件,作为docker子命令,不是独立程序。
- 特点
- 命令:
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依赖
- 命令:
- 生产推荐版本
- 生产环境:使用随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、迁移小提示
- 旧yaml文件不需要大规模重写,V2可以直接读取旧2.x/3.x compose文件
- 脚本中把
docker-compose→docker compose,或者配置aliasalias docker-compose="docker compose" - 如果依赖容器名下划线,启动时加
--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 标记的服务,实现按需启停可选服务。
核心规则:
- 没有写
profiles的服务,属于默认分组,默认一定会启动;- 写了
profiles的服务,默认不启动 ;只有命令行通过--profile指定对应分组名称,才会被拉起;- 一个服务可以归属多个分组
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种情况处理:
- 容器不存在(从未创建,或者已经被down删除):新建容器、创建网络/命名卷、构建镜像(如果配置build),然后启动容器。
- 容器已经存在,但处于停止状态(exited) :执行重启容器。
- 容器已经存在,并且正在运行中(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
执行流程:
- 强制执行构建:读取
build上下文,重新 build 镜像(哪怕 Dockerfile、上下文文件一点没变,依然会跑一遍构建流程,生成新镜像层 / 镜像 ID) - Compose 对比:当前容器使用的镜像 和 刚刚新构建的镜像 ID 不一致
- 动作:停止正在运行的旧容器 → 删除旧容器 → 使用新构建镜像创建新容器并启动
--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
两条规则同时生效:
- 强制重新构建镜像
- 强制重建容器(就算构建完镜像 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,
不能单独写--rmiDocker
# 语法
docker compose down --rmi local
docker compose down --rmi all
--rmi local(本地构建镜像)
只删除本项目 build 出来、没有手动自定义 tag 的镜像;
从远程拉取的镜像(如nginx:alpine)保留不删。--rmi all
删除当前 compose 项目用到的全部镜像,不管是本地构建还是远程拉取。- 不带任何
--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 - 数据位置:宿主机你自己指定的目录,你可以直接在宿主机打开、编辑里面的文件
- 演示流程:
- 在
compose.yaml同级新建conf目录,新建default.confnginx 配置 - 启动容器后,容器读取的配置文件就是宿主机
conf/default.conf - ✅ 热更新:宿主机修改这个配置文件,容器内立刻生效,不需要重建容器
- 在
- 适用场景:开发时代码挂载、挂载配置文件、静态资源
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:检测间隔。默认30stimeout:单次检测超时时间,超时判定失败。默认30sretries:连续失败多少次标记unhealthy。默认3start_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:
app和redis两个容器都挂载到app-net这同一个bridge网络。- 容器之间使用服务名称作为域名互相访问
- 在
app容器内部,可以直接用域名redis:6379访问redis服务。 - 在
redis容器内,可以直接用app:80访问nginx。
- 在
- 这个网络是项目隔离 的:别的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可选配置(作为了解):
seccomp:unconfined:关闭seccomp系统调用过滤 。- 适用场景:部分数据库、向量数据库(Milvus)等底层需要大量系统调用的组件;
- ⚠️ 安全风险:内核系统调用限制全部移除,容器逃逸风险上升,仅在业务必须时开启,不要全局滥用。
seccomp:default:使用Docker内置默认seccomp策略,默认值,启用系统调用黑名单,生产推荐默认保持该项。seccomp:/path/profile.json:加载自定义的seccomp规则json文件,精细化管控允许/禁止的系统调用。
command / entrypoint
一句话:
entrypoint = 用哪个程序
command = 给这个程序传什么参数
如果没有entrypoint,command就变成"用哪个程序+参数"。
核心规则:
ENTRYPOINT:容器入口可执行程序(优先级最高)command(Dockerfile里CMD / compose里command):传给entrypoint的参数- 如果镜像没有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
stdin_open: true:打开标准输入,可以向容器里输入文字。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编写最佳实践
- 镜像固定tag,禁止latest;
- 数据库等持久化数据使用命名卷,不要bind挂载;
- 所有业务容器配置
restart: always+healthcheck; - 增加logging限制日志大小,避免磁盘打爆;
- 敏感密码不要硬编码写yaml,放到
.env文件,并且git忽略.env; - 多环境拆分:base.yml + dev.override.yml / prod.override.yml,用
-f合并加载; - 内网服务尽量不把端口映射到宿主机,利用compose内部网络互通;
- security_opt 最小权限原则 :默认保留
seccomp:default;只有组件文档明确要求(如Milvus),才开启seccomp:unconfined,评估安全风险。