共计 2310 个字符,预计需要花费 6 分钟才能阅读完成。
文章目录 [显示]
用了这么久 Docker Compose,你是不是还停留在 docker compose up -d 就完事了?
坦白说,大多数人对 Compose 的使用只触及了冰山一角。真正让它发挥威力的,是那些藏在文档深处、很少被提及的进阶用法。今天这篇,我把自己踩坑多年攒下来的实战经验一次性讲透。
下载了一个开源代码,想跟着项目更新
这是我认为 Compose 最被低估的能力——override 机制 。
很多人遇到过这种场景:用的是开源项目提供的 docker-compose.yml,但你想加点自己的配置,比如多挂载一个目录、改个端口。直接改原文件?下次更新就被覆盖了。
Docker Compose 天生支持多文件合并。它会自动读取同目录下的 docker-compose.override.yml,把里面的配置叠加到主文件上。
# docker-compose.yml(原始文件,不要动它)services:
web:
image: nginx:latest
ports:
- "80:80"
# docker-compose.override.yml(你的自定义配置)services:
web:
ports:
- "8443:443"
volumes:
- ./custom.conf:/etc/nginx/conf.d/custom.conf
environment:
- DEBUG=true
执行 docker compose up 时,两个文件会自动合并。端口变成了 80 和 8443 两个映射,volume 和环境变量也追加上去了。
如果你想更灵活,可以用 -f 参数手动指定多个文件:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
合并规则也不复杂: 单值字段后者覆盖前者,列表字段追加合并 。image 是单值,后面的文件会覆盖前面的;ports、volumes 是列表,会叠加在一起。
这招在区分开发和生产环境时特别好用。基础配置放主文件,开发环境的 debug 参数放 override,生产环境的资源限制放 prod 文件。各管各的,互不干扰。
profiles:按需启动,告别注释大法
你的 Compose 文件里有没有这种情况——调试工具、监控组件平时不需要,用的时候才启动?
以前的做法是注释掉再取消注释,丑得很。profiles 完美解决了这个问题。
services:
app:
image: myapp:latest
ports:
- "8080:8080"
debug-tools:
image: busybox
profiles:
- debug
command: sleep infinity
prometheus:
image: prom/prometheus
profiles:
- monitoring
ports:
- "9090:9090"
没有 profiles 标签的服务(比如 app)每次都会启动。带了标签的,只有显式激活才会跑:
# 只启动 app
docker compose up -d
# 启动 app + debug 工具
docker compose --profile debug up -d
# 全都要
docker compose --profile debug --profile monitoring up -d
干净利落,不用来回改文件。
depends_on 的正确打开方式
很多人知道 depends_on 能控制启动顺序,但只写一个服务名就完事了。问题是,容器启动了不代表服务就绪。数据库容器跑起来了,但 MySQL 还在初始化,应用就去连接——直接报错。
Compose 支持健康检查条件:
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
start_period: 30s
app:
image: myapp:latest
depends_on:
db:
condition: service_healthy
restart: true
condition: service_healthy 意味着 app 会等到 db 的健康检查通过才启动。restart: true 是个容易被忽略的参数——当 db 重启后,app 也会跟着重启。在数据库升级场景下非常实用。
start_period 也值得说一下:它给容器一个 " 宽限期 ",在这段时间内健康检查失败不算数。数据库第一次初始化可能要几十秒,没有这个参数会被误判为不健康。
资源限制:别让一个容器吃光服务器
生产环境不做资源限制就是在裸奔。一个内存泄漏就能把整台机器拖垮。
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: "2.0"
memory: 1G
reservations:
cpus: "0.5"
memory: 256M
limits 是硬上限,超了就 OOM Kill。reservations 是预留资源,Docker 会保证至少给你这么多。
说白了,limits 是天花板,reservations 是地板。
extension fields:消灭重复配置
多个服务用差不多的配置,复制粘贴改来改去?用 YAML 锚点和 Compose 的扩展字段。
x-common: &common-config
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- app-net
services:
api:
<<: *common-config
image: api-server:latest
ports:
- "8080:8080"
worker:
<<: *common-config
image: worker:latest
environment:
- QUEUE=default
x- 开头的顶级字段会被 Compose 忽略,专门用来存公共配置。<<: *common-config 把公共配置展开合并进来。日志策略、重启策略、网络配置——写一次,到处用。
几个容易踩的坑
env_file 的优先级 。同时用了 env_file 和 environment,后者会覆盖前者中的同名变量。另外 .env 文件是给 Compose 文件本身做变量替换的,和容器内的环境变量是两回事,别搞混了。
services:
app:
image: myapp:${APP_VERSION:-latest} # 从 .env 读取
env_file:
- ./app.env # 注入到容器内
environment:
- DB_HOST=production # 覆盖 app.env 中的 DB_HOST
init 参数 。加一行 init: true,容器里会跑一个 init 进程(tini)来转发信号、回收僵尸进程。不加这个,你的应用可能收不到 SIGTERM,docker compose down 每次都要等 10 秒超时强杀。
services:
app:
image: myapp:latest
init: true
tmpfs 挂载 。需要高速临时存储又不想写磁盘?比如上传文件的临时目录、应用缓存:
services:
app:
image: myapp:latest
tmpfs:
- /tmp:size=100M
- /app/cache:size=200M
基于内存,读写速度快,容器重启自动清空。
实用命令速查
最后分享几个日常高频但很多人不知道的命令:
# 只重建某个服务,不影响其他容器
docker compose up -d --no-deps --build api
# 查看最终合并后的完整配置(排查问题用)docker compose config
# 按服务查看日志,只看最近 100 行
docker compose logs --tail 100 -f api
# 在运行中的容器执行命令
docker compose exec db mysql -u root -p
# 暂停 / 恢复服务(不销毁容器)docker compose pause api
docker compose unpause api
其中 docker compose config 强烈建议养成习惯。用了多文件合并、变量替换、扩展字段之后,最终生效的配置到底长什么样?这个命令直接告诉你答案,省得瞎猜。
Docker Compose 的能力远不止 " 编排几个容器 " 这么简单。Override 机制、profiles、健康检查、资源限制、扩展字段——这些功能组合起来,足以应对大多数中小规模的部署场景。
与其急着上 K8s,不如先把手头的 Compose 用明白。
觉得有用的话,点个在看转发一下,让更多人少踩坑。
