Files
lisglosips/docs/S30_RELEASE_BACKUP_RUNBOOK.md
T

5.5 KiB
Raw Blame History

S30 备份、Runbook、灰度与上线

1. 范围

当前 S30 面向本地 KVM 开发环境的 V2 闭环冻结与上线演练,不执行真实阿里云切流。阿里云迁移必须在新 VPC、正式 DNS/TLS、独立数据盘和安全组完成后,按本文重新演练。

2. 冻结清单

当前值
B release /opt/lisglosips/releases/s28-v2-20260621220924
B current /opt/lisglosips/current 指向当前 release
A OpenSIPS /etc/opensips/opensips.cfg
T 测试脚本 /opt/lisglosips-s28/lisglosips-s28-sip.py
数据库迁移 prisma/migrations/ 和 B 当前库为准
Redis 配置版本 cfg:active_version 为准

冻结后只允许以下变更进入上线窗口:

  • 修复已确认阻塞缺陷;
  • 更新正式域名/TLS/安全组等环境参数;
  • 文档化灰度和回滚记录。

3. 上线前备份

在任何 release 切换、数据库迁移、OpenSIPS 配置替换、Nginx/TLS 调整前执行:

sudo systemctl start lisglosips-backup.service
sudo find /data/backups/mysql -mindepth 1 -maxdepth 1 -type d | sort | tail -1
sudo find /data/backups/redis -mindepth 1 -maxdepth 1 -type d | sort | tail -1
sudo sha256sum -c /data/backups/mysql/<stamp>/SHA256SUMS
sudo sha256sum -c /data/backups/redis/<stamp>/SHA256SUMS

另外备份:

sudo install -d -m 0700 /var/backups/lisglosips-s30/<stamp>
sudo cp -a /etc/lisglosips /etc/nginx /etc/systemd/system/lisglosips@.service /var/backups/lisglosips-s30/<stamp>/
sudo cp -a /etc/opensips/opensips.cfg /var/backups/lisglosips-s30/<stamp>/opensips.cfg

备份目录只允许 root 读取,不复制明文 secret 到仓库或聊天记录。

4. 恢复演练

MySQL 只恢复到隔离临时库:

sudo mysql -e 'DROP DATABASE IF EXISTS lisglosips_s30_restore; CREATE DATABASE lisglosips_s30_restore CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;'
gzip -dc /data/backups/mysql/<stamp>/lisglosips.sql.gz | sudo mysql lisglosips_s30_restore
sudo mysql -e 'SELECT COUNT(*) FROM lisglosips_s30_restore.raw_cdrs;'
sudo mysql -e 'DROP DATABASE lisglosips_s30_restore;'

Redis 只启动隔离临时实例:

sudo install -d -m 0700 /tmp/lisglosips-s30-redis-restore
sudo cp /data/backups/redis/<stamp>/dump.rdb /tmp/lisglosips-s30-redis-restore/dump.rdb
sudo redis-server --dir /tmp/lisglosips-s30-redis-restore --dbfilename dump.rdb --port 0 --unixsocket /tmp/lisglosips-s30-redis.sock --daemonize yes
sudo redis-cli -s /tmp/lisglosips-s30-redis.sock DBSIZE
sudo redis-cli -s /tmp/lisglosips-s30-redis.sock SHUTDOWN NOSAVE

禁止把备份直接覆盖运行中 /var/lib/mysql/var/lib/redis

5. 发布顺序

  1. 记录当前 release、数据库迁移版本、Redis cfg:active_version、A OpenSIPS 配置 SHA-256。
  2. 执行 S30 备份与恢复演练。
  3. 上传新 release 到 /opt/lisglosips/releases/<release-id>,校验 package.jsonpnpm-lock.yaml 和构建产物。
  4. 执行向前兼容数据库迁移,禁止同窗口不可逆删列。
  5. 原子切换 /opt/lisglosips/current
  6. 重启 lisglosips@apilisglosips@cdr-workerlisglosips@recording-worker
  7. nginx -t 后 reload Nginx。
  8. 执行 infra/server-b/s30/lisglosips-release-preflight.sh
  9. 发布 Redis 配置快照并确认 A 使用新版本。
  10. T 发起测试呼叫,按 Call-ID 核对 SIP、CDR、计费、录音、HOMER。
  11. 开始灰度。

6. 灰度策略

本地环境使用 cus_s28_t 和 T 专用 UAS 作为灰度对象;生产环境选择一个低风险客户或一条低风险线路。

灰度观察项:

  • SIP 2xx/4xx/5xx 分布;
  • Redis Stream lag 和 pending
  • CDR 入库与重复扣费;
  • 余额变化和充值幂等;
  • 录音 READY 比例、A tmpfs 使用率;
  • API 5xx、P95/P99
  • HOMER 是否可按 Call-ID 查询。

扩大条件:连续观察 24-72 小时无 P1/P2 缺陷、无 CDR/余额异常、录音搬运无持续积压。

7. 回滚

B 应用回滚

sudo infra/server-b/s30/lisglosips-release-rollback.sh /opt/lisglosips/releases/<previous-release>

脚本会切换 current、重启 API/CDR/Recording Worker、校验 Nginx 并检查 API ready。

Redis 配置回滚

切回上一个 cfg:active_version,不删除 Stream 或 deadletter。回滚后用 T 呼叫验证 A 已读取旧版本。

OpenSIPS 回滚

sudo infra/server-a/s30/lisglosips-opensips-config-restore.sh /var/backups/<path>/opensips.cfg

脚本会先备份当前配置、执行 opensips -C,通过后替换并重启 OpenSIPS。重启会影响现有通话,生产必须放在维护窗口或确认可接受。

数据库回滚

优先发布向前修复迁移;只有在确认不可用且有完整备份时,才进入维护窗口做数据恢复。充值、CDR、审计、录音记录不直接物理删除。

S29 Redis 恢复遗留风险

若 B Redis 故障恢复后 A 继续返回 503 Redis Unavailable

  1. 确认 B redis-server activeA 到 B Redis 端口可达。
  2. 重启 A lisglosips-redis-auth-proxy
  3. 若仍失败,维护窗口内重启 A opensips
  4. 记录 Call-ID、A 日志、Redis proxy 日志,作为后续根因修复输入。

8. 阿里云迁移前阻断项

  • 正式 DNS/TLS 未完成;
  • A/B 未位于同 VPC 低时延私网;
  • B 未使用独立数据盘承载 MySQL、Redis、录音和监控;
  • 安全组未限制 SSH 管理来源,或禁用了 SSH 密钥登录;
  • 未完成新环境备份恢复演练;
  • 未完成 T 或外部测试端到端呼叫;
  • Redis 恢复后 OpenSIPS 自动恢复风险未纳入告警/Runbook。