Files
junhong_cmp_fiber/docs/deployment/production-runbook.md
break 98c145fe70
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 3m55s
实现支付商户池与微信授权配置
新增收款商户、商户池轮询、微信授权配置独立管理;三类新支付
(C端套餐购买、C端资产钱包充值、代理在线预存款充值)无条件
经商户池选择并冻结路由,无旧综合配置回退。merchant_id 为空
历史支付继续按 payment_config_id 双读。凭证版本化加载与
ID+版本缓存保证轮换一致性。删除商户池新支付创建开关及全部
引用。
2026-09-09 18:13:04 +08:00

12 KiB
Raw Permalink Blame History

生产环境运行说明

元数据

  • 用途:区分生产与测试运行方式,并为人工发布、迁移和回滚提供事实入口。
  • 适用范围:xm-iot.cn 的生产 API 与 Worker不适用于本地、测试环境或 Docker Compose。
  • 事实源:生产维护者提供的主机与目录信息;实际 systemd Unit、线上 .env.prodmigrate version 输出优先于本文。
  • Owner生产维护者。
  • 最后核验2026-08-13基于维护者提供的信息未连接生产环境
  • 更新触发Unit、目录、进程数、Worker 角色、发布顺序、迁移方式或回滚方式变化。
  • 验证Agent 可通过 dbhub 对生产数据库执行只读诊断查询服务器上的只读核对、生产发布、迁移、回滚及其他写操作由维护者执行并回填结果Agent 不连接生产主机。

与测试环境的边界

生产环境不是仓库中的 Docker Compose 测试部署:

项目 生产环境 仓库 Compose/工作流
平台 Ubuntu 24.04 x86_64 测试环境自动部署
发布 本地交叉编译后手工上传二进制 Gitea 构建、镜像与 Docker Compose
进程管理 systemd Docker Compose
运行单元 1 个 API、1 个调度 Worker、3 个执行 Worker 不作为生产拓扑依据
配置 每个运行目录的 .env.prod Compose environment
回滚 覆盖为部署前备份的二进制,必要时恢复数据库备份 不适用

不得从 .gitea/workflows/deploy.yamldocker-compose.prod.yml 或其中的测试配置推断生产地址、凭据、发布步骤或运行拓扑。

已确认运行布局

  • 主机Ubuntu 24.04.4 LTSx86_64
  • API 服务 junhong-cmp-api.service:工作目录与程序分别为 /opt/junhong_cmp/api/opt/junhong_cmp/api/api,通过同目录 .env.prod 加载环境变量;旧二进制备份在 releases/
  • 调度 Worker 服务 junhong-cmp-worker.service:工作目录与程序分别为 /opt/junhong_cmp/worker/opt/junhong_cmp/worker/worker,通过根目录 .env.prod 加载环境变量。
  • 三个执行 Worker 服务 junhong-cmp-worker-1.servicejunhong-cmp-worker-2.servicejunhong-cmp-worker-3.service:共享 /opt/junhong_cmp/worker/worker 这一份二进制;工作目录和 EnvironmentFile 分别是 worker-1/worker-2/worker-3/ 下的 .env.prod。因此发布只替换根目录的 worker,不复制三份二进制。
  • 五个 Unit 都以 rootRestart=alwaysRestartSec=3 运行,标准输出和错误写入 journald。
  • Worker 拓扑:调度实例已确认 JUNHONG_WORKER_ROLE=leaderJUNHONG_WORKER_INSTANCE_NAME=leader;三个执行实例均为 JUNHONG_WORKER_ROLE=consumer。当前三个执行实例的 JUNHONG_WORKER_INSTANCE_NAME 都是 worker-1,会混淆日志与 Outbox Relay 实例标识;七月发布前须分别修正为 worker-1worker-2worker-3
  • 域名:xm-iot.cnAPI 域名:cmp-api.xm-iot.cn
  • 企微回调由自建企微中台 wecom.xm-iot.cn 配置;支付和运营商回调由各官方平台配置。

构建与上传

维护者当前在本地构建:

GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o ./build/api ./cmd/api
GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o ./build/worker ./cmd/worker

本次迭代更新上传 API 与 Worker 二进制;配置变化由维护者直接修改各运行目录的 .env.prod。生产迁移文件必须随本次发布上传到 API 目录下的 migrations/,并同步上传当前分支的 scripts/migrate.sh,供 migrate 记录和执行。生产服务器当前尚未安装 migrate;安装路径和版本待首次迁移前确认。

