第二章:测试环境搭建——Docker容器化部署、模拟行情源配置、日志与监控系统初始化
说实话,搭建测试环境这件事,我见过太多团队栽跟头了。
有人花了两周配环境,结果发现行情源地址写错了。有人跑了一晚上回测,第二天发现日志根本没写进去。嗯,这些坑我都踩过。所以这一章,咱们就把环境搭建这件事,一次说清楚。
2.1 为什么非要用Docker?
你想想看,量化测试最怕什么?
最怕「在我机器上能跑,到你那就崩了」。我早期做事件驱动系统时,就因为Python版本差了0.1,折腾了整整一个下午。后来我学乖了——直接用Docker把整个环境打包。
核心思路:把操作系统、依赖库、配置文件、行情源地址,全部写进Dockerfile里。任何人拉下来,docker-compose up 一键启动。
我个人习惯用三层镜像结构:
# 基础层:Python 3.9 + 系统依赖
FROM python:3.9-slim AS base
RUN apt-get update && apt-get install -y \
gcc \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
# 依赖层:安装量化库
FROM base AS dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 应用层:放代码
FROM dependencies AS app
COPY ./src /app/src
COPY ./config /app/config
WORKDIR /app
CMD ["python", "src/main.py"]
为什么要分三层?因为这样改代码时,只需要重新构建最上层,不用每次都重装依赖。我在项目中遇到过,有人把所有东西写在一层里,每次改个配置文件都要等10分钟编译——太浪费时间了。
2.2 模拟行情源配置
行情源是事件驱动系统的「燃料」。没有行情,系统就是个空壳。
但真实行情源太贵了,而且测试时你也不想真的去交易。所以我们需要一个模拟行情源。
我常用的方案是:
- 回放模式:把历史行情数据存成CSV或Parquet,按时间戳逐条推送
- 随机生成模式:用随机数模拟价格波动,适合压力测试
- 混合模式:先回放一段历史数据,然后切换到随机生成
这里给个简单的模拟行情源配置示例:
# config/market_simulator.yaml
simulator:
mode: replay # replay | random | mixed
data_path: /data/historical/2024-01-01.parquet
speed: 1.0 # 1.0表示实时速度,2.0表示两倍速
symbols:
- BTC/USDT
- ETH/USDT
- SOL/USDT
random_config:
base_price: 50000
volatility: 0.002 # 0.2%的波动率
interval_ms: 100 # 每100ms推送一次
小技巧:我建议在模拟行情源里加一个「异常注入」开关。比如随机插入一些空数据、重复数据、或者价格跳变。这样能提前测试你的系统容错能力。我曾经因为没测这个,上线第一天就被一个异常行情搞崩了。
2.3 日志系统初始化
日志这东西,平时没人看,出问题的时候才觉得它珍贵。
我见过最惨的情况是:系统崩了,但日志里只有一行 Error: something went wrong。这跟没写有什么区别?
所以我的日志系统有三个原则:
- 结构化:用JSON格式,方便后续分析
- 分级别:DEBUG、INFO、WARNING、ERROR,一个都不能少
- 带上下文:每条日志都要包含事件ID、时间戳、模块名
这是我常用的日志配置:
# config/logging.yaml
version: 1
formatters:
json:
format: '{"time": "%(asctime)s", "level": "%(levelname)s", "module": "%(module)s", "event_id": "%(event_id)s", "message": "%(message)s"}'
handlers:
console:
class: logging.StreamHandler
level: INFO
formatter: json
file:
class: logging.handlers.RotatingFileHandler
filename: /var/log/trading_system.log
maxBytes: 10485760 # 10MB
backupCount: 5
level: DEBUG
formatter: json
loggers:
market:
level: DEBUG
handlers: [console, file]
strategy:
level: INFO
handlers: [console, file]
risk:
level: WARNING
handlers: [file]
注意:千万别把日志写到容器内部!容器一重启,日志就没了。我早期犯过这个错,排查问题时发现日志全丢了,那种绝望感...嗯,你懂的。一定要用volume挂载到宿主机。
2.4 监控系统初始化
日志是事后诸葛亮,监控才是实时保镖。
我个人习惯用Prometheus + Grafana这套组合。轻量、开源、社区活跃。
监控的核心指标:
| 指标类别 | 具体指标 | 告警阈值 |
|---|---|---|
| 行情延迟 | 行情到达时间 - 行情生成时间 | > 500ms |
| 事件处理速率 | 每秒处理的事件数 | < 1000 events/s |
| 内存使用率 | 进程RSS / 总内存 | > 80% |
| 错误率 | ERROR日志数 / 总日志数 | > 1% |
这里有个小坑:监控本身也会消耗资源。我见过有人把监控粒度设到1秒,结果监控进程占用了20%的CPU。建议行情延迟用1秒粒度,其他指标用10秒粒度就够了。
2.5 整体架构图
说了这么多,咱们用一张图把整个环境串起来:
这张图说白了就是:行情源进来,事件引擎处理,日志和监控全程盯着。三个模块各司其职,互不干扰。
2.6 一键启动脚本
最后,给个完整的 docker-compose.yml:
version: '3.8'
services:
market-simulator:
build: ./market-simulator
volumes:
- ./data:/data
- ./config:/app/config
environment:
- MODE=replay
- SPEED=1.0
networks:
- trading-net
event-engine:
build: ./event-engine
depends_on:
- market-simulator
volumes:
- ./logs:/var/log
- ./config:/app/config
environment:
- LOG_LEVEL=DEBUG
networks:
- trading-net
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- trading-net
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
networks:
- trading-net
networks:
trading-net:
driver: bridge
启动命令:docker-compose up -d
查看日志:docker-compose logs -f event-engine
访问Grafana:浏览器打开 http://localhost:3000,默认账号密码都是 admin
嗯,环境搭好了,下一步就是写测试用例了。不过那是下一章的事,咱们先把这套环境跑起来再说。