3、Docker Compose核心概念:服务、项目、编排、配置文件结构、常用命令与生命周期管理
在掌握了Docker基础容器操作之后,面对多模型推理服务的部署场景——例如同时启动一个LLM推理服务、一个向量化嵌入服务、一个模型网关以及配套的Redis缓存——如果仍然逐个使用docker run命令,不仅命令冗长易错,而且难以管理服务间的网络依赖与启动顺序。Docker Compose正是为解决这一痛点而生的容器编排工具。本章将深入剖析Compose的五大核心概念:服务(Service)、项目(Project)、编排(Orchestration)、配置文件结构(Compose File)以及生命周期管理,帮助您建立从单容器到多容器协作的思维跃迁。
3.1 服务(Service)与项目(Project)的本质
核心原理:从容器到服务的抽象
在Compose语境中,服务(Service)是对一组功能相同、配置相同的容器实例的抽象定义。它描述的是“运行什么镜像、开放哪些端口、挂载哪些卷、依赖哪些环境变量”,而不是某个具体的容器ID。例如,在模型推理场景中,您可以定义一个名为llm-server的服务,指定镜像registry.example.com/llm:v2,并设置NVIDIA_VISIBLE_DEVICES=all环境变量。当执行docker compose up时,Compose会根据该服务定义创建1个或多个容器副本(通过replicas参数控制)。
项目(Project)则是多个服务的逻辑集合,其默认名称取自docker-compose.yml文件所在目录的基名。例如,在/opt/modelserving目录下执行Compose命令,项目名即为modelserving。项目为所有资源(容器、网络、卷)提供了命名空间前缀,例如网络会被命名为modelserving_default,卷命名为modelserving_model-cache。这种隔离机制允许多个项目在同一台宿主机上并行运行而互不干扰。
判断方法:如何区分服务与项目
- 服务是“做什么”:关注镜像、命令、端口、资源限制等运行细节。
- 项目是“属于谁”:关注目录边界、命名空间、资源归属。
- 执行
docker compose ls可查看当前所有项目及其状态;执行docker compose ps仅查看当前项目内的服务。
| 维度 | 服务(Service) | 项目(Project) |
|---|---|---|
| 定义位置 | compose文件中的services节点 | compose文件所在目录名 |
| 粒度 | 单个应用组件(如API网关) | 一组关联服务的集合 |
| 命名规则 | 由用户自定义(如web) | 默认目录名,可用-p覆盖 |
| 资源归属 | 容器、网络别名 | 网络、卷、容器统一前缀 |
| 操作命令 | docker compose up 服务名 | docker compose up(全部) |
案例拆解:多模型推理项目结构
假设您要部署一个包含三个服务的推理平台:gateway(流量入口)、llm(大语言模型)、embedding(向量嵌入)。在/opt/ai-platform目录下创建docker-compose.yml,项目名自动为ai-platform。执行docker compose up -d后,Compose会创建ai-platform_gateway_1、ai-platform_llm_1、ai-platform_embedding_1三个容器,以及一个名为ai-platform_default的桥接网络。服务间通过服务名(如http://llm:8000)互相访问,无需关心容器IP变化。
container_name虽然方便,但会破坏Compose的扩展性(如水平伸缩时产生冲突)。建议依赖自动生成的容器名,通过服务名进行服务发现。3.2 编排(Orchestration)的工作机制
核心原理:声明式编排与依赖解析
编排是Compose的核心价值所在。它读取配置文件中的声明(期望状态),然后调用Docker API逐步创建网络、卷、容器,并处理服务间的依赖关系。例如,gateway服务依赖llm和embedding,Compose会先启动被依赖的服务,再启动依赖方。这种依赖通过depends_on字段声明,但需要注意:depends_on仅控制启动顺序,不等待服务内部就绪(如模型加载完成)。
操作流程:编排的四个阶段
- 解析:读取
docker-compose.yml,校验语法与字段合法性。 - 规划:计算需要创建/更新/删除的资源,生成执行计划。
- 执行:按依赖顺序创建网络、卷、拉取镜像、启动容器。
- 收敛:持续监控容器状态,尝试恢复异常退出的容器(除非设置了
restart: "no")。
实战场景:健康检查与依赖就绪
对于模型推理服务,模型加载可能耗时数十秒。若gateway在llm尚未就绪时即开始转发请求,会导致大量502错误。解决方案是结合healthcheck与depends_on的条件判断:
services:
llm:
image: llm:v2
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 5s
timeout: 3s
retries: 12
gateway:
image: gateway:v1
depends_on:
llm:
condition: service_healthy
这样编排器会等待llm的健康检查通过后,才启动gateway,确保依赖真正可用。
depends_on构建过长的依赖链,这会拖慢启动速度并增加故障传播面。建议将强依赖控制在两层以内,弱依赖通过重试机制处理。3.3 配置文件结构详解
核心原理:YAML的层次化描述
docker-compose.yml采用YAML格式,顶层节点通常包含version(已废弃但可保留)、services(必填)、networks、volumes、configs、secrets。其中services是唯一必填项,每个服务键名即服务名,键值包含镜像、构建上下文、环境变量、端口映射、卷挂载等属性。
操作流程:编写配置文件的步骤
- 定义服务清单:列出所有需要运行的应用组件。
- 确定镜像来源:使用
image指定远程镜像,或使用build指定本地Dockerfile。 - 配置网络与端口:通过
ports暴露宿主机端口,通过expose仅内部通信。 - 声明依赖与健康检查:设置
depends_on和healthcheck。 - 挂载存储:使用
volumes持久化模型权重或日志。 - 设置资源限制:通过
deploy.resources.limits限制CPU/内存。
案例拆解:一个完整的推理服务配置
version: "3.8"
services:
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
llm:
image: registry.example.com/llm:v2
environment:
- MODEL_NAME=/models/llama-7b
- NUM_GPU=1
volumes:
- model-cache:/models
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
depends_on:
redis:
condition: service_healthy
gateway:
image: gateway:v1
ports:
- "8080:80"
depends_on:
- llm
volumes:
redis-data:
model-cache:
该配置定义了三个服务:Redis缓存、LLM推理、API网关。通过命名卷model-cache持久化模型文件,通过deploy.resources申请GPU资源,通过健康检查确保Redis先就绪。
docker compose config可验证配置文件的语法正确性,并查看合并后的完整配置(包括默认值)。这是CI/CD流水线中必备的检查步骤。3.4 常用命令与生命周期管理
核心原理:状态机与命令映射
Compose管理的服务生命周期包括:创建(create)→ 启动(start)→ 运行(running)→ 暂停(pause)→ 停止(stop)→ 删除(rm)。常用命令是这些状态转换的触发器。理解状态转换有助于排障:例如docker compose restart会先停止再启动,而docker compose up -d --force-recreate会强制重新创建容器以应用新配置。
操作流程:标准部署与更新流程
- 首次部署:
docker compose up -d(后台运行)或docker compose up(前台查看日志)。 - 查看状态:
docker compose ps显示容器状态、端口映射、健康状态。 - 查看日志:
docker compose logs -f 服务名跟踪特定服务输出。 - 更新配置:修改YAML后执行
docker compose up -d,Compose自动检测变更并重建受影响的服务。 - 停止服务:
docker compose stop保留容器与数据;docker compose down删除容器和默认网络(加-v同时删除卷)。
| 命令 | 作用 | 适用场景 | 注意事项 |
|---|---|---|---|
up -d | 创建并后台启动 | 日常部署 | 幂等,可重复执行 |
down | 停止并删除容器/网络 | 彻底清理 | 默认不删卷,加-v删卷 |
ps | 列出项目内容器 | 状态检查 | 加-a显示已停止容器 |
logs | 查看日志输出 | 排障与监控 | -f持续跟踪,--tail控制行数 |
exec | 进入运行中容器 | 调试与运维 | 需指定服务名,如exec llm bash |
pull | 拉取最新镜像 | 更新前准备 | 不重启容器 |
案例拆解:模型版本升级的完整闭环
背景与约束:某团队维护的LLM服务需要从v1.2升级到v1.3,要求零停机(滚动更新),且模型权重文件需保留。
初始状态:docker compose ps显示llm服务运行正常,使用镜像llm:v1.2,卷model-cache挂载于/models。
操作步骤:
- 修改
docker-compose.yml中的镜像标签为llm:v1.3。 - 执行
docker compose pull llm预拉取新镜像,避免更新时超时。 - 执行
docker compose up -d --no-deps llm,仅重建llm服务,不触碰依赖服务。 - 观察
docker compose ps确认新容器健康,docker compose logs -f llm检查模型加载日志。
结果与复盘:由于使用了--no-deps,网关服务未受影响,实现了平滑升级。若新版本启动失败,可执行docker compose up -d --force-recreate --no-deps llm回滚镜像标签。此流程避免了全量重启带来的请求中断。
docker compose down会删除数据卷。实际上,默认情况下down仅删除容器和网络,卷仍然保留。只有显式添加-v标志才会删除卷。在清理测试环境时,务必确认是否需要保留数据。3.5 总结复盘:从概念到实践的要点
- 服务是配置单元,项目是隔离边界:理解两者的关系是使用Compose的基石。
- 编排的核心是声明式管理:您描述期望状态,Compose负责收敛到该状态。
- 配置文件是唯一事实来源:所有变更都应通过修改YAML并执行
up完成,避免手动docker run造成漂移。 - 生命周期命令需谨慎:
down -v是危险操作,会删除所有关联卷;restart与up --force-recreate有本质区别。 - 健康检查是生产环境的必备项:没有健康检查的依赖管理只是“伪编排”。
掌握这些核心概念后,您已经具备阅读和理解绝大多数Compose文件的能力。下一阶段将进入实际部署环节,学习如何编写适用于GPU推理场景的Compose配置,并处理设备映射、资源预留等高级主题。