8.2 KiB
8.2 KiB
测试 → 生产 增量部署规范
原则:生产上线永远是增量,没有全量。每次上线 = 一批「增量单元」(代码 + 数据库变更 + 配置), 每个单元必须可幂等重放、可验证、可回退。本规范固化四步:备份 → 操作 → 验证 → 故障回退。
0. 目录约定(在 pipeline-app 仓库)
deploy/
├── METHODOLOGY.md # 本文档
├── dmig.py # 迁移引擎:台账表 + up/down + 幂等
├── ddiff.py # 环境差异比对:代码模块版本 / schema / 参数
├── dbackup.py # 增量表备份(mysqldump)+ 恢复命令生成
└── migrations/
├── m0001_xxx.json # 迁移单元(编号递增,字典序 = 执行序)
└── ...
1. 增量单元(一次上线携带什么)
每次从测试推向生产,变更分成三类,各走各的通道,不允许混:
| 类型 | 载体 | 通道 |
|---|---|---|
| 代码 | 各模块仓库提交 | 生产机 git pull --ff-only + pip install(非 editable 模块)+ 清 __pycache__ + 重启 |
| 数据库变更 | deploy/migrations/mNNNN_*.json |
dmig.py(测试先跑 → 生产复核后执行) |
| 系统配置 | params 表 / 环境变量 |
走 dmig.py 的 params_set/env 类型,或记录在迁移说明里人工核对 |
铁律:
- 测试环境执行过的库变更,必须在提交测试代码的同时登记成迁移文件——没有登记就没有测试通过。
- 生产执行迁移前必须先跑
ddiff.py,拿差异清单与迁移批次对照,清单里没有对应迁移的差异 = 遗漏。 - 破坏性操作(DROP/DELETE/TRUNCATE)永远生成命令由人工在生产执行,自动化只做到「打印 [NEEDS_APPROVAL]」。
2. 迁移单元格式(migrations/mNNNN_名称.json)
{
"id": "m0001",
"title": "说明(一句话:这次改了什么、为什么)",
"backup_tables": ["sd_projects"],
"up": [
{"op": "sql", "sql": "ALTER TABLE ..."},
{"op": "create_table", "name": "xxx", "columns": "...完整 CREATE TABLE 语句..."},
{"op": "params_set", "key": "tender_api_base", "value": "http://..."}
],
"down": [
{"op": "sql", "sql": "DROP TABLE xxx"},
{"op": "params_del", "key": "tender_api_base"}
]
}
规则:
id唯一且递增;执行顺序 = 文件名字典序。up的每一步都必须幂等:CREATE TABLE IF NOT EXISTS、ALTER TABLE ... ADD COLUMN IF NOT EXISTS(MySQL 8 用先查 information_schema 的方式,dmig 会自动做存在性检查)、INSERT ... ON DUPLICATE KEY UPDATE。down是up的精确逆操作;无逆操作的步骤(如纯数据修复)down写空数组并在 title 注明「不可逆」。backup_tables列出本次会改动的表——dmig.py apply前自动备份这些表(生产备份由人工确认后执行)。
3. 四步流程
第 1 步:数据备份(上线前必做,不可跳)
# 生产机执行。dmig 会自动列出本次批次涉及的表;也可手工指定。
cd /d/doit/pipeline-app
./py3/bin/python deploy/dbackup.py backup --batch # 备份本批次所有 backup_tables
./py3/bin/python deploy/dbackup.py backup --tables sd_projects,pipeline_tasks # 手工补充
# 备份文件:/d/doit/pipeline-app/backups/<db>_<table>_<日期时间>.sql,同目录生成 RESTORE 命令文件
检查点:
- 备份文件存在且非空(
ls -lh backups/) - 备份文件头部有
CREATE TABLE且行数与线上一致(dbackup.py verify <file>)
第 2 步:操作(按序执行增量)
顺序铁律:先库后码再重启。新代码可能引用新列/新表,代码先上会在重启后
Unknown column 崩溃(2026-09-01 生产实录)。
# 2.1 差异核对(必做):打印生产与测试的差异清单,与本次批次对照
./py3/bin/python deploy/ddiff.py --checklist
# 2.2 数据库增量先行(迁移引擎)
./py3/bin/python deploy/dmig.py status # 看台账:哪些已执行、哪些待执行
./py3/bin/python deploy/dmig.py plan # 预演:打印本批次将执行的 SQL(不落库)
./py3/bin/python deploy/dmig.py apply # 执行(自动先备份本批次表,逐条落库,逐条记账)
# 2.3 代码增量(库就绪后才上代码)
cd /d/doit/pipeline-app/pkgs/<module> && git pull --ff-only origin main
cd /d/doit/pipeline-app && ./py3/bin/pip install pkgs/<module>
find py3/lib/python3.10/site-packages/<module_pkg> -name __pycache__ -exec rm -rf {} +
# 2.4 最后重启(生产四进程:逐 pidfile kill 再 start,见 pipeline-prod-operations 技能;
# 禁止 pkill -f "pipeline-app" ——会误杀自己的远程 shell)
nohup bash restart-pipeline.sh </dev/null > logs/restart.log 2>&1 &
sleep 15; curl -s -o /dev/null -w "health:%{http_code}\n" http://127.0.0.1:9090/
dmig.py apply 的行为:
- 每条语句执行前查存在性(表/列/索引已有 → 跳过,保证幂等);
- 执行成功才写台账
pipeline_deploy_ledger(批次、步骤、状态、时间); - 任一步失败 → 停止后续步骤,打印已完成步骤清单和对应
down操作(供回退),不自动回滚。
第 3 步:验证(不验证 = 没上线)
./py3/bin/python deploy/ddiff.py --checklist # 差异清零(或只剩本批次明确豁免项)
./py3/bin/python deploy/dmig.py status # 台账:本批次全部 applied
curl -s -o /dev/null -w "health:%{http_code}\n" http://127.0.0.1:9090/ # 服务 200
grep -a "poller started" logs/pipeline.log | tail -5 # 各 poller 正常
# + 功能验收:真实浏览器走一遍本次变更影响的功能(铁律:功能验收必须真浏览器执行)
检查点:
- ddiff 差异清零
- dmig status 全 applied、无 failed
- 服务健康 + poller 正常
- 功能浏览器实测通过(记录证据:页面/请求/数据三选一)
第 4 步:故障回退(任一步失败时)
判断:回退的是「这次增量」,不是整个系统。定位到失败的批次号。
# 4.1 库回退:用迁移自带的 down
./py3/bin/python deploy/dmig.py rollback m0003 # 执行 m0003 的 down + 台账置 rolled_back
# 4.2 数据回退:down 不足以恢复时,用第 1 步的备份
./py3/bin/python deploy/dbackup.py restore --file backups/pipeline_sd_projects_20260901_120000.sql --confirm
# 注意:恢复会先 DROP 再导入该表——执行前人工确认没有比备份点更新的写入(或先导出增量)
# 4.3 代码回退
cd /d/doit/pipeline-app/pkgs/<module> && git log --oneline -5 # 找到上线前的提交
git checkout <上线前提交> && cd ../.. && ./py3/bin/pip install pkgs/<module> && 重启
回退后重新走第 3 步验证,确认系统回到上线前状态。
4. 台账表(自动创建)
CREATE TABLE IF NOT EXISTS pipeline_deploy_ledger (
id VARCHAR(32) NOT NULL,
batch VARCHAR(16) NOT NULL, -- 迁移 id:m0001
step INT NOT NULL, -- up 内步骤序号
status VARCHAR(16) NOT NULL, -- applied / rolled_back / skipped
backup_file VARCHAR(255),
detail VARCHAR(4000),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_ledger_batch_step (batch, step)
);
5. 测试环境的角色
测试环境是迁移的演练场:每个迁移文件必须在测试环境 dmig.py apply 跑通(含幂等重跑验证)
才允许进入生产批次。测试环境的台账独立(同库不同环境实例),生产台账只反映生产执行记录。
6. 常见坑(历史教训)
- 改库没登记 → 生产是老库。8/31-9/1 连续发现:
pipeline_agent_instances缺表、sd_projects.directory_name缺列、pipeline_deliverables.created_by还是 32 宽。 根治就是本规范:改库必须落成迁移文件。 - 只在测试跑 DDL 口头说"生产一样" → 必然漂移。用
ddiff.py强制对账。 - 回退没有 down → 不可逆变更(DROP 列、删数据)必须有备份兜底,备份在操作前。
- 破坏性语句进自动化 → 永远
[NEEDS_APPROVAL],人工执行。