Files
lislgosms/docs/production-deployment.md
T

267 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CMPP 平台部署手册(当前实例为预发布环境)
## 当前预发布环境端口与域名
- 运营端、客户端页面:`https://sms.lisglo.com`Cloudflare 橙云代理,源站使用 Cloudflare Origin CA;旧 `12026` 仅在切换期临时保留。
- 客户 HTTP API`https://api.lisglo.com/api/openapi/v1`Cloudflare 灰云直连,源站必须使用公网信任的 Let’s Encrypt 证书;该虚拟主机不得暴露 admin/client 管理接口或前端页面。
- API:仅本机 `127.0.0.1:3000`,由 Nginx `/api/` 反向代理。
- Redis:仅本机 `127.0.0.1:6379`
- PostgreSQL:仅本机 `127.0.0.1:5432`
- MinIO:仅本机 `127.0.0.1:9000/9001`,页面上传下载通过 API 转发。
- Gateway 控制服务:仅本机 `127.0.0.1:8090`
- CMPP 入站端口:`17890`,由 Go Gateway 启动真实 CMPP 3.0 Server,接收企业应用下游 connect/login 和 submit。
## 预生产磁盘与目录规划
本节是预生产 `8.160.169.106` 的存储规范入口;实施证据见 [第一阶段记录](preprod-data-disk-stage1-20260831.md)和[第二阶段记录](preprod-data-disk-stage2-20260831.md),阶段完成状态见 [测试与运维进度](testing-progress.md)。旧版部署示例与本节冲突时,以本节为准。
### 当前状态与实施边界
- 以下已实施状态来自2026-08-31晚间第二阶段验证,不代表每次阅读时已重新核验服务器。
- 60GiB系统盘承载操作系统、运行程序、应用日志和监控历史数据;100GiB数据盘挂载 `/data`,已承接PostgreSQL、Redis、MinIO、版本恢复备份及原备份目录内的操作记录。
- 用户明确授权后已完成第二阶段三项存储迁移;应用日志、监控历史数据及其他表内待实施项不包含在本次迁移中,不得把规划当成实施事实。
- 本次迁移的系统盘原数据副本及一致性冷备暂作恢复资产保留,不属于新增旧代码版本。后续清理或进一步迁移需按明确指令执行,不能直接切回已落后于新写入的旧数据。
### 目录分工
| 内容 | 当前路径或位置 | 规划位置 | 实施状态与要求 |
|---|---|---|---|
| 操作系统、软件包、服务配置及密钥 | 系统盘;配置主要在 `/etc` | 继续留在系统盘 | 配置按最小权限保存,不因数据迁移改为公开可读 |
| 当前运行程序与依赖 | `/opt/cmpp-platform` | 继续留在系统盘 | 发布切换只处理程序,不携带持久化数据 |
| 上一个版本运行目录 | `/opt/cmpp-platform.previous-*` | 继续留在系统盘 | 稳态仅保留上一个有效版本;不得积累失败、incoming及更早版本目录 |
| 数据库、Redis、源码与配置的成套恢复备份 | `/opt/cmpp-platform-backups` | `/data/cmpp-platform-backups` | **已实施**;原入口是指向数据盘的符号链接,发布脚本不得替换成普通目录 |
| 备份目录内业务操作留痕 | `/opt/cmpp-platform-backups/operations` | `/data/cmpp-platform-backups/operations` | **已实施**;不纳入旧版本自动清理范围 |
| PostgreSQL业务数据及WAL | `/var/lib/pgsql/data` | `/data/postgresql/data` | **第二阶段已实施**`/data/postgresql`绑定挂载到`/var/lib/pgsql`,无外置表空间或WAL链接,完整冷复制及110张表行数核对通过 |
| Redis持久化文件 | `/var/lib/redis` | `/data/redis` | **第二阶段已实施**;绑定挂载保留旧路径,保持RDB开启、AOF关闭,停写SAVE后冷复制,三条Stream状态一致 |
| MinIO对象及存储元数据 | `/var/lib/minio` | `/data/minio` | **第二阶段已实施**;绑定挂载保留旧路径,完整目录及62个业务对象清单核对通过,独立探针读写删除通过 |
| 本地对象存储备用驱动的上传文件 | 配置项 `OBJECT_STORAGE_LOCAL_ROOT` | 如启用则规划为 `/data/cmpp-platform/object-storage` | 条件规划;第一阶段未启用或切换存储驱动,禁止擅自改用local替代MinIO |
| API、各Worker、Gateway应用日志 | `/opt/cmpp-platform/logs/<service>` | `/data/log/cmpp-platform/<service>` | **待单独实施**;需同步日志输出路径、权限和轮转,不能因迁盘取消保留上限 |
| Prometheus历史指标 | 仓库安装脚本使用 `/var/lib/prometheus/metrics2` | `/data/prometheus/metrics2` | **待核验并单独实施**;当前值以实际服务参数为准,保留时间及容量双重限制 |
| 系统journal、系统及安全日志 | `/var/log` 等系统路径 | 继续留在系统盘 | 设置轮转及容量上限;不能将安全审计日志当旧发布文件删除 |
待实施目标子目录应在相应迁移时创建,不提前启动服务写入。目前备份入口使用符号链接,三项存储使用绑定挂载;其他规划路径不假设已有链接、挂载或服务配置支持。
### 挂载、权限与缺盘保护
- 使用磁盘UUID持久化挂载,不把 `/dev/nvme1n1` 的枚举顺序作为永久身份。当前预生产UUID与fstab记录见第一阶段文档;其他主机不得照抄该UUID或格式化命令。
- 第一阶段 `/data` 采用 `nofail`,仅保证缺盘时系统仍可启动;这不意味着未来依赖数据盘的数据库可以在缺盘时启动。
- 现有备份保护由 `/usr/local/sbin/cmpp-backup-storage-check` 和未挂载时底层空 `/data` 的immutable属性共同提供。不得移除保护、重建系统盘备份目录或在检查失败后改用其他路径继续备份。
- 第二阶段已为`postgresql``redis``cmpp-minio`配置`50-cmpp-data-disk.conf`,通过`RequiresMountsFor``After``BindsTo`关联`data.mount`及各自绑定挂载;`ExecStartPre`调用`/usr/local/sbin/cmpp-data-storage-check`核验UUID、挂载源目录、读写属性和原有存储标记。后续服务迁入也必须在启动前建立同等保护。
- 挂载失败、UUID不匹配或数据盘只读时,备份及相应迁入服务应明确失败并告警,禁止初始化新数据库、空Redis或空MinIO来掩盖故障。
- 各数据目录归对应服务的实际运行用户,保留原权限、ACL、扩展属性与适用的SELinux上下文;核验systemd沙箱的 `ReadWritePaths` 等限制。禁止对整个 `/data` 执行统一业务用户授权或 `chmod 777`
- 三项原路径通过fstab中的绑定挂载兼容,底层空挂载目录保持immutable,缺盘时不能写回系统盘。部署与安装脚本检测`/etc/cmpp-platform/data-disk.uuid`或PostgreSQL存储drop-in后,先执行存储检查;不得删除标记或drop-in绕过检查。尚未以整机重启验证开机过程。
### 备份、保留与容量规则
- 所有后续发布备份统一使用固定入口 `/opt/cmpp-platform-backups`,先执行存储检查,失败即中止;禁止使用随发布切换的 `/opt/cmpp-platform/backups`
- 发布前生成数据库、运行代码和配置的成套恢复点;涉及Redis队列或对象存储的变更,还须包含相应一致性恢复资产。SHA-256、数据库备份目录和压缩包可读性检查通过不等于已完成实际恢复演练。
- 版本保留目标是“当前运行版本 + 上一个有效运行版本 + 上一个版本的完整恢复备份”。发布期间允许暂时保留原有效恢复点与新候选恢复点;新恢复点校验、发布验收和版本对应关系确认前,不能先删唯一有效备份。
- 失败发布、临时传输包、incoming及更早恢复点不长期保留;先确认不被进程引用、无待诊断事故或未完成回退,并按当次明确授权清理。文档中的保留目标不等于已安装自动清理任务,也不授权定时删除业务操作记录。
- 日志和监控数据需要轮转、压缩及容量上限。应用日志保留期不得替代数据库中操作日志的业务保留规则,短信、账务和审计数据不得因磁盘告警直接删除。
- 系统盘与数据盘分别监控使用率、可用字节、inode、读写异常和增长速度。规划告警分级为70%预警、80%严重、90%紧急;属于**待配置并验证的运维目标**,本次文档更新不表示告警已上线。
- 发布或迁移前按实际数据量核算源/目标临时副本、恢复备份、数据库WAL与Redis持久化重写所需空间;余量不足则停止该次操作,不以删除唯一恢复点腾空间。
- 数据和备份同在这块100GiB盘后会共用容量及故障风险。同机备份不等于异机灾备,异机或对象存储副本须另行实施并验证恢复能力;当前不声称已经配置。
### 第二阶段与后续发布验收
1. 收到明确指令后,重新核对实际磁盘、服务配置、运行用户、数据量及业务流量;为每个服务记录源路径、目标路径、复制方式、切换条件和回退点。
2. 先建立可验证的一致性恢复资产。不得把普通在线目录复制视为PostgreSQL或Redis的一致备份;停写窗口或复制同步方式另定。
3. 短信切换前确认入口接收策略、在途提交、供应商回执、持久化Inbox/Outbox和Redis Streams;不得清队列、手工ACK/XDEL或重投真实短信来缩短迁移窗口。
4. 切换后验证实际文件落盘位置、挂载依赖、用户权限、数据库和对象读写、队列积压、供应商连接及API健康;真实短信测试需单独授权。所有检查通过前保留源数据副本,不能只凭HTTP 200删除它。
5. 若新位置已接受业务写入,回退必须包含新增数据的一致性处理,不能直接切回已经落后的旧目录。
6. 同步部署脚本、systemd配置及安装器,防止下次发布把数据目录、日志路径或挂载依赖恢复成旧默认值。当前仓库部署脚本仍向 `$APP_DIR/logs` 输出日志,不能把本文目标路径当作已经实现的脚本行为。
7. 完成状态、配置证据和未验证项写入阶段记录及 `testing-progress.md`;本节维护长期规范,不将一次实施记录当成永久实时状态。
## 首次部署
1. 本机提交并推送 `main`
2. 使用 root 登录服务器,或先放置免密 SSH key。
3. 在服务器执行:
```bash
curl -fsSL http://175.27.255.91:3000/hectorzhao/lislgosms/raw/branch/main/tools/deploy/production-bootstrap.sh -o /root/production-bootstrap.sh
bash /root/production-bootstrap.sh
```
可覆盖的环境变量:
```bash
APP_DIR=/opt/cmpp-platform
REPO_URL=http://175.27.255.91:3000/hectorzhao/lislgosms.git
BRANCH=main
PUBLIC_HTTP_PORT=12026
API_PORT=3000
API_HOST=127.0.0.1
API_METRICS_HOST=127.0.0.1
API_METRICS_PORT=9464
API_DB_POOL_MAX=32
API_WORKER_DB_POOL_MAX=8
HTTP_API_MASTER_KEY=<至少32位随机值,用于AES-256-GCM加密HTTP访问凭据和Webhook密钥>
HTTP_API_PUBLIC_ORIGIN=https://api.lisglo.com
API_ENABLE_SEND_WORKER=true
API_SEND_WORKER_CONCURRENCY=50
ADMIN_SESSION_IDLE_TIMEOUT_MS=3600000
CLIENT_SESSION_IDLE_TIMEOUT_MS=7200000
SESSION_LOCK_RECOVERY_MS=14400000
SESSION_ABSOLUTE_TIMEOUT_MS=43200000
SESSION_RECENT_AUTH_MS=1800000
SESSION_COOKIE_SECURE=true
OPERATION_LOG_ARCHIVE_ENABLED=true
OPERATION_LOG_RETENTION_DAYS=180
OPERATION_LOG_ARCHIVE_BATCH_SIZE=1000
OPERATION_LOG_ARCHIVE_MAX_BATCHES=20
OPERATION_LOG_ARCHIVE_INTERVAL_MS=86400000
SMS_RECEIPT_TIMEOUT_SCAN_ENABLED=true
SMS_RECEIPT_TIMEOUT_HOURS=72
SMS_RECEIPT_TIMEOUT_SCAN_INTERVAL_MS=300000
PROMETHEUS_URL=http://127.0.0.1:9090
PROMETHEUS_QUERY_TIMEOUT_MS=5000
REPORT_DAILY_REFRESH_ENABLED=true
REPORT_REFRESH_INTERVAL_MS=3600000
CMPP_DOWNSTREAM_ACK_TIMEOUT_SECONDS=30
GATEWAY_CMPP_ADDR=0.0.0.0:17890
CMPP_PUBLIC_HOST=8.160.169.106
CMPP_PUBLIC_PORT=17890
GATEWAY_STARTUP_RECONNECT_DELAY_MS=1000
GATEWAY_SUBMIT_WORKER_CONCURRENCY=64
GATEWAY_SUBMIT_RESULT_STREAM=gateway.submit.results
GATEWAY_SUBMIT_RESULT_GROUP=cmpp-api-callback
GATEWAY_SUBMIT_RESULT_CONSUMER=gateway-1
GATEWAY_SUBMIT_RESULT_WORKER_CONCURRENCY=8
GATEWAY_CMPP_INBOUND_MAX_CONCURRENCY=64
OBJECT_STORAGE_DRIVER=minio
OBJECT_STORAGE_LOCAL_ROOT=/var/lib/cmpp-platform/object-storage
PROD_ADMIN_EMAIL=admin@example.com
PROD_ADMIN_USERNAME=prod_admin
PROD_ADMIN_PASSWORD='change-me'
```
系统监控需要在发布前单独安装,配置和规则位于`tools/monitoring/`。在 Debian/Ubuntuapt)或 Alibaba Cloud Linux 4 ARM64 服务器依次执行`bash tools/monitoring/install-prometheus-monitoring.sh``bash tools/monitoring/install-service-exporters.sh`apt 环境使用系统包,Alibaba Cloud Linux 4 ARM64 使用脚本内锁定版本与 SHA-256 的官方 release 二进制。前者安装 Prometheus/Node Exporter,后者安装 PostgreSQL/Redis/Nginx Exporter 并开启 MinIO 回环原生指标。脚本必须先备份已有配置并运行`promtool`校验;9090、9100、9187、9121、9113、9464和API/Gateway控制端口必须只监听`127.0.0.1`,不得加入Nginx公网反向代理或安全组放行。安装完成且确认全部 target 为 up 后,才可完成发布;详细口径见`docs/prometheus-system-monitoring-design-20260814.md`
安全会话使用 HttpOnly Cookie,正式生产必须先为页面和管理 API 配置 HTTPS,并保持 `SESSION_COOKIE_SECURE=true`;纯 HTTP 的 `IP:12026` 不作为受支持的登录入口,即使切换期仍保留其监听,也只允许用于非登录的兼容检查并应尽快下线。`sms.lisglo.com` 只允许 Cloudflare 回源,`api.lisglo.com` 通过独立 Nginx SNI 虚拟主机只开放客户接口、客户 Swagger 和健康检查;Lets Encrypt 使用 DNS-01 自动续期,不依赖开放 80 端口。
系统操作日志默认在线保留 180 天。API 每日以最多 20 个、每批 1000 条的小事务将过期记录搬入 `OperationLogArchive`,并用 `archiveMonth=YYYY-MM` 标记归档月份;归档记录不会自动删除。调整保留期或批量参数前,应先评估数据库、备份窗口和审计要求。归档表达到千万级或清理窗口不能满足要求时,再实施按 `createdAt` 的月度 PostgreSQL 分区,不在当前数据规模下提前改造主表分区。
脚本会安装 Node.js、Go、PostgreSQL、Redis、MinIO、Nginx,创建 systemd 服务,执行 Prisma migrate,构建前端/API/Gateway,并创建平台管理员。Node.js、Go 和 MinIO 下载会按服务器架构自动选择 x64/amd64 或 arm64。
`API_ENABLE_SEND_WORKER=true` 是生产发送链路必填项。后续发布脚本会在构建和迁移前校验该开关以及正整数 `API_SEND_WORKER_CONCURRENCY`;缺失时直接终止发布,防止 API/Gateway 健康但 BullMQ 短信队列无人消费。
HTTP容量边界固定为:NestJS普通JSON/URL-encoded请求体`2 MiB`,仅`/api/client/send/imports/*`使用`25 MiB` JSON解析上限,原始CSV/TSV正文继续由业务层限制为`20 MiB`Gateway读取NestJS API响应最多`4 MiB`且超限必须明确报错。客户文件导入和运营端报备资料导入走`sms.lisglo.com`私有API;报备XLSX允许最大100MiB并另设500MiB解压后总量限制,因此该虚拟主机的`client_max_body_size`必须不低于`110m`(包含multipart开销),标准bootstrap配置为`110m``api.lisglo.com`只承载单条公网HTTP API、Swagger和健康检查,不承载文件导入;不要为导入需求开放私有路由或把NestJS所有JSON接口统一放宽。发布前使用`nginx -T`确认最终生效值,不能只检查仓库模板。
Gateway 的最终 TPS 防线依赖与 API 相同的 Redis。通道连接时会写入 `rate:gateway:channel:config:<channelId>` 权威上限,实际预约使用 `rate:gateway:channel:<channelId>`;这些 key 不应在正常发布时清理。多 Gateway 实例必须指向同一 Redis,才能共享单通道额度。超速的 `gateway.submit.commands` 消息会保持在 consumer group pending 中等待,不应通过手工 `XACK` 或删除 Stream 处理积压;先检查通道配置、Redis key、consumer group 和 Gateway 日志。V2起Submit Worker使用持续补位有界池并逐条ACK,`GATEWAY_SUBMIT_WORKER_CONCURRENCY`缺省64、最大1024;调整前必须同时核对供应商连接数、窗口、TPS限制、Gateway RSS和`cmpp_gateway_submit_worker_slots`,不能用放大并发绕过通道限速。
V3起客户CMPP入站Submit按应用`cmppWindowSize`在单连接内受限并发,全局上限`GATEWAY_CMPP_INBOUND_MAX_CONCURRENCY`缺省64、最大1024。发布后必须先以已认证测试连接确认`cmpp_gateway_inbound_submit_slots{state="configured"}`等于应用有效窗口,再执行阶梯压测;不得通过调高全局值绕过应用窗口,也不得在未验证Sequence_Id关联、心跳/ACK活性和断线清理时直接提高生产并发。
V4起供应商分片和聚合结果写入`gateway.submit.results`幂等Outbox,由默认8槽、最大1024的独立回调Worker上报API。聚合结果XADD与原命令XACK由Redis Lua原子执行;回调成功后结果事件XACK+XDEL,失败事件留在PEL。发布时必须保留同一Redis数据和AOF,不得清理`gateway.submit.results`、其consumer group或`gateway.submit.results:dedupe:*`;调整`GATEWAY_SUBMIT_RESULT_WORKER_CONCURRENCY`前须核对API/PostgreSQL承载能力。第91条向前兼容migration增加`SmsSubmitRecord.resultEventId/resultProcessedAt`,用于回调跨重启幂等,不删除历史字段或数据。
服务重启顺序必须是 Gateway 在前、API 在后。API 启动后等待 `GATEWAY_STARTUP_RECONNECT_DELAY_MS`(默认 1 秒),从 PostgreSQL 读取全部 active 通道并重新下发真实连接命令,同时恢复 Gateway 内存连接池和 Redis 权威 TPS key;禁止沿用数据库中重启前的 connected 状态冒充当前连接。
日报任务默认启用,并由 `REPORT_REFRESH_INTERVAL_MS` 每小时检查一次北京时间业务日是否变化;每个业务日只执行一次 T-4 至 T-1 重算。服务重启后也会自动补跑最近四个完整自然日,确保 72 小时回执更新反映到对账和利润报表。
如预发布服务器临时无法稳定下载 MinIO,可显式传入 `OBJECT_STORAGE_DRIVER=local`,文件会通过真实 API 保存到服务器本地目录 `OBJECT_STORAGE_LOCAL_ROOT``cmpp-minio` 服务会跳过安装和启动。该模式只建议用于验证环境;正式生产建议恢复 `OBJECT_STORAGE_DRIVER=minio`
## 后续发布
```bash
cd /opt/cmpp-platform
git fetch origin main
git reset --hard origin/main
bash tools/deploy/production-deploy.sh
```
### Fail2ban 安全检测发布前置条件
本功能包含新增 PostgreSQL migration、非 root API 身份、安全代理、Fail2ban、Nginx include 和 nftables 表,不能按普通前端热发布处理。发布前除平台标准 PostgreSQL、运行源码和环境文件恢复资产外,必须额外备份 `/etc/systemd/system/cmpp-api.service*``/etc/systemd/system/cmpp-security-agent.service``/etc/fail2ban``/etc/nginx``/etc/nftables.conf``/etc/nftables.d``/var/lib/cmpp-security-agent`,并逐项生成、复核 SHA-256。
发布脚本会构建 `cmpp-security-agent` 并运行 `tools/security/install-security-agent.sh`。安装器创建专用 `cmpp-api` 用户和 `cmpp-security` 组、写入 systemd 加固 drop-in、安装固定 Fail2ban filter/action、校验 Nginx/Fail2ban/nftables,但不会执行任何人工封禁。环境文件至少明确:
```bash
SECURITY_AGENT_SOCKET=/run/cmpp-security-agent/agent.sock
SECURITY_AGENT_TIMEOUT_MS=3000
SECURITY_EVENT_TOKEN=<至少32字节随机值,仅供Gateway和安全代理上报固定事件>
TRUSTED_PROXY_IPS=127.0.0.1,::1
SECURITY_BUILTIN_PROTECTED_NETWORKS=<运维出口CIDR,健康检查CIDR,源站公网IP>
```
正式 Nginx 的 `sms.lisglo.com` 运营端/客户端 server 块必须 `include /etc/nginx/snippets/cmpp-security-deny.conf;`;API 专用域名继续只开放客户接口。Cloudflare `real_ip_header` 及可信网段必须按官方来源单独维护和验证,禁止信任任意客户端 `CF-Connecting-IP``X-Forwarded-For`。发布后需证明 `cmpp-api` 进程用户不是 root、无 sudo 权限且无法写 `/etc/fail2ban`,安全代理 Socket 不监听 TCP,九类规则版本一致,Fail2ban 为 report-onlynftables/Nginx 回读与数据库状态一致。任何一项失败均不得开放人工封禁按钮。
部署脚本重启 Gateway、API 和 Nginx 后,会分别对 Gateway、API 健康接口执行最多 60 秒的逐秒就绪检查。Nest 初始化、活动通道恢复或生产数据量增加可能使 API 启动超过固定数秒;发布流程不得用单次固定延时把正常慢启动误判为失败。超过 60 秒仍不健康时才终止发布,并结合 systemd journal 和发布前数据库、源码、环境备份判断回滚方式。
标准发布会先清空自身管理的`/etc/nginx/conf.d/cmpp-compression.conf`,再排除该文件检查Nginx现有配置。发行版或既有虚拟主机已经启用`gzip on`时直接复用;完全未启用时才写入平台级压缩配置。不得无条件叠加第二个HTTP级`gzip on`,每次重启前必须以`nginx -t`为准。
## 账号和密钥
- 生产管理员账号写入 `/root/cmpp-platform-admin.txt`
- 首次部署汇总凭据写入 `/root/cmpp-platform-credentials.txt`
- 这两个文件权限为 `600`,不要提交到 Git。
- root 密码后续可修改;SSH 私钥放入 `/root/.ssh/authorized_keys` 后即可免密登录。
## 日志
```bash
journalctl -u cmpp-api -f
journalctl -u cmpp-gateway -f
journalctl -u cmpp-minio -f
tail -f /opt/cmpp-platform/logs/api/stderr.log
tail -f /opt/cmpp-platform/logs/gateway/stderr.log
```
## 健康检查
```bash
curl http://127.0.0.1:3000/api/health
curl http://127.0.0.1:8090/health
curl http://127.0.0.1:12026/
redis-cli -h 127.0.0.1 -p 6379 ping
pg_isready -d "$(grep '^DATABASE_URL=' /etc/cmpp-platform/cmpp-platform.env | cut -d= -f2-)"
grep -E '^(API_ENABLE_SEND_WORKER|API_SEND_WORKER_CONCURRENCY|GATEWAY_SUBMIT_WORKER_CONCURRENCY|GATEWAY_SUBMIT_RESULT_WORKER_CONCURRENCY|GATEWAY_CMPP_INBOUND_MAX_CONCURRENCY)=' /etc/cmpp-platform/cmpp-platform.env
grep -E '^(API_HTTP_KEEP_ALIVE_TIMEOUT_MS|API_HTTP_HEADERS_TIMEOUT_MS|CMPP_INBOUND_FAST_PATH_ENABLED|CMPP_INBOUND_WORKFLOW_WORKER_ENABLED|API_INBOUND_WORKFLOW_CONCURRENCY|API_INBOUND_WORKFLOW_BATCH_ENABLED|API_INBOUND_WORKFLOW_BATCH_SIZE|API_INBOUND_WORKFLOW_BATCH_WAIT_MS|API_INBOUND_WORKFLOW_TARGET_BATCH_SIZE|API_INBOUND_WORKFLOW_POLL_INTERVAL_MS|API_INBOUND_WORKFLOW_STALE_SECONDS|API_DB_POOL_MAX|API_WORKER_DB_POOL_MAX|API_WORKER_METRICS_PORT)=' /etc/cmpp-platform/cmpp-platform.env
systemctl is-active cmpp-api cmpp-send-worker cmpp-gateway
curl -fsS http://127.0.0.1:9465/metrics | grep '^cmpp_worker_inbound_workflow_'
redis-cli --scan --pattern 'rate:gateway:channel:*'
redis-cli XINFO GROUPS gateway.submit.commands
redis-cli XINFO GROUPS gateway.submit.results
```
## 回滚
1. 数据库备份:
预生产 `8.160.169.106` 已于 2026-08-31 完成数据盘第一阶段:`/opt/cmpp-platform-backups` 指向 `/data/cmpp-platform-backups`。后续备份先执行存储检查,再写入该固定入口;不得重新写入随运行目录切换的 `/opt/cmpp-platform/backups`。其他环境需先完成相应存储配置,不可直接套用预生产的磁盘 UUID。配置、验证及第二阶段边界见 [数据盘第一阶段记录](preprod-data-disk-stage1-20260831.md)。
```bash
set -e
/usr/local/sbin/cmpp-backup-storage-check
set -a
. /etc/cmpp-platform/cmpp-platform.env
set +a
pg_dump "${DATABASE_URL%%\?*}" > /opt/cmpp-platform-backups/cmpp-$(date +%F-%H%M%S).sql
```
2. 回滚代码:
```bash
cd /opt/cmpp-platform
git reset --hard <上一版提交>
bash tools/deploy/production-deploy.sh
```
3. 如迁移造成不可兼容故障,先停服务,再恢复数据库备份。
## CMPP耐久Inbox与独立Worker发布门禁(2026-08-20
- 发布前恢复资产除PostgreSQL、运行源码和环境文件外,必须包含`cmpp-api.service``cmpp-send-worker.service`及其drop-in;逐项校验`pg_restore --list`、tar可读性和SHA-256。数据库回滚与运行代码必须成套执行,禁止只回退代码后让旧Prisma Client访问新状态机。
- 环境必须显式启用`CMPP_INBOUND_FAST_PATH_ENABLED=true``CMPP_INBOUND_WORKFLOW_WORKER_ENABLED=true`,并给出正整数`API_INBOUND_WORKFLOW_CONCURRENCY``API_DB_POOL_MAX``API_WORKER_DB_POOL_MAX`;推荐初始值分别为32槽、API池32、Worker池8、轮询100ms、租约300秒。API systemd角色必须是`api`Worker角色必须是`worker`Worker可通过`API_WORKER_DATABASE_URL`使用独立地址,未配置时仍使用同一数据库地址但保持独立进程和有界连接池。发布前必须核对两池之和、其他服务连接与运维余量不超过PostgreSQL`max_connections`
- Worker日志目录归`cmpp-api:cmpp-security`且仅服务可写,9465只监听回环并加入Prometheus `cmpp-send-worker` target。发布后必须验证两进程均为非root、API/Gateway health、Worker metrics、PostgreSQL/Redis,以及Inbox pending/processing/最老等待可观测。
- 回滚前先停止Gateway、API和Worker,保留故障现场Inbox及日志;如恢复旧数据库备份,必须同时恢复对应源码和环境/systemd资产。不得在回滚时删除pending Inbox或重投真实短信。
- 第三阶段要求环境显式设置`API_INBOUND_WORKFLOW_BATCH_ENABLED=true`和正整数`API_INBOUND_WORKFLOW_BATCH_SIZE`,初始建议64且不得大于Worker业务槽的可解释倍数。批次增大前必须核对PostgreSQL参数数量、单事务持续时间、Worker RSS和租约时长;付费短信仍走逐条账务锁,不能用零计费批量结果替代付费链路验收。
- 单企业微批阶段要求显式设置`API_INBOUND_WORKFLOW_BATCH_WAIT_MS=40``API_INBOUND_WORKFLOW_TARGET_BATCH_SIZE=32`,目标不得超过批次上限;发布脚本拒绝负等待、超过250ms或目标越界。API还必须设置`API_HTTP_KEEP_ALIVE_TIMEOUT_MS=120000`和更大的`API_HTTP_HEADERS_TIMEOUT_MS=125000`,确保服务端keep-alive长于Gateway 90秒空闲池;发布后用响应头回读实际timeout并检查Gateway日志无loopback reset。
- `GATEWAY_CALLBACK_BATCH_ENABLED`默认关闭,只有独立Gateway callback进程已启用、`GATEWAY_CALLBACK_API_BASE_URL`指向其回环端口且批量路由健康检查通过时才允许显式设为`true`。主API回调模式必须保持`false`;发布后同时检查`gateway.submit.results``pending/lag`,任何持续增长均视为发布失败,禁止手工ACK掩盖回调未持久化。
- 服务重启顺序必须为“独立callback(如启用)→ Gateway → API → Send Worker”。API必须在Gateway已健康后重启,利用启动同步重新下发活动供应商通道;发布后`cmpp_gateway_upstream_connections{state="desired"}``connected`必须恢复到发布前活动连接数,不能只凭Gateway `/health` 判定发布成功。
## 测试环境持久验收管理员(2026-09-06)
用户明确授权创建并保留后续复用的专用测试管理员。2026-09-06在测试环境100.93.204.60创建`codex_qa_admin`,显示名“自动化验收管理员”,用户ID`cmtphgp340000yrle7v11jemc`,角色`platform_admin`、状态`active`;正常验证码登录和退出已验证。该账号仅在测试环境创建,预生产不适用。
安全认证入口:工作站`C:\Users\hectorzhao\.config\cmpp-qa\test-admin.json`,仅当前Windows用户及SYSTEM可访问;测试机备份入口`/home/hector/.config/cmpp-qa/test-admin.json`,目录0700、文件0600、所有者hector。文件保存随机生成密码和登录地址,禁止打印内容、提交Git或将密码写进命令、截图和报告。后续通过既有SSH安全入口读取到内存或使用本机受限文件完成正常登录;账号和凭据按用户要求保留,测试结束退出会话即可。使用前重新核验账号有效性,不覆盖已有账号、不因认证失败重置其他管理员;账号存在不构成发送短信、改余额或通道/客户配置的授权。