2. Docker Compose 核心回顾:docker-compose.yml 结构、services/volumes/networks 定义、depends_on 与健康检查、环境变量注入
在构建多租户隔离栈之前,必须彻底掌握 Docker Compose 的核心构造块。本章将系统回顾 docker-compose.yml 的骨架结构、三大顶级元素(services、volumes、networks)的定义方式、服务依赖与健康检查的配合模式,以及环境变量注入的多种手段。这些知识是后续实现租户级网络隔离、存储隔离和启动顺序编排的基础。
2.1 docker-compose.yml 骨架结构
一个标准的 Compose 文件由版本声明、服务、卷、网络四个顶层块组成。以下是最小化但完整的骨架:
version: "3.9"
services:
app:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- app_data:/usr/share/nginx/html
networks:
- frontend
volumes:
app_data:
networks:
frontend:
driver: bridge
各顶层块的作用如下表所示:
| 顶层块 | 作用 | 多租户场景关注点 |
|---|---|---|
| services | 定义每个容器的镜像、端口、挂载、网络、命令等 | 每个租户对应一组独立 service 或通过变量复用同一 service 模板 |
| volumes | 声明持久化存储卷,可指定驱动、驱动选项、外部卷 | 租户数据必须使用独立卷,避免跨租户数据泄露 |
| networks | 定义容器间通信网络,支持 bridge、overlay、macvlan 等驱动 | 每个租户应有独立网络栈,默认 bridge 无法满足隔离需求 |
| configs / secrets | (Swarm 模式)注入配置文件和敏感信息 | 租户密钥应通过 secret 机制分发,避免明文写入镜像 |
networks 和 volumes,即使只使用默认值。这为后续扩展租户隔离策略保留了清晰的配置入口。
2.2 services 定义详解
services 块是 Compose 的核心,每个 service 对应一个或多个容器实例。以下是生产级 service 定义的常用字段:
services:
web:
image: myapp:latest
container_name: tenant-${TENANT_ID}-web
ports:
- "${HOST_PORT}:80"
environment:
- DB_HOST=db
- TENANT_ID=${TENANT_ID}
volumes:
- web_data:/var/www/html
networks:
- tenant_net
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
deploy:
replicas: 2
resources:
limits:
cpus: "0.5"
memory: "256M"
关键字段说明:
- image:指定镜像来源,支持私有仓库地址,多租户场景建议使用带租户标签的镜像版本。
- container_name:显式命名容器,便于日志和监控定位,通常结合环境变量动态生成。
- ports:端口映射,多租户时必须使用不同宿主机端口,通过变量
${HOST_PORT}避免冲突。 - environment:注入环境变量,是传递租户标识、数据库连接串的主要方式。
- volumes:挂载命名卷或绑定挂载,租户数据卷必须隔离。
- networks:指定容器加入的网络,每个租户应有独立网络。
- depends_on:控制启动顺序,配合 healthcheck 实现真正的就绪等待。
- healthcheck:定义容器健康检测命令,是 depends_on 条件判断的依据。
- deploy:Swarm 模式下的扩缩容和资源限制,单机 Compose 中部分字段仅作声明。
2.3 volumes 定义与租户数据隔离
volumes 块用于声明持久化存储。在多租户架构中,每个租户必须拥有独立的卷,避免数据交叉访问。以下是卷定义的三种常见模式:
| 模式 | 定义方式 | 适用场景 | 隔离级别 |
|---|---|---|---|
| 匿名卷 | volumes: - /data |
临时数据,不关心持久化位置 | 低(Docker 自动生成哈希名称) |
| 命名卷 | volumes: - app_data:/data |
需要显式管理的数据,如数据库存储 | 高(卷名可包含租户 ID) |
| 绑定挂载 | volumes: - ./data:/data |
开发环境或需要直接访问宿主机文件 | 中(依赖路径唯一性) |
租户隔离的卷命名策略示例:
volumes:
tenant_a_db_data:
driver: local
tenant_b_db_data:
driver: local
tenant_a_upload_data:
driver: local
driver_opts:
type: none
device: /mnt/ssd/tenant_a/uploads
o: bind
2.4 networks 定义与租户网络隔离
networks 块定义容器间的通信拓扑。默认的 bridge 网络无法满足多租户隔离需求,必须为每个租户创建独立的网络栈。以下是常用网络驱动对比:
| 驱动 | 隔离性 | 跨主机 | 多租户适用性 |
|---|---|---|---|
| bridge | 中等(通过子网隔离) | 否 | 单机多租户,每个租户一个 bridge |
| overlay | 高(VXLAN 封装) | 是 | Swarm 集群多租户,推荐生产使用 |
| macvlan | 高(独立 MAC/IP) | 是(需物理网络支持) | 需要容器直接暴露在物理网络时使用 |
| ipvlan | 高(共享 MAC) | 是 | macvlan 的轻量替代,适合大规模租户 |
租户网络定义示例:
networks:
tenant_a_net:
driver: bridge
ipam:
config:
- subnet: "172.20.1.0/24"
gateway: "172.20.1.1"
tenant_b_net:
driver: bridge
ipam:
config:
- subnet: "172.20.2.0/24"
gateway: "172.20.2.1"
每个租户的 service 通过 networks: - tenant_x_net 加入对应网络,确保租户 A 的容器无法直接访问租户 B 的容器。
2.5 depends_on 与健康检查
depends_on 控制服务启动顺序,但默认只等待容器启动(service_started),不等待服务就绪。生产环境必须配合 healthcheck 使用 condition: service_healthy 实现真正的就绪等待。
三种 depends_on 条件对比:
| 条件 | 行为 | 适用场景 |
|---|---|---|
service_started |
仅等待容器进程启动(默认) | 无依赖或依赖方启动极快 |
service_healthy |
等待健康检查通过 | 数据库、缓存等需要就绪后才能接受连接的服务 |
service_completed_successfully |
等待容器正常退出(exit 0) | 初始化任务、数据库迁移等一次性作业 |
完整示例:
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
app:
image: myapp:latest
depends_on:
db:
condition: service_healthy
environment:
DB_HOST: db
depends_on 只是“启动顺序保证”,不是“就绪保证”。在数据库迁移或缓存预热场景中,必须显式定义 healthcheck 并设置合理的 start_period 避免误判。
2.6 环境变量注入的四种方式
环境变量是向容器传递租户配置、数据库连接、密钥等动态信息的主要手段。Compose 支持以下四种注入方式:
| 方式 | 语法 | 优先级 | 多租户场景推荐 |
|---|---|---|---|
| 硬编码 | environment: - KEY=value |
最高 | 不推荐,泄露敏感信息且无法复用 |
| 宿主变量引用 | environment: - KEY=${VAR_NAME} |
中 | 适合通过 .env 文件或 shell 导出变量 |
| env_file | env_file: ./tenant_a.env |
低 | 每个租户一个 env 文件,结构清晰 |
| Docker Secrets | secrets: - db_password |
中(Swarm 模式) | 生产环境敏感信息,加密存储 |
推荐的多租户 env_file 策略:
# 目录结构
config/
tenant_a.env
tenant_b.env
# tenant_a.env 内容
TENANT_ID=a
DB_NAME=tenant_a_db
DB_USER=tenant_a_user
DB_PASSWORD=pass_a_123
REDIS_DB=0
# docker-compose.yml 引用
services:
app:
env_file:
- ./config/tenant_${TENANT_ID}.env
env_file 时,Compose 会按文件顺序加载,后加载的变量会覆盖先加载的同名变量。将公共变量放在基础 env 文件中,租户特有变量放在租户文件中,实现分层配置。
2.7 本章小结
Docker Compose 的核心回顾为多租户隔离栈的构建奠定了语法和概念基础。需要牢记的关键点:
- services 是容器模板,通过环境变量和卷挂载实现租户差异化配置。
- volumes 必须按租户独立命名,避免数据交叉。
- networks 必须按租户独立创建,使用不同子网实现二层隔离。
- depends_on + healthcheck 是生产级启动顺序控制的标配。
- 环境变量注入 优先使用
env_file配合.env文件,敏感信息使用 Docker Secrets。