4. Docker Compose编排:服务定义、网络配置、卷挂载、环境变量管理
说实话,Docker Compose 是我日常工作中用得最多的工具之一。你想想看,一个微服务项目动辄十几个服务,要是每个都手动 docker run,那不得累死?Compose 就是来解决这个问题的——用一个 YAML 文件,把整个应用栈描述清楚。
我个人习惯把 Compose 文件叫做「项目的说明书」。谁依赖谁、端口怎么映射、数据存哪里、环境变量配什么,全写在一个文件里。团队里新同学来了,看一眼 docker-compose.yml,基本就知道系统长什么样了。
4.1 服务定义:把每个容器说清楚
服务定义是 Compose 的核心。说白了,就是告诉 Docker:「我要跑哪些容器,每个容器用什么镜像、开什么端口、挂什么卷。」
来看一个我项目里用过的例子:
version: '3.8'
services:
web:
image: nginx:1.25-alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- api
api:
build: ./backend
ports:
- "3000:3000"
environment:
- DB_HOST=db
- DB_PORT=5432
depends_on:
db:
condition: service_healthy
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
这里有几个关键点:
- image vs build:直接用现成镜像就用 image,需要自己构建就用 build。我建议开发阶段用 build,生产环境用 image 加 tag 锁定版本。
- depends_on:控制启动顺序。但注意,它只保证容器启动了,不保证服务就绪了。所以我加了 healthcheck,让 api 等 db 真正健康了再启动。
- ports:左边是宿主机端口,右边是容器端口。记得别冲突,我见过好几次 8080 被占用的惨案。
4.2 网络配置:容器间怎么通信
默认情况下,Compose 会为项目创建一个网络,所有服务都在这个网络里。但有时候我们需要更精细的控制。
为什么会这样?因为有些服务需要隔离,有些需要对外暴露。比如数据库通常只在内部网络访问,而 Nginx 需要暴露到外网。
我在项目中遇到过这样的场景:前端和后端需要通信,但数据库只对后端开放。我的做法是定义两个网络:
services:
frontend:
image: nginx
networks:
- frontend
- backend
backend:
image: my-api
networks:
- backend
database:
image: postgres
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
这样 frontend 能访问 backend,但碰不到 database。backend 和 database 在同一个 backend 网络里,可以互相通信。
4.3 卷挂载:数据不能丢
容器是无状态的,重启后数据就没了。所以数据库、日志、上传文件这些,必须挂载到宿主机或卷里。
卷挂载有两种方式:
| 类型 | 写法 | 适用场景 |
|---|---|---|
| 绑定挂载 | ./data:/app/data | 开发调试,直接改宿主机文件 |
| 命名卷 | myvolume:/app/data | 生产环境,由 Docker 管理 |
我个人习惯开发时用绑定挂载,改代码立竿见影。生产环境用命名卷,方便备份和迁移。
来看一个完整的卷配置:
services:
app:
image: my-app
volumes:
- ./src:/app/src # 开发时热更新
- uploads:/app/uploads # 上传文件持久化
- /etc/localtime:/etc/localtime:ro # 时区同步
volumes:
uploads:
driver: local
嗯,这里要注意:绑定挂载的路径必须是绝对路径或相对路径(以 ./ 或 ../ 开头)。命名卷需要在文件底部声明,否则 Compose 会报错。
4.4 环境变量管理:配置与代码分离
12-Factor App 告诉我们,配置要存到环境变量里。Compose 支持多种方式管理环境变量:
- 直接写在文件里:简单粗暴,适合小项目
- 使用 .env 文件:把变量抽出来,Compose 自动加载
- 使用 env_file:指定外部文件,适合多环境
来看一个实际项目的配置:
services:
api:
image: my-api
environment:
- NODE_ENV=production
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- REDIS_URL=${REDIS_URL}
env_file:
- ./config/${ENV}/db.env
- ./config/${ENV}/redis.env
对应的 .env 文件:
DB_HOST=localhost
DB_PORT=5432
REDIS_URL=redis://localhost:6379
ENV=dev
4.5 知识体系总览
下面这张图是我自己总结的 Compose 编排核心逻辑,你看一眼就能明白各个模块的关系:
这张图把四个核心模块串起来了。你写 Compose 文件时,就按这个思路来:先定义服务,再配网络,然后挂数据,最后管配置。顺序对了,基本不会出大问题。
公众号:蓝海资料掘金营,微信deep3321