# 预生产数据盘第二阶段(2026-08-31晚间) ## 授权、范围与结果 - 用户明确要求“开始执行第二阶段”;主机`8.160.169.106:12022`,仅迁移PostgreSQL、Redis、MinIO三项持久化存储。 - 实际业务停写窗口:`2026-08-31 22:31:57—22:33:14 +08:00`,约77秒。API在窗口内不可用,CMPP上下游连接经历断开与重连,不能表述为零中断。 - 当前业务程序、应用日志、Prometheus和系统日志未迁移,Nginx及Security Agent未重启;未发送、补发或手工重投短信,未手工ACK/XDEL或清理业务队列。 - 22:36复核11项服务active,供应商连接9/9,客户端连接恢复4个(刚恢复时短暂为2个),API/Callback/Gateway/MinIO健康、PostgreSQL接受连接、Redis PONG,外部API健康通过。 ## 迁移前真实状态 - 部署标记`1a5063a7c635280b912021c42c33a07c99f4c5dd`;第二阶段未发布新的业务产物。 - 数据盘UUID `ef4ee3bb-a19b-4aeb-b00c-aa2b995611c2`,第一阶段挂载和备份保护正常。 - PostgreSQL 15.18,PGDATA `/var/lib/pgsql/data`,不存在外部表空间或WAL符号链接;数据库`postgres`及`cmpp_platform`随完整父目录迁移。 - Redis目录`/var/lib/redis`,RDB `dump.rdb`,AOF关闭;未借迁盘改持久化策略。 - 对象存储驱动为MinIO,目录`/var/lib/minio`,62个业务对象,总大小40846589字节。未切换到local备用驱动。 - 下游连接4个、上游9/9,在途提交0。命令Stream长度75533但pending/lag均0,是已消费历史条目,不能据长度清队列;结果及协议日志Stream长度0、pending/lag均0。 - Inbox completed 4613,Submit Outbox published 867,UpstreamReceiptInbox matched 140861 / unmatched 7;7条既有未匹配回执未作为迁盘问题删除或强制处理。 ## 恢复资产与执行顺序 - 本次恢复点:`/data/cmpp-platform-backups/preprod-storage-stage2-20260831T142327Z`(也可通过原固定备份入口访问)。旧版本`4d526b8`原恢复点继续保留。 - 停写前已保存PostgreSQL custom dump、全局角色、当前运行目录、系统配置和fstab,`SHA256SUMS.online`、`pg_restore --list`及两份tar可读性通过。配置备份第一次遇到不存在的`/etc/sysconfig/pgsql`而中止;去除错误输入、重新生成并校验后才进入停写窗口。 - 停Send Worker、Outbox和API,确认Gateway在途及结果Outbox为0,再停Gateway;核对Stream无待处理后停Callback和Protocol Log Worker。 - 在冻结状态保存110张public表的精确行数、MinIO对象清单及三条Stream的长度/组状态;执行Redis SAVE,再停止MinIO、Redis、PostgreSQL。 - 确认九个写入相关服务MainPID均为0,`pg_controldata`显示`shut down`后,以40MiB/s限速做完整冷复制。不是把普通在线目录复制当作一致备份。 - PostgreSQL 2225个文件/目录条目、2373269966文件字节;Redis 2条目、184085437字节;MinIO 249条目、40914208字节。三组全部文件SHA-256、类型、属主、权限、mtime和扩展属性与源目录一致。 - 原目录改名为以下冷副本,再建立绑定挂载;它们未删除,也不再是服务使用的数据目录: - `/var/lib/pgsql.systemdisk-stage2-20260831` - `/var/lib/redis.systemdisk-stage2-20260831` - `/var/lib/minio.systemdisk-stage2-20260831` - 恢复点中的`cold-storage.tar.gz`包含上述三项同一停写窗口的原数据副本,约611MiB;压缩包可读性及`tar --compare`源文件内容比对通过。`SHA256SUMS.final`覆盖36项恢复文件并全部校验通过,完整恢复点约1006MiB。在线逻辑dump与独立Redis文件不能替代这套一致冷备。 ## 当前路径与启动保护 | 服务 | 原访问路径 | 实际数据盘目录 | systemd挂载单元 | |---|---|---|---| | PostgreSQL | `/var/lib/pgsql` | `/data/postgresql` | `var-lib-pgsql.mount` | | Redis | `/var/lib/redis` | `/data/redis` | `var-lib-redis.mount` | | MinIO | `/var/lib/minio` | `/data/minio` | `var-lib-minio.mount` | - 原服务参数、账号、业务路径不变。fstab绑定挂载使用`bind,nofail,x-systemd.requires=data.mount,x-systemd.after=data.mount`。 - 每个服务的`/etc/systemd/system/.service.d/50-cmpp-data-disk.conf`配置`RequiresMountsFor`、`After`及`BindsTo`,并以root权限在`ExecStartPre`调用存储检查;数据盘缺失不能因`nofail`启动空库。 - `/etc/cmpp-platform/data-disk.uuid`保存预期UUID。`/usr/local/sbin/cmpp-data-storage-check`核验数据盘、对应绑定挂载、源/目标设备号及inode一致、读写挂载选项、PG_VERSION/RDB/MinIO格式标记。 - 三个系统盘底层空挂载点均设置immutable;第一阶段底层`/data`的保护保持不变。 - 在隔离mount namespace内实测:缺少绑定挂载、缺少数据盘、错绑同盘其他目录、只读绑定挂载均被拒绝;底层系统盘写入失败。宿主机挂载及三个服务PID在这些测试中未改变。 - `findmnt --verify --verbose`无错误或警告。此次未重启整机,开机过程尚未通过真实重启验收;未通过真实拔盘触发线上停机测试。 ## 恢复前后数据核对 - 新位置启动三项存储后、恢复业务前,110张public表精确行数逐项一致;其中短信记录74457、提交记录75532、回执记录148281、上游回执Inbox 140868。 - 三条Redis Stream在冻结前后长度、consumer group、pending、lag、last-delivered-id及entries-read完整一致。恢复Worker后协议日志组consumer数从2变3是新进程注册,pending/lag仍0,未删除旧consumer。 - MinIO 62个业务对象的key/size/etag清单一致,并完成独立随机对象的上传、下载内容比对与删除;未操作已有业务对象。 - PostgreSQL以临时表事务执行真实写读并ROLLBACK;Redis使用随机诊断key写读删除,附带60秒TTL;无短信发送探针。 - 依次启动Callback/Protocol Log Worker/Outbox、Gateway、API、Send Worker,API启动后恢复供应商连接。启动健康轮询出现短暂端口未监听,随后全部通过;没有将启动瞬态当作已验收服务。 - 封存冷备后的最终数据盘约4.0G使用、93G可用(5%);系统盘约24%、可用44G,其中仍保留约2.4GiB原数据冷副本。容量是本次观察值,后续以实际df为准。 ## 后续部署与回退限制 - 本地及服务器`production-deploy.sh`、`production-bootstrap.sh`增加已有数据盘安装的前置检查,失败发生在初始化/发布写入之前;原systemd drop-in不被这两个脚本覆盖。新增可复用检查源文件为`tools/deploy/check-data-storage.sh`。 - 两个脚本和检查器已通过bash语法检查,两个入口在隔离缺挂载环境中均拒绝继续。未执行完整部署、Git提交或推送;当前运行目录的运维脚本有本次明确记录的修改,业务产物部署标记不变,不能声称运行目录所有文件仍与该commit逐字节一致。 - 新位置已恢复正常业务写入,禁止直接切回系统盘旧目录或冷备覆盖新数据。若需回迁,必须重新停写,将新位置的最新一致数据反向同步,核验后再修改挂载。 - 暂保留的原副本是迁移恢复证据;后续清理需核对本次冷备及业务状态。备份和业务数据同在一块数据盘不能替代异机灾备。 - 服务器执行资产:`/etc/cmpp-platform/storage-stage2-20260831/`;本地原始证据:`outputs/data-disk-stage2-*.txt`。`migration.log`、表行数、Stream/对象清单、文件校验清单、前后配置均位于本次恢复点。