4. 回放环境搭建:基于 Docker Compose 的轻量级集成测试环境搭建步骤

在性能调优实践中,回放环境的核心目标是快速、可重复、隔离地复现生产流量。基于 Docker Compose 的轻量级集成测试环境,能够将 Report Export 服务及其依赖(数据库、消息队列、缓存)在数分钟内启动,并确保每次回放的环境一致性。

4.1 环境架构概览

下图展示了回放环境的服务拓扑。所有组件通过 Docker Compose 编排,运行在同一个虚拟网络中,实现服务间低延迟通信。

回放环境服务拓扑(Docker Compose) 回放引擎 replay-engine:1.0 端口映射: 8080:8080 流量注入 + 结果采集 Report Export 服务 report-export:2.3.1 端口映射: 9090:9090 导出报表业务逻辑 MySQL 数据库 mysql:8.0 端口映射: 3306:3306 报表元数据 + 业务数据 Redis 缓存 redis:7-alpine 端口映射: 6379:6379 RabbitMQ 消息队列 rabbitmq:3-management 端口映射: 5672:5672 / 15672:15672 MinIO 对象存储 minio/minio:latest 端口映射: 9000:9000 / 9001:9001 说明: 所有服务通过 docker-compose 定义的网络互联,服务名即主机名。回放引擎向 Report Export 注入流量,后者依赖 MySQL、Redis、RabbitMQ 和 MinIO 完成报表生成。 端口映射仅用于外部调试,服务间内部通信使用容器名 + 内部端口。

4.2 环境准备与前置条件

在开始搭建之前,请确保宿主机满足以下条件。推荐使用 Linux 或 macOS 作为运行平台,Windows 可通过 WSL2 获得一致体验。

组件 最低版本 验证命令 备注
Docker Engine 20.10+ docker --version 支持 Compose V2
Docker Compose 2.15+ docker compose version 建议使用插件版
Git 2.30+ git --version 用于拉取配置仓库
可用内存 ≥ 8 GB free -h 回放期间建议 ≥ 16 GB
磁盘空间 ≥ 20 GB df -h 包含镜像与测试数据
💡 提示:如果宿主机资源有限,可以只启动 Report Export + MySQL + Redis 三个核心服务,跳过 RabbitMQ 和 MinIO,使用本地文件系统替代对象存储。但回放结果可能与生产环境存在偏差。

4.3 搭建步骤详解

以下步骤从零开始构建完整的回放环境。请按顺序执行,每一步都附带验证方法。

步骤 1:创建项目目录与配置文件

在宿主机上创建专用的回放环境目录,并准备 docker-compose.yml 文件。

mkdir -p ~/replay-env && cd ~/replay-env
touch docker-compose.yml .env

编辑 .env 文件,定义环境变量,避免敏感信息硬编码:

# .env 文件
MYSQL_ROOT_PASSWORD=replay_root_2024
MYSQL_DATABASE=report_export
MYSQL_USER=replay_user
MYSQL_PASSWORD=replay_pass_2024
REDIS_PASSWORD=redis_replay_2024
RABBITMQ_DEFAULT_USER=replay_mq
RABBITMQ_DEFAULT_PASS=mq_replay_2024
MINIO_ROOT_USER=minio_replay
MINIO_ROOT_PASSWORD=minio_replay_2024
REPLAY_ENGINE_VERSION=1.0.0
REPORT_EXPORT_VERSION=2.3.1

步骤 2:编写 docker-compose.yml 核心编排文件

以下为完整的编排文件,定义了 6 个服务及其依赖关系。注意服务启动顺序:数据库和中间件先于业务服务启动。

version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: replay-mysql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init-scripts/mysql:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - replay-net

  redis:
    image: redis:7-alpine
    container_name: replay-redis
    command: redis-server --requirepass ${REDIS_PASSWORD}
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - replay-net

  rabbitmq:
    image: rabbitmq:3-management
    container_name: replay-rabbitmq
    environment:
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
    ports:
      - "5672:5672"
      - "15672:15672"
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "ping"]
      interval: 15s
      timeout: 10s
      retries: 5
    networks:
      - replay-net

  minio:
    image: minio/minio:latest
    container_name: replay-minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - minio_data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 15s
      timeout: 10s
      retries: 5
    networks:
      - replay-net

  report-export:
    image: registry.example.com/report-export:${REPORT_EXPORT_VERSION}
    container_name: replay-report-export
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
      minio:
        condition: service_healthy
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useSSL=false
      SPRING_DATASOURCE_USERNAME: ${MYSQL_USER}
      SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD}
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PASSWORD: ${REDIS_PASSWORD}
      SPRING_RABBITMQ_HOST: rabbitmq
      SPRING_RABBITMQ_USERNAME: ${RABBITMQ_DEFAULT_USER}
      SPRING_RABBITMQ_PASSWORD: ${RABBITMQ_DEFAULT_PASS}
      MINIO_ENDPOINT: http://minio:9000
      MINIO_ACCESS_KEY: ${MINIO_ROOT_USER}
      MINIO_SECRET_KEY: ${MINIO_ROOT_PASSWORD}
    ports:
      - "9090:9090"
    volumes:
      - ./logs/report-export:/app/logs
    networks:
      - replay-net

  replay-engine:
    image: registry.example.com/replay-engine:${REPLAY_ENGINE_VERSION}
    container_name: replay-engine
    depends_on:
      - report-export
    environment:
      TARGET_URL: http://report-export:9090
      REPLAY_MODE: integrated
      OUTPUT_DIR: /replay-results
    ports:
      - "8080:8080"
    volumes:
      - ./replay-results:/replay-results
      - ./replay-scenarios:/replay-scenarios
    networks:
      - replay-net

