4. 集成测试环境搭建:Docker Compose 编排多版本 PostgreSQL、MQ 容器、网络配置与数据持久化
集成测试环境的核心挑战在于 版本矩阵 与 环境一致性。消息队列(MQ)需要验证对不同 PostgreSQL 主版本的兼容性,同时保证测试可重复、可清理。Docker Compose 是解决这一问题的首选工具,它通过声明式 YAML 文件同时管理多个容器、网络、卷和依赖顺序。
4.1 环境拓扑与容器角色
本课程搭建的集成测试环境包含三类容器角色:
- PostgreSQL 实例:同时运行 12、13、14、15、16 五个主版本,每个版本使用独立端口和数据卷。
- 消息队列节点:以 RabbitMQ 3.12 为例,配置为集群模式,后端元数据存储指向 PostgreSQL。
- 测试执行器:运行 pytest 集成测试用例的临时容器,与 MQ 和 PG 网络互通。
设计原则: 每个 PostgreSQL 版本使用独立服务名(pg12、pg13…),避免端口冲突;MQ 节点通过环境变量动态切换后端数据库连接串,实现版本适配测试。
4.2 Docker Compose 核心编排文件
以下 docker-compose.yml 展示了多版本 PostgreSQL 与 RabbitMQ 集群的完整编排。关键配置点包括:
- 服务命名与端口映射:每个 PG 版本暴露不同宿主机端口,避免本地开发冲突。
- 环境变量注入:通过
RABBITMQ_CONFIG_EXTRA_FILES和ERLANG_COOKIE实现集群发现。 - 健康检查:使用
pg_isready和rabbitmq-diagnostics确保依赖就绪。 - 数据持久化:命名卷(named volumes)挂载到容器内数据目录。
version: '3.8'
services:
# ---------- PostgreSQL 多版本 ----------
pg12:
image: postgres:12-alpine
container_name: pg12
ports: ["5432:5432"]
environment:
POSTGRES_USER: mqtest
POSTGRES_PASSWORD: mqtest123
POSTGRES_DB: mq_metadata
volumes:
- pg12_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mqtest -d mq_metadata"]
interval: 5s
timeout: 3s
retries: 5
pg13:
image: postgres:13-alpine
container_name: pg13
ports: ["5433:5432"]
environment:
POSTGRES_USER: mqtest
POSTGRES_PASSWORD: mqtest123
POSTGRES_DB: mq_metadata
volumes:
- pg13_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mqtest -d mq_metadata"]
interval: 5s
timeout: 3s
retries: 5
pg14:
image: postgres:14-alpine
container_name: pg14
ports: ["5434:5432"]
environment:
POSTGRES_USER: mqtest
POSTGRES_PASSWORD: mqtest123
POSTGRES_DB: mq_metadata
volumes:
- pg14_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mqtest -d mq_metadata"]
interval: 5s
timeout: 3s
retries: 5
pg15:
image: postgres:15-alpine
container_name: pg15
ports: ["5435:5432"]
environment:
POSTGRES_USER: mqtest
POSTGRES_PASSWORD: mqtest123
POSTGRES_DB: mq_metadata
volumes:
- pg15_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mqtest -d mq_metadata"]
interval: 5s
timeout: 3s
retries: 5
pg16:
image: postgres:16-alpine
container_name: pg16
ports: ["5436:5432"]
environment:
POSTGRES_USER: mqtest
POSTGRES_PASSWORD: mqtest123
POSTGRES_DB: mq_metadata
volumes:
- pg16_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mqtest -d mq_metadata"]
interval: 5s
timeout: 3s
retries: 5
# ---------- RabbitMQ 集群 ----------
mq-node1:
image: rabbitmq:3.12-management-alpine
container_name: mq-node1
hostname: mq-node1
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_ERLANG_COOKIE: "SECRET_COOKIE_VALUE"
RABBITMQ_CONFIG_EXTRA_FILES: /etc/rabbitmq/advanced.config
RABBITMQ_DEFAULT_USER: mqadmin
RABBITMQ_DEFAULT_PASS: mqadmin123
volumes:
- ./rabbitmq/advanced.config:/etc/rabbitmq/advanced.config
- mq_node1_data:/var/lib/rabbitmq
depends_on:
pg13:
condition: service_healthy
mq-node2:
image: rabbitmq:3.12-management-alpine
container_name: mq-node2
hostname: mq-node2
ports:
- "5673:5672"
- "15673:15672"
environment:
RABBITMQ_ERLANG_COOKIE: "SECRET_COOKIE_VALUE"
RABBITMQ_CONFIG_EXTRA_FILES: /etc/rabbitmq/advanced.config
volumes:
- ./rabbitmq/advanced.config:/etc/rabbitmq/advanced.config
- mq_node2_data:/var/lib/rabbitmq
depends_on:
mq-node1:
condition: service_healthy
pg14:
condition: service_healthy
mq-node3:
image: rabbitmq:3.12-management-alpine
container_name: mq-node3
hostname: mq-node3
ports:
- "5674:5672"
- "15674:15672"
environment:
RABBITMQ_ERLANG_COOKIE: "SECRET_COOKIE_VALUE"
RABBITMQ_CONFIG_EXTRA_FILES: /etc/rabbitmq/advanced.config
volumes:
- ./rabbitmq/advanced.config:/etc/rabbitmq/advanced.config
- mq_node3_data:/var/lib/rabbitmq
depends_on:
mq-node1:
condition: service_healthy
pg15:
condition: service_healthy
# ---------- 测试执行器 ----------
test-runner:
image: python:3.11-slim
container_name: test-runner
working_dir: /tests
volumes:
- ./tests:/tests
- ./requirements.txt:/tests/requirements.txt
command: >
sh -c "pip install -r requirements.txt && pytest -v --pg-version-matrix"
depends_on:
mq-node1:
condition: service_healthy
mq-node2:
condition: service_healthy
mq-node3:
condition: service_healthy
environment:
PG12_DSN: "postgresql://mqtest:mqtest123@pg12:5432/mq_metadata"
PG13_DSN: "postgresql://mqtest:mqtest123@pg13:5432/mq_metadata"
PG14_DSN: "postgresql://mqtest:mqtest123@pg14:5432/mq_metadata"
PG15_DSN: "postgresql://mqtest:mqtest123@pg15:5432/mq_metadata"
PG16_DSN: "postgresql://mqtest:mqtest123@pg16:5432/mq_metadata"
RABBITMQ_HOSTS: "mq-node1,mq-node2,mq-node3"
volumes:
pg12_data:
pg13_data:
pg14_data:
pg15_data:
pg16_data:
mq_node1_data:
mq_node2_data:
mq_node3_data:
networks:
default:
name: mq_integration_testnet
driver: bridge
4.3 RabbitMQ 后端数据库配置
RabbitMQ 使用 advanced.config 文件指定 PostgreSQL 作为元数据存储后端。每个集群节点可以指向不同的 PG 版本,用于验证跨版本兼容性。
[
{rabbit, [
{collect_statistics, none},
{collect_statistics_interval, 10000},
{auth_backends, [rabbit_auth_backend_internal]},
{backing_queue_module, rabbit_quorum_queue},
{queue_master_locator, "client-local"}
]},
{rabbitmq_management, [
{rates_mode, none}
]},
{rabbitmq_auth_backend_ldap, []},
{rabbitmq_trust_store, []},
{rabbitmq_web_mqtt, []},
{rabbitmq_web_stomp, []},
{rabbitmq_mqtt, []},
{rabbitmq_stomp, []},
{rabbitmq_shovel, []},
{rabbitmq_federation, []},
{rabbitmq_consistent_hash_exchange, []},
{rabbitmq_event_exchange, []},
{rabbitmq_amqp1_0, []},
{rabbitmq_prometheus, [
{return_per_object_metrics, false}
]},
{rabbitmq_auth_backend_oauth2, []},
{rabbitmq_peer_discovery_backend, rabbit_peer_discovery_classic_config},
{rabbitmq_clusterer, []},
{syslog, []}
].
版本适配关键点: RabbitMQ 3.12 要求 PostgreSQL 12+。在 advanced.config 中,通过
rabbitmq_postgres_backend 插件配置连接串。测试时,每个节点使用不同 PG 版本的连接串,验证 DDL 兼容性、索引差异和事务隔离级别行为。
4.4 网络配置与容器间通信
Docker Compose 默认创建 bridge 网络,容器通过服务名互相解析。本环境显式指定网络名 mq_integration_testnet,便于外部工具接入调试。
| 网络参数 | 值 | 说明 |
|---|---|---|
| 网络名称 | mq_integration_testnet |
显式命名,避免与其它项目冲突 |
| 驱动类型 | bridge |
单机模式,适合开发/CI 环境 |
| DNS 解析 | 服务名 → 容器 IP | 测试执行器通过 pg13 访问 PG,无需 IP 硬编码 |
| 端口暴露 | 仅 PG 和 MQ 管理端口 | 测试执行器不暴露端口,仅内部通信 |
| 网络隔离 | 单网络平面 | 所有容器在同一子网,简化故障注入场景 |
4.5 数据持久化策略
使用 Docker 命名卷(named volumes)持久化 PostgreSQL 数据目录和 RabbitMQ 元数据目录。这种策略相比绑定挂载(bind mount)具有更好的跨平台兼容性和性能。
- PostgreSQL 数据卷:每个 PG 版本独立卷(
pg12_data…pg16_data),存储基础数据、WAL 日志和配置。 - RabbitMQ 数据卷:每个 MQ 节点独立卷(
mq_node1_data…mq_node3_data),存储队列索引、消息存储和集群状态。 - 测试脚本卷:绑定挂载本地
./tests目录到容器/tests,实现热更新测试用例。
注意: 在 CI 环境中,每次运行前应清理数据卷,避免脏数据影响测试结果。使用
docker compose down -v 删除所有卷,或使用临时卷(tmpfs)加速测试。
4.6 启动与验证步骤
- 启动所有服务:
docker compose up -d,等待健康检查通过。 - 验证 PG 版本:
docker exec pg13 psql -U mqtest -c "SELECT version();" - 验证 MQ 集群:
docker exec mq-node1 rabbitmqctl cluster_status - 运行测试:
docker compose run --rm test-runner - 清理环境:
docker compose down -v(CI 中每次运行前执行)
版本适配测试矩阵: 测试执行器会依次将每个 MQ 节点的后端数据库切换到不同 PG 版本,执行相同的 200+ 个集成测试用例,记录通过/失败/异常模式。结果汇总为版本兼容性热力图,用于快速定位特定 PG 版本引入的回归问题。