第四章:CI/CD流水线搭建:Jenkins/GitLab CI配置、自动化测试集成、一键部署策略
做市商系统最怕什么?我最怕半夜三点被电话吵醒,说策略没跑起来。嗯,CI/CD流水线就是解决这个问题的。说白了,就是把代码从提交到上线这整条链路自动化,让机器替人做那些重复、容易出错的事。
我个人习惯把CI/CD拆成三段来看:持续集成(CI)负责代码合并和自动化测试,持续交付(CD)负责把验证过的代码部署到测试环境,持续部署(CD)负责一键推到生产。做市商业务对延迟和稳定性要求极高,所以我们的流水线设计必须兼顾速度和可靠性。
4.1 为什么做市商需要CI/CD?
你想想看,做市策略通常涉及多个模块:行情接入、订单管理、风控引擎、数据库同步。任何一个模块出问题,都可能造成亏损。我记得有一次,团队里有人改了风控参数,忘记通知其他人,结果上线后触发大量误报单。从那以后,我强制要求所有变更必须走CI/CD流水线。
做市商CI/CD的核心目标有三个:
- 快速验证:每次提交代码,自动跑单元测试、集成测试、回测验证
- 环境一致性:开发、测试、生产环境配置完全一致,避免「在我机器上能跑」的尴尬
- 一键回滚:出问题时,能在30秒内恢复到上一个稳定版本
核心原则:做市商流水线必须支持「灰度发布」和「蓝绿部署」。我曾经因为全量发布导致整个交易节点断连,损失惨重。现在我的流水线里,生产部署永远先推10%的流量,观察5分钟没问题再全量。
4.2 GitLab CI配置实战
我个人更倾向GitLab CI,因为它和代码仓库深度集成,配置简单。做市商项目通常用Monorepo结构,一个仓库里放策略、风控、数据服务等多个模块。下面是我常用的.gitlab-ci.yml配置模板:
stages:
- lint
- test
- build
- deploy
variables:
DOCKER_IMAGE_TAG: $CI_COMMIT_SHORT_SHA
lint-job:
stage: lint
script:
- flake8 strategies/
- pylint risk_engine/
only:
- develop
- master
test-job:
stage: test
script:
- pytest tests/unit/ --cov=strategies
- pytest tests/integration/ --cov=risk_engine
artifacts:
paths:
- coverage/
expire_in: 1 week
build-job:
stage: build
script:
- docker build -t market-maker:$DOCKER_IMAGE_TAG .
- docker push registry.example.com/market-maker:$DOCKER_IMAGE_TAG
only:
- master
deploy-staging:
stage: deploy
script:
- kubectl set image deployment/market-maker-staging market-maker=registry.example.com/market-maker:$DOCKER_IMAGE_TAG
environment:
name: staging
only:
- master
deploy-production:
stage: deploy
script:
- kubectl set image deployment/market-maker-prod market-maker=registry.example.com/market-maker:$DOCKER_IMAGE_TAG
when: manual
environment:
name: production
only:
- master
这里有个坑要注意:生产部署必须手动触发。我曾经设置过自动部署到生产,结果凌晨三点一个同事提交了未完成的代码,直接推到了线上,导致策略报错。现在我的流水线里,生产部署永远加when: manual,需要两个人确认才能执行。
4.3 Jenkins Pipeline配置
如果团队用Jenkins,我建议用Pipeline as Code的方式。Jenkinsfile比自由风格任务更可控,也更容易版本管理。做市商场景下,我通常会设计一个带「门禁」的流水线:
pipeline {
agent any
stages {
stage('代码检查') {
steps {
sh 'sonar-scanner -Dsonar.projectKey=market-maker'
}
}
stage('单元测试') {
steps {
sh 'pytest tests/unit/ --junitxml=report.xml'
}
post {
always {
junit 'report.xml'
}
}
}
stage('回测验证') {
steps {
sh 'python backtest/run.py --days 30 --symbol BTC-USDT'
}
}
stage('构建镜像') {
steps {
sh 'docker build -t market-maker:${BUILD_NUMBER} .'
}
}
stage('部署到测试环境') {
steps {
sh 'ansible-playbook deploy-staging.yml -e version=${BUILD_NUMBER}'
}
}
stage('冒烟测试') {
steps {
sh 'python smoke_test.py --env staging'
}
}
stage('部署到生产') {
when {
branch 'master'
expression { currentBuild.resultIsBetterOrEqualTo('SUCCESS') }
}
steps {
input message: '确认部署到生产环境?', ok: '确认'
sh 'ansible-playbook deploy-production.yml -e version=${BUILD_NUMBER}'
}
}
}
post {
failure {
emailext(
subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "请检查构建日志: ${env.BUILD_URL}",
to: 'team@example.com'
)
}
}
}
我的经验:Jenkins的input步骤非常有用。做市商部署前,最好让风控负责人和策略负责人同时确认。我见过太多因为一个人手滑导致的事故了。
4.4 自动化测试集成
做市商的测试不能只靠单元测试。我要求流水线里至少包含三类测试:
| 测试类型 | 覆盖内容 | 执行时机 | 失败处理 |
|---|---|---|---|
| 单元测试 | 策略逻辑、风控规则、数据解析 | 每次提交 | 阻断流水线 |
| 集成测试 | 交易所API对接、数据库读写、消息队列 | 每次提交到develop分支 | 阻断流水线 |
| 回测验证 | 历史数据回放、盈亏计算、滑点模拟 | 每次提交到master分支 | 告警但不阻断 |
| 冒烟测试 | 部署后检查服务健康、延迟、订单流 | 每次部署后 | 自动回滚 |
为什么回测验证失败不阻断?因为回测结果受市场环境影响,偶尔会有假阳性。我建议设置一个「回测报告」的Artifact,让策略分析师人工审核。但冒烟测试失败必须自动回滚,这个没得商量。
避坑指南:我曾经在冒烟测试里只检查了HTTP状态码,结果服务返回200但内部逻辑全错了。现在我的冒烟测试会模拟一笔真实订单,检查订单是否被正确路由、风控是否触发、数据库是否更新。这才是真正的「冒烟」。
4.5 一键部署策略
一键部署听起来简单,但做市商场景下有很多细节。我总结了一套「三阶段部署法」:
- 预热阶段:新版本启动后,先订阅行情但不交易,等待缓存预热、连接建立。这个阶段持续30秒到1分钟。
- 观察阶段:开始交易,但只分配10%的仓位。监控延迟、成交率、盈亏。持续5分钟。
- 全量阶段:观察期无异常,自动切到100%仓位。如果观察期有异常,自动切回旧版本。
下面是一个简化版的部署脚本:
#!/bin/bash
# 一键部署脚本
DEPLOY_ID=$(date +%s)
OLD_VERSION=$(kubectl get deployment market-maker -o jsonpath='{.spec.template.spec.containers[0].image}')
NEW_VERSION="market-maker:$1"
echo "开始部署: $NEW_VERSION"
# 1. 启动新版本
kubectl set image deployment/market-maker-canary market-maker=$NEW_VERSION
# 2. 等待就绪
kubectl rollout status deployment/market-maker-canary --timeout=120s
# 3. 预热阶段
echo "预热30秒..."
sleep 30
# 4. 观察阶段
echo "观察5分钟..."
for i in {1..5}; do
sleep 60
HEALTH=$(curl -s http://market-maker-canary:8080/health)
if [[ $HEALTH != *"healthy"* ]]; then
echo "健康检查失败,触发回滚"
kubectl set image deployment/market-maker market-maker=$OLD_VERSION
exit 1
fi
done
# 5. 全量部署
kubectl set image deployment/market-maker market-maker=$NEW_VERSION
kubectl rollout status deployment/market-maker --timeout=120s
echo "部署完成: $DEPLOY_ID"
关键点:一键部署不是「一键就完事」,而是「一键触发,自动完成所有步骤」。我见过很多团队把部署脚本写得特别复杂,结果出问题时没人敢点那个按钮。保持简单,保持可观测。
4.6 流水线监控与告警
流水线本身也需要监控。我要求每个CI/CD任务都必须输出关键指标:
- 构建时间:超过10分钟告警
- 测试覆盖率:低于80%告警
- 部署频率:每天超过3次告警(说明开发流程有问题)
- 回滚次数:每周超过1次告警
这些指标我会推送到Grafana,做成一个「流水线健康看板」。嗯,这里要注意:不要只看成功率,要看趋势。如果构建时间从5分钟慢慢涨到9分钟,说明代码库在膨胀,需要重构了。
最后说一句:CI/CD流水线不是写出来就完事的。我每个月都会和团队一起复盘流水线,看看哪些步骤可以优化,哪些告警是噪音。做市商业务变化快,流水线也要跟着迭代。
我的习惯:每次部署后,我会让流水线自动发一条消息到团队群,包含部署版本、变更日志、健康检查结果。这样所有人都知道当前线上跑的是什么版本,避免「我以为你部署了」的误会。
这张图展示了做市商CI/CD流水线的完整链路。从代码提交开始,经过CI验证、构建打包,再到三阶段部署,最后通过自动化测试确保质量。每个环节都有对应的监控和告警,形成一个闭环。
公众号:蓝海资料掘金营,微信deep3321