Docker Compose 编排本地多服务一键起
奶油甜饼
9 分钟阅读
Docker Compose 编排本地多服务一键起
docker compose 的价值不在于“能跑容器”,而在于把一组有依赖关系的服务、网络、数据卷和环境变量收敛到一个声明文件里,一条 docker compose up 就能把整套本地依赖拉起来。没有它,每个服务都要手动 docker run 加一长串参数,网络和卷容易错配。对单机本地开发、集成测试、轻量演示来说,它比上完整编排系统便宜太多,是性价比最高的“环境即代码”入口。当团队里每个人机器上的依赖版本、启动顺序都不一样时,一份 compose 文件是大家对齐环境的契约,谁的环境起不来,先 diff 这份文件。
一个最小可用的 compose 文件
services:
web:
build: .
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgres://app:app@db:5432/app
db:
image: postgres:16
environment:
- POSTGRES_USER=app
- POSTGRES_PASSWORD=app
- POSTGRES_DB=app
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pgdata:
depends_on 的 condition: service_healthy 让 web 等 db 真正能接受连接后再启动,避免应用起来就因连不上库而崩溃重启。这里的关键是 healthcheck 必须真实探测“能否服务”,而不是只探“进程在不在”。pg_isready 返回就绪才放行,比单纯等几秒的 condition: service_started 可靠得多。
网络是默认打通的
compose 会为项目创建一个默认网络,服务名就是内网 DNS 名。上面 web 用 db:5432 访问数据库,不需要暴露宿主机端口。仅当宿主机需要访问时才在 ports 里映射,这能减少不必要的端口暴露面。如果项目里有多个栈或想隔离不同层,用 networks 显式定义并给服务指定归属:
networks:
backend:
frontend:
services:
web:
networks: [frontend, backend]
db:
networks: [backend]
这样 db 只在 backend 网络,frontend 的服务无法直接访问它,隔离粒度更清晰,也接近真实部署的分层,避免所有容器默认混在同一个大二层里互相可见。
数据要落到卷里
容器文件系统是临时的,重启即丢。数据库这类有状态服务必须挂卷。pgdata:/var/lib/postgresql/data 是命名卷,由 compose 管理生命周期;也可以写成 ./data:/var/lib/postgresql/data 的绑定挂载,方便在宿主机直接看文件,但绑定挂载在 Linux 上有 uid 权限对齐的坑。经验上,本地开发用绑定挂载改代码即时生效最方便,但凡需要稳定属主或跨平台一致时用命名卷。两者混用时要清楚数据落在哪、谁负责清理,否则清理环境时容易误删或漏删。
环境区分用多文件覆盖
本地、测试的配置差异用多个 compose 文件叠加:
docker compose -f compose.yaml -f compose.override.yaml up
compose.override.yaml 默认自动加载,用来加调试端口、换镜像 tag。敏感值不要写死在文件里,用 .env:
environment:
- DATABASE_URL=${DATABASE_URL}
.env 不进版本库,靠 .env.example 给模板。多环境可以 compose.test.yaml、compose.prod.yaml 分别用 -f 引入,避免把生产配置和本地混在同一份文件里被人误推。多文件合并时,后写的文件字段会覆盖前面的,数组类字段(如 ports)则是追加而非替换,这点容易踩。
资源限制防止单服务吃满整机
本地常开一堆服务,某个失控会拖垮整机。给每个服务设上限:
services:
web:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
deploy.resources 在 compose 规范里定义;老版本用 mem_limit 和 cpus 字段,命名随规范版本不同,写错会静默忽略不生效。设了上限,某个服务内存泄漏也只撑爆自己,不影响同机其他服务,调试时更容易定位是哪个容器出了问题。
日志与调试
docker compose logs -f web # 跟踪某服务日志
docker compose exec web sh # 进容器排错
docker compose ps # 看各服务状态与退出码
排查“起不来”先看 logs,再看 ps 的状态和退出码,比盲猜快得多。exec 进去能直接验证配置文件、网络连通性,是本地排错的常规入口。多个服务互相调用时,先确认被调方日志正常,再查调用方,顺着依赖方向查最快。
先校验再启动
docker compose config 会在启动前展开模板、合并多文件并打印最终配置,能提前发现缩进和字段拼错。多人协作时把它放进提交前检查,比线上报错便宜。
docker compose up -d --scale worker=3 可把无状态服务横向扩到多个实例,验证本地并发消费逻辑是否成立。注意 --scale 只对没绑定固定宿主机端口的服务有意义,绑定了端口的会冲突起不来。
按需启动部分服务
compose 支持 profiles,把不常用的服务(如本地缓存、消息队列)归到某个 profile,默认不启动:
services:
redis:
image: redis:7
profiles: ["cache"]
docker compose up --profile cache # 才把 redis 拉起来
这对“主链路常开、旁路按需”的本地栈很实用,避免每次都起一堆用不上的中间件拖慢整机。
compose 规范版本说明
旧版 compose 文件顶部写 version: "3",新规范已废弃该字段,写了反而可能告警甚至被忽略部分功能。直接用最新 compose 规范,不写 version 即可,工具按文件内容自动识别。沿用老模板时顺手删掉 version 字段,能少踩一类兼容性坑。
和集群编排的边界
compose 解决的是“单机多容器协同”,不是集群编排。需要跨机调度、自动扩缩、自愈,那是另一层系统的事。别用 compose 去扛生产多节点,它的定位就是本地和轻量,越界只会既不好用又不稳,把该交给编排系统的职责留给专门的工具。
踩坑清单
depends_on 只等启动成功,不等“就绪”。不加 healthcheck 就靠 condition,web 仍可能连库失败。
build 与 image 同时写时,构建出的镜像会以 image 字段命名;只改代码不重建,用 docker compose up --build。
- 宿主机端口冲突会让整个
up 失败,先 docker compose down 清理旧实例。
- 默认
restart 策略为空,进程退出容器就停;本地常驻建议 restart: unless-stopped。
- 卷属主在 Linux 上可能是 root,应用以非 root 跑会写不进,需要在 Dockerfile 里建好用户并赋权。
docker compose down -v 会连同命名卷一起删,恢复干净环境但数据全丢,演练前确认不是生产数据。
- 不同项目用了相同服务名或卷名,compose 按目录名做项目前缀,换目录能隔离;残留的匿名卷要记得
docker volume prune 清理。
浏览 0
点赞 0
评论 0