Docker Compose 是什么?为什么算法服务需要多个容器?
码海寻道 · AI 算法工程化与系统落地实战第 4 篇
一个算法服务很少只需要一个进程。实际项目通常还需要 PostgreSQL 保存任务和结果,Redis 做缓存和限流,MinIO 保存文件,RabbitMQ 处理异步推理,Nginx 接收外部流量。
如果每个服务都手动安装、手动配置、手动启动,换一台机器就要重复一遍。Docker Compose 用一个 YAML 文件描述多个容器、网络、数据卷、健康检查和环境配置,让开发、测试和单机部署拥有一套可复现的启动方式。
一、为什么算法服务需要多个容器?
算法 API 负责接收请求,但不应该承担所有职责:
algorithm-api:HTTP 接口、鉴权、参数校验 algorithm-worker:耗时推理和批量任务 postgres:任务、用户、结果和审计 redis:缓存、限流和短期状态 minio:原始文件、结果文件和模型文件 rabbitmq:异步任务投递 nginx:TLS、路由和外部访问入口拆分的核心原因不是“服务越多越专业”,而是不同组件的生命周期和资源需求不同:API 需要快速响应,Worker 需要消耗 CPU/GPU,数据库需要持久化,缓存可以重建,消息队列需要可靠投递。
二、Compose 文件的基本结构
services:api:build:.ports:-"8000:8000"redis:image:redis:7-alpineservices是核心。每个服务可以指定镜像或构建目录、环境变量、端口、网络、Volume、健康检查和依赖关系。服务之间通过 Compose 网络中的服务名访问,而不是使用宿主机localhost。
例如,API 容器连接 Redis 时应该使用:
redis://redis:6379/0不能写成redis://localhost:6379/0,因为容器中的localhost指向 API 容器自己。
三、一个最小的算法系统
services:api:build:.environment:REDIS_URL:redis://redis:6379/0DATABASE_URL:postgresql://app:app@postgres:5432/appports:-"8000:8000"depends_on:redis:condition:service_healthypostgres:condition:service_healthypostgres:image:postgres:16environment:POSTGRES_DB:appPOSTGRES_USER:appPOSTGRES_PASSWORD:appvolumes:-postgres_data:/var/lib/postgresql/datahealthcheck:test:["CMD-SHELL","pg_isready -U app -d app"]interval:5stimeout:3sretries:10redis:image:redis:7-alpinevolumes:-redis_data:/datahealthcheck:test:["CMD","redis-cli","ping"]interval:5stimeout:3sretries:10volumes:postgres_data:redis_data:这里有三个容易被忽略的点:
- 数据库和 Redis 使用命名 Volume,避免容器删除后数据直接消失;
depends_on配合service_healthy,避免 API 在数据库尚未可用时立即启动;- 数据库密码只是演示值,生产环境应通过 Secret 或外部密钥管理注入。
Compose 官方文档支持服务级健康检查、命名 Volume 和 Secrets 配置。Docker Compose services 文档
四、服务启动顺序不等于服务可用
错误理解是:
postgres 容器先启动 ↓ api 容器再启动 ↓ 数据库一定可以连接实际情况是,容器进程启动后,数据库可能还在初始化、执行恢复或创建用户。depends_on只能表达依赖关系,可靠判断应该依赖健康检查;应用自身也要在启动时重试连接,并在数据库不可用时保持未就绪。
五、网络、端口和服务发现
端口有两个概念:
容器端口:服务在容器内部监听的端口 宿主机端口:外部访问宿主机时使用的端口只有需要被宿主机或公网访问的服务才需要ports:
api:ports:-"8000:8000"PostgreSQL、Redis 和 RabbitMQ 管理端口通常只供内部服务访问,可以不映射到宿主机,减少暴露面。API 通过服务名访问它们:
postgres:5432 redis:6379 rabbitmq:5672六、环境变量和不同环境
不要把开发、测试和生产配置写成三份完全不同的 Compose 文件。可以把稳定结构放在compose.yaml,再用环境变量覆盖:
services:api:image:${ALGO_IMAGE:-algorithm-api:dev}environment:APP_ENV:${APP_ENV:-development}MODEL_VERSION:${MODEL_VERSION:-demo}运行前检查最终配置:
dockercompose config这个命令可以帮助发现变量没有注入、Volume 路径错误和 YAML 合并问题。敏感配置不要提交.env到 Git,生产环境应使用 Secret 文件、平台密钥或专用密钥管理系统。
七、健康检查应该检查什么?
api:healthcheck:test:["CMD","python","-c","import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]interval:30stimeout:5sretries:3start_period:30s算法服务最好区分:
/health:进程是否存活 /ready:模型、数据库和必要依赖是否已经准备好不要在健康检查中执行一次完整推理,也不要让一个短暂的 Redis 故障导致 API 容器不断重启。健康检查应该轻量、有限时,并且能反映服务是否适合接收流量。
八、常用操作命令
# 构建并后台启动dockercompose up-d--build# 查看服务状态dockercomposeps# 查看 API 日志dockercompose logs-fapi# 查看某个服务的环境和配置dockercompose config# 执行一次性命令,例如数据库迁移dockercompose run--rmapi python-mapp.migrate# 停止并删除容器,但保留命名 Volumedockercompose down# 连同 Volume 一起删除,谨慎使用dockercompose down-vdown -v会删除数据库和缓存数据,开发环境也不要在没有确认目标的情况下执行。
九、Compose 适合什么,不适合什么?
适合:
- 本地开发和联调;
- 单机测试环境;
- 小规模内部服务;
- 复现一套依赖组件;
- 给新成员提供一键启动环境。
不应把 Compose 自动等同于高可用生产平台。生产环境还需要考虑多机调度、滚动发布、备份恢复、监控告警、密钥轮换和故障转移。规模扩大后,可以把同样的服务边界迁移到 Kubernetes 或托管容器平台。
结语
Docker Compose 的真正价值,是把“算法 API 依赖哪些组件、如何连接、数据存在哪里、什么时候算健康”写成可以评审和复现的配置。下一篇将用 Compose 启动 FastAPI、PostgreSQL 和 Redis,做出第一个可以落地的算法服务基础环境。
参考资料
- Docker Compose 官方文档:How Compose works
- Docker Compose 官方文档:Services
- Docker Compose 官方文档:Quickstart
本文为“码海寻道”原创技术文章。Compose 示例用于开发和单机联调,生产部署仍需单独设计高可用、备份和安全策略。