16 KiB
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。
首次部署
- 本机提交并推送
main。 - 使用 root 登录服务器,或先放置免密 SSH key。
- 在服务器执行:
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
可覆盖的环境变量:
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/Ubuntu服务器依次执行bash tools/monitoring/install-prometheus-monitoring.sh和bash tools/monitoring/install-service-exporters.sh;前者安装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 和健康检查;Let’s 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,因此该虚拟主机的client_max_body_size必须不低于30m,标准bootstrap配置为50m。api.lisglo.com只承载单条公网HTTP API、Swagger和健康检查,不承载客户文件导入;不要为导入需求开放私有路由或把NestJS所有JSON接口统一放宽到25MiB。发布前使用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。
后续发布
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,但不会执行任何人工封禁。环境文件至少明确:
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-only,nftables/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后即可免密登录。
日志
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
健康检查
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
回滚
- 数据库备份:
pg_dump "$(grep '^DATABASE_URL=' /etc/cmpp-platform/cmpp-platform.env | cut -d= -f2-)" > /opt/cmpp-platform/backups/cmpp-$(date +%F-%H%M%S).sql
- 回滚代码:
cd /opt/cmpp-platform
git reset --hard <上一版提交>
bash tools/deploy/production-deploy.sh
- 如迁移造成不可兼容故障,先停服务,再恢复数据库备份。
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使用独立地址,未配置时仍使用同一数据库地址但保持独立进程和有界连接池。发布前必须核对两池之和、其他服务连接与运维余量不超过PostgreSQLmax_connections。 - Worker日志目录归
cmpp-api:cmpp-security且仅服务可写,9465只监听回环并加入Prometheuscmpp-send-workertarget。发布后必须验证两进程均为非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判定发布成功。