4. 回放环境搭建:基于 Docker Compose 的轻量级集成测试环境搭建步骤
在性能调优实践中,回放环境的核心目标是快速、可重复、隔离地复现生产流量。基于 Docker Compose 的轻量级集成测试环境,能够将 Report Export 服务及其依赖(数据库、消息队列、缓存)在数分钟内启动,并确保每次回放的环境一致性。
4.1 环境架构概览
下图展示了回放环境的服务拓扑。所有组件通过 Docker Compose 编排,运行在同一个虚拟网络中,实现服务间低延迟通信。
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 目录备份到其他位置。
4.7 小结
通过 Docker Compose,我们成功搭建了一个包含 6 个核心服务的轻量级集成测试环境。该环境具有以下特点:
- 快速启动:首次构建约 3-5 分钟,后续启动仅需 30 秒。
- 环境隔离:每个回放任务可使用独立的 Compose 项目,互不干扰。
- 配置即代码:所有环境定义在
docker-compose.yml和.env中,可版本化管理。 - 易于清理:一条命令即可销毁环境,释放资源。