配置规则

  • 二进制只从嵌入默认配置和 JUNHONG_ 环境变量读取;.env.prod 必须由 systemd Unit 显式加载,或由 Unit 启动脚本 source 后启动。需要以 Unit 内容核实实际方式。
  • API 与 Worker 共享数据库、Redis、日志、JWT、对象存储、Gateway、短信、支付等基础配置只记录键名不将实际凭据写入仓库文档。
  • 七月新增的 API/Worker 共用配置:JUNHONG_APPROVAL_LEGACY_REFUND_MANUAL_ENABLEDJUNHONG_APPROVAL_LEGACY_OFFLINE_RECHARGE_PAY_ENABLEDJUNHONG_WECOM_BASE_URLJUNHONG_WECOM_TIMEOUT
  • 本次新增的 Worker 配置:JUNHONG_WORKER_ROLEJUNHONG_WORKER_INSTANCE_NAMEJUNHONG_WORKER_POLLING_TOTAL_MAX_CONCURRENCYJUNHONG_WORKER_AUDIT_RETENTION_CLEANUP_ENABLEDJUNHONG_WORKER_AUDIT_ARCHIVE_TASKS_ENABLED。轮询总并发必须保持在 1-1000初始生产值建议 100归档/留存总开关默认关闭。
  • 首发要求:退款人工入口、线下充值人工确认、企微审批、运营商实名回调、微信/支付宝在线充值均按维护者决定启用;审计物理清理保持关闭。
  • 企微应用凭据由后台配置写入数据库明文字段;这不是启动环境变量。本文不记录其值。

人工发布与迁移

已确认顺序:备份二进制和数据库 → 上传二进制及迁移文件 → 停止 systemd 服务 → 执行迁移 → 启动服务。