volumes:
  mysql_data:
  redis_data:
  rabbitmq_data:
  minio_data:

networks:
  replay-net:
    driver: bridge

步骤 3:初始化数据库与测试数据

创建 init-scripts/mysql 目录,放入建表脚本和基础数据。回放环境需要与生产环境一致的 Schema。

mkdir -p init-scripts/mysql
cat > init-scripts/mysql/01_schema.sql << 'EOF'
CREATE TABLE IF NOT EXISTS report_templates (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    config JSON NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS export_tasks (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    template_id BIGINT NOT NULL,
    status VARCHAR(32) DEFAULT 'PENDING',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (template_id) REFERENCES report_templates(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
EOF

cat > init-scripts/mysql/02_seed_data.sql << 'EOF'
INSERT INTO report_templates (id, name, config) VALUES
(1, '月度销售报表', '{"format":"xlsx","filters":{"date_range":"last_month"}}'),
(2, '年度汇总报表', '{"format":"pdf","filters":{"date_range":"last_year"}}');
EOF

步骤 4:启动环境并验证

使用以下命令启动所有服务,并观察健康状态。

# 启动所有服务(后台模式)
docker compose up -d

# 查看服务启动日志
docker compose logs -f

# 等待所有服务健康检查通过后,验证服务状态
docker compose ps

# 测试 Report Export 服务健康端点
curl -s http://localhost:9090/actuator/health | jq .
✅ 预期输出:所有服务状态为 Up(healthy),Report Export 返回 {"status":"UP"}。如果某个服务启动失败,使用 docker compose logs <service_name> 查看详细错误。

4.4 环境验证清单

在开始回放之前,建议执行以下验证步骤,确保环境功能完整。

验证项 验证方法 预期结果
MySQL 连接 docker exec replay-mysql mysql -u replay_user -p'replay_pass_2024' -e "SHOW DATABASES;" 显示 report_export 数据库
Redis 连接 docker exec replay-redis redis-cli -a 'redis_replay_2024' PING 返回 PONG
RabbitMQ 管理界面 浏览器访问 http://localhost:15672 使用 replay_mq / mq_replay_2024 登录成功
MinIO 控制台 浏览器访问 http://localhost:9001 使用 minio_replay / minio_replay_2024 登录成功
Report Export API curl -s http://localhost:9090/api/report/templates 返回包含 2 条模板的 JSON 数组
服务间网络通信 docker exec replay-engine ping report-export -c 2 丢包率 0%

4.5 常见问题与避坑指南

基于多次搭建经验,以下问题最容易出现,请提前规避。

  • 端口冲突:宿主机上已运行的 MySQL/Redis 等会占用 3306/6379 端口。解决方案:修改 docker-compose.yml 中的宿主机映射端口,例如 "3307:3306"。
  • 健康检查超时:MySQL 首次启动需要初始化数据,可能超过默认重试次数。解决方案:增加 healthcheck 的 retries 到 10,或手动等待 30 秒后再启动依赖服务。
  • 镜像拉取失败:私有仓库需要登录。解决方案:先执行 docker login registry.example.com,或在 docker-compose.yml 中指定 image: mysql:8.0 等公共镜像测试。
  • 数据持久化冲突:多次启动时,volume 中的旧数据可能导致 Schema 不一致。解决方案:使用 docker compose down -v 清除 volume 后重新启动。
  • 网络 DNS 解析:服务间使用容器名通信,如果自定义网络配置不当,可能导致 Name or service not known。解决方案:确保所有服务都在同一个 replay-net 网络中。
⚠️ 注意:回放环境仅用于性能测试,严禁在生产环境直接使用本配置。生产环境应使用 Kubernetes 或独立部署,并配置 TLS、防火墙、资源限制等安全措施。

4.6 环境清理与重置

每次回放结束后,建议清理环境以释放资源,避免下次回放受到残留数据影响。

# 停止所有服务并保留数据卷
docker compose down

# 停止所有服务并删除数据卷(完全重置)
docker compose down -v

# 删除所有相关镜像(可选)
docker compose down --rmi all

# 清理未使用的网络
docker network prune -f

如果需要保留测试结果,请在执行 down -v 之前将 ./replay-results 目录备份到其他位置。

Docker Compose 回放环境搭建流程 步骤 1 创建目录与 .env 步骤 2 编写 docker-compose.yml 步骤 3 初始化 SQL 脚本 步骤 4 启动与验证 验证 1:docker compose ps 所有服务状态 Up 验证 2:健康检查端点 curl /actuator/health 验证 3:API 功能测试 curl /api/report/templates 关键原则: 1. 所有配置通过 .env 文件管理,避免硬编码敏感信息。 2. 服务依赖使用 healthcheck + depends_on 确保启动顺序。 3. 每次回放前执行验证清单,确保环境一致性。

4.7 小结

通过 Docker Compose,我们成功搭建了一个包含 6 个核心服务的轻量级集成测试环境。该环境具有以下特点:

  • 快速启动:首次构建约 3-5 分钟,后续启动仅需 30 秒。
  • 环境隔离:每个回放任务可使用独立的 Compose 项目,互不干扰。
  • 配置即代码:所有环境定义在 docker-compose.yml 和 .env 中,可版本化管理。
  • 易于清理:一条命令即可销毁环境,释放资源。