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 节点通过环境变量动态切换后端数据库连接串,实现版本适配测试。
集成测试环境 Docker Compose 容器拓扑 集成测试环境容器拓扑 PostgreSQL 版本矩阵 pg12 (端口 5432) pg13 (端口 5433) pg14 (端口 5434) pg15 (端口 5435) pg16 (端口 5436) RabbitMQ 集群 mq-node1 (5672) 后端 DB: pg13 mq-node2 (5673) 后端 DB: pg14 mq-node3 (5674) 后端 DB: pg15 测试执行器 pytest + pika/psycopg2 版本适配断言 故障注入脚本 共享网络: testnet

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)加速测试。
数据持久化与卷映射关系 数据持久化与卷映射关系 容器内部路径 pg12: /var/lib/postgresql/data pg13: /var/lib/postgresql/data mq-node1: /var/lib/rabbitmq mq-node2: /var/lib/rabbitmq mq-node3: /var/lib/rabbitmq Docker 命名卷 pg12_data pg13_data mq_node1_data mq_node2_data / mq_node3_data 卷生命周期 docker compose down -v 删除所有卷 测试前建议清理

4.6 启动与验证步骤

  1. 启动所有服务:docker compose up -d,等待健康检查通过。
  2. 验证 PG 版本:docker exec pg13 psql -U mqtest -c "SELECT version();"
  3. 验证 MQ 集群:docker exec mq-node1 rabbitmqctl cluster_status
  4. 运行测试:docker compose run --rm test-runner
  5. 清理环境:docker compose down -v(CI 中每次运行前执行)
版本适配测试矩阵: 测试执行器会依次将每个 MQ 节点的后端数据库切换到不同 PG 版本,执行相同的 200+ 个集成测试用例,记录通过/失败/异常模式。结果汇总为版本兼容性热力图,用于快速定位特定 PG 版本引入的回归问题。