生产当前迁移版本已于 2026-08-13 由维护者在 API 目录验证为 140(非 dirty。历史迁移已归档生产从 140 向后执行根目录 migrations/141+ 迁移;七月分支最终目标为 209。根目录不存在 173 号迁移,这是正常编号空档。执行前先以显式 DB_* 参数运行:

DB_HOST=<生产主机> DB_PORT=<端口> DB_USER=<用户> \
DB_PASSWORD='<密码>' DB_NAME=<库名> DB_SSLMODE=<模式> \
./scripts/migrate.sh version

DB_HOST=<生产主机> DB_PORT=<端口> DB_USER=<用户> \
DB_PASSWORD='<密码>' DB_NAME=<库名> DB_SSLMODE=<模式> \
./scripts/migrate.sh up

迁移失败时不启动新二进制;按失败迁移的事务状态决定处理,必要时恢复已确认可用的数据库备份。启动失败时覆盖回部署前备份的二进制,再恢复数据库备份(如迁移已改变数据库)。

商户池支付路由发布与回滚

本节是维护者操作清单不是已执行证据。迁移、生产发布、Redis 操作和富友真实渠道核验均由维护者执行;本轮未执行,不能以本地构建替代。

前置条件

  1. 留存维护者指定测试环境或本地验证证据,且不得记录密钥或完整报文中的敏感凭证。富友仅沿用现有实现;未进行外部渠道实测不构成开发、测试部署、任务完成、归档或发布前置。
  2. 确认本次商户池 Schema 迁移已完成可恢复备份及校验;停止服务后确认迁移锁影响、无长事务和可接受维护窗口。
  3. 上传支持 merchant_id/payment_config_id 双读的 API 与 Worker 二进制。商户池新支付没有运行时开关。

发布后检查

  1. 由维护者执行迁移并部署双读二进制。C 端套餐购买、C 端资产钱包充值、代理在线预存款充值的后续新支付立即经启用商户池创建并冻结 merchant_id、商户池与 routing_epoch
  2. 无可用商户池、成员缺失或池停用必须稳定失败,不得回退旧综合支付配置或自动换商户;后台线下订单、后台钱包余额支付和员工线下代充值不经过商户池。
  3. 检查首次成功唯一累计,以及回调/查单/退款 A 对 merchant_id 新单和 payment_config_id 历史单的双读分流应用、审计和集成日志不得包含凭证、私钥、Token、证书或完整敏感配置。

回滚与记录

  1. 不存在关闭商户池新支付创建的运行时开关。故障只能在仍支持双读的二进制上前向修复,不得恢复旧综合支付配置创建。
  2. 只要存在 merchant_id 非空支付、成功累计事实或新商户池配置,禁止部署不识别新路由的旧二进制,也禁止执行破坏这些事实的 down 迁移。
  3. 维护者记录二进制版本、时间、目标 PostgreSQL/Redis 的脱敏标识、备份校验、验证结果与全部阻塞原因。

零金额退款发布后核验

发布本次退款审批变更后,维护者应先等待既有重试处理稳定事件 approval:26:approved;若重试已耗尽,按受控运维流程重放同一事件,不得直接修改退款、订单或钱包数据。随后核验:

  1. 退款单 RF20260820170954264700 已通过,关联订单支付状态为已退款。
  2. 该退款没有代理主钱包或资产钱包的零金额回款流水。

锁的含义与发布影响

000171 会对 tb_agent_wallet000178 会对 tb_iot_card 使用 PostgreSQL ACCESS EXCLUSIVE 锁。该锁执行期间会阻塞该表的读写及其他 DDL直到迁移事务提交或回滚若有未结束业务查询/事务,它也会等待。因此必须在 API 和全部 Worker 停止后执行,并在迁移前检查没有长事务。锁持续时间取决于表数据量、索引创建和等待中的旧事务;不能从仓库估算具体秒数。

待维护者确认的最小信息

  1. migrate 的安装路径、版本和迁移文件上传命令;确认 140 到首个根目录迁移版本之间是否存在待补的迁移文件。
  2. 数据库恢复的准确命令、恢复前提,以及发布前新建备份的执行人。
  3. 已停止服务后的锁前检查命令/结果(至少确认无长事务),以及可接受维护窗口。
  4. 企微中台转发到 API 的最终回调路径;支付与运营商平台配置的回调 URL 清单(可脱敏域名外路径)。

已确认数据库备份

每日凌晨 02:00 自动备份 junhong_cmp_prod:数据库运行在 Docker 容器 postgres 中,备份脚本执行 pg_dump -Fc -Z 6,写入 /data/backups/postgresql/<库名>_<时间>.dump,同时生成 MD5 文件并以 pg_restore --list 校验结构;保留 30 天。发布前仍须人工新建一次备份并确认校验通过,不能只依赖凌晨的最近备份。恢复命令待维护者实际演练或确认后补充。

日审计日志留存启用与恢复

归档与日留存由调度 Worker 受控处理 Asia/Shanghai 的已结束自然日;JUNHONG_WORKER_AUDIT_ARCHIVE_TASKS_ENABLED=false 时不注册调度,已入队任务也安全跳过、不扫描在线日志表。开启后每次最多推进一个日期。JUNHONG_WORKER_AUDIT_RETENTION_CLEANUP_ENABLED=false 时仅验证归档、对象、manifest、数据库数量和日期连续性不写清理断点、不删除在线数据。Integration 的 pending 记录保留在线但不会阻断同日终态日志、Audit 数据或后续日期的清理;若 pending 后续终结Worker 会重建该日 revision 后续跑删除。

  1. 发布新 Worker 后保持 JUNHONG_WORKER_AUDIT_ARCHIVE_TASKS_ENABLED=false 和物理清理开关关闭;轮询总并发先设为 100。维护者先核对 tb_log_archive_run 中 Audit 与 Integration 两个来源从历史最早在线日期起没有缺失账本;缺失日期必须先受控补归档,不能跳过失败日。
  2. 以关闭开关的 Worker 完成只读演练,观察 /opt/junhong_cmp/worker/logs/audit-retention.log:每个日期应有来源计数和耗时;若出现日期、来源、失败分类或安全错误摘要,先修复该来源的归档或校验问题后再演练。仅有 pending 不需要人工终结才可继续。
  3. 低峰期先将调度 Worker 的 JUNHONG_WORKER_AUDIT_ARCHIVE_TASKS_ENABLED=true,重启该 Worker确认单日归档稳定后再将 JUNHONG_WORKER_AUDIT_RETENTION_CLEANUP_ENABLED=true,并持续观察上述独立日志、tb_log_archive_run.cleanup_started_at / cleaned_at 断点及表大小。DELETE 释放的 PostgreSQL 页面会供后续写入复用,表文件不会立即缩小;维护者按既有运维窗口评估 VACUUMWorker 不执行 VACUUM 或表重写。
  4. 异常时立即将开关改回 false 并重启调度 Worker。已清理日期按已验证的对象和 manifest 执行归档恢复;尚未清理或阻断的日期仍保留在线,无需数据库恢复。恢复后先重新执行只读演练,再决定是否重新开启清理。