这套 Perforce P4(Helix Core)实验跑在两台 Ubuntu 虚拟机上,GameDemo 是用来模拟游戏研发协作的项目。程序员和美术分别提交过文件,主线做过热修,发布分支也演练过回退。随后做了完整备份,在另一台虚拟机上恢复,最后用 Windows 上的 P4V 同步了 6 个文件。实验使用小样本模拟数据,文中的 192.0.2.x 是文档示例地址。

Checkpoint 4 上传到 NAS 后,systemd 显示 Result=success,备份包也通过了校验。可等任务退出,P4 的 1666 端口就连不上了。后来查到,脚本拉起的 P4 进程跟备份任务在同一个 cgroup 里。

一、主节点、NAS 和恢复节点

Windows:P4V / P4Admin / P4Merge
  ├─ SSL 1666 → p4-primary(192.0.2.10)
  │                └─ CIFS → NAS(192.0.2.20)
  └─ SSH 转发 → p4-monitor(192.0.2.11)
                   ├─ 恢复实例:127.0.0.1:1766
                   └─ 外部 SSL 探测 → p4-primary
本机和外部监控分别通过 SMTP 发告警

两台虚拟机运行 Ubuntu 24.04 LTS,实验使用 P4D/P4 2026.1。主节点通过 Perforce APT 软件包安装 P4,初始化 Testlab 实例。先用 p4dctl status Testlab、监听端口和 p4 info 检查,再从 Windows 客户端连接。切换到 SSL 前先备份实例配置,由服务账户运行 p4d -Gc 生成证书并设置 P4SSLDIR,端口仍用 1666。客户端核对指纹后建立信任,管理员使用有时效的登录票据。

主实例把 root/(P4ROOT)、archives/、journals/ 和 logs/ 分开。server.depot.root 指向 ../archives。主节点负责服务和本地备份,SMB NAS 保存第二份包,p4-monitor 同时承担隔离恢复和跨机探测。恢复实例有自己的 P4ROOT、配置、日志和活动 Journal。

二、研发协作:从路径权限到发布回退

用真实提交和打开文件验证权限

Stream Depot GameDemo 从 //GameDemo/main 起步,设了一名管理员、两名开发和两名美术。main 和后续建立的 dev 都允许成员读取。开发组可写 Source/ 和 Config/,美术组可写 Assets/,管理员保留全局 super。备份旧保护表、移除全局写权限后,用下列命令查路径权限,再用实际提交核对。Change 3、4、5 分别更新了 C++、JSON 和 SVG 文件。

p4 protects -M -u developer //GameDemo/main/Source/main.cpp
p4 protects -M -u artist //GameDemo/main/Assets/player.svg
p4 opened -a

character.png 在 Change 6 以 binary+l 入库。第一个美术账号打开文件时,p4 opened 显示 *exclusive*。第二个账号尝试编辑被拒,等第一个账号 revert 后才打开成功。原有的全局 PNG 规则也匹配这个文件,实测确认的是文件类型和锁的行为。

两名开发者同时编辑 merge-demo.cpp,分别把 speed 从 10 改为 15 和 25。第一份修改提交为 Change 8,第二份提交时遇到 out-of-date。同步最新版本后,用 P4Merge 对照 base、source 和 target,手工将 speed 定为 20,保留 damage=20,提交为 Change 9。

热修提交错了分支,发布错误用 Undo 保留历史

从 main 初始化 dev 后,Change 12 在开发分支把 damage 改为 30。接着把原定给主线的热修提交到了 dev(Change 13),从 main 向 dev 合并时只看到 All revision(s) already integrated。查工作区和提交历史才发现,主线上根本没有这笔热修。Change 17 恢复了 dev 原有的改动,Change 18 把热修提交到 main。Change 19 将主线的 speed=25 合入 dev,保留 damage=30,Change 20 再把结果 copy 回主线。

发布分支起初误选了 release sparse,换成普通 release stream 后,从主线初始化了六个文件(Change 22)。为模拟错误发布,Change 23 写入 damage=9999,随后用 p4 undo 生成 Change 24。release 的 merge-demo.cpp#3 恢复到 speed=25、damage=30,用 p4 filelog 还能看到哪次提交撤销了错误版本。

三、备份与灾备:从 Checkpoint 3 恢复到 P4V

Checkpoint 保存版本历史、权限和工作区等数据库元数据,Journal 记录元数据变更,Archives 保存真正的文件内容。恢复到 Checkpoint 之后的某个时点,还要结合归档和对应的后续 Journal。核心脚本 p4-backup.sh 使用 flock、受限权限和独立暂存目录。停服后确认 1666 已停止监听,再以服务账户运行 p4d -jc 和 p4d -jv;随后复制 Archives、Journal、实例配置、server.id 及 SSL 目录。复制完成后先启动 P4,再打包、验证 tar 可读并计算 SHA256。退出处理会尝试拉起服务并保留失败现场。备份包含认证材料和 SSL 私钥,实验包按私有数据管理。

第一个完整备份包包含 Checkpoint 3。把包传到运行同版 P4D 的 p4-monitor,先核对压缩包的 SHA256,解包后再校验 Checkpoint。在 /srv/p4-restore/Testlab 准备空数据库目录和归档,并设置服务账户权限。下列命令在隔离且已停止的恢复实例上执行。Checkpoint 3 已包含 Journal 2 的变更,因此没有再次重放该文件。

p4d -r /srv/p4-restore/Testlab/root \
    -jr /srv/p4-restore/Testlab/checkpoint/Testlab.ckp.3

重放后统计到 108 个 db.* 表。启动 Testlab-restore 后只监听 ssl:127.0.0.1:1766。用服务账户查验 SSL 指纹时曾报 chdir: .: Permission denied,因为命令继承了无法访问的 /root 工作目录。改在可访问的恢复根目录执行,并显式指定 -r 和 P4SSLDIR 后通过。

在恢复端核对了五个用户、两个组、三个 Streams、18 个分支文件路径,以及 Change 24、路径权限和 PNG 的独占类型。p4 verify -q //GameDemo/... 未报错。Windows 通过 SSH 本地转发接入 1766,在独立 P4V 工作区对 release 执行 Get Latest,同步了 6 个文件。merge-demo.cpp 的 Change 22、23、24 都能查到。用 p4 print 读取历史版本时,Change 23 中的 damage=9999 和 Change 24 中的 damage=30 也对得上。

四、NAS 自动备份遇到的三个故障

p4-backup-nas.sh 先生成本地包,再通过 SMB 3.1.1 加密挂载。脚本用 findmnt 确认实际挂载类型和共享源,避免把数据写到挂载点下的本地目录。已有副本验证一致即跳过;新副本以临时文件上传,SHA256 通过后改成最终文件名,再验一次。NAS 失败时保留本地包。本地和 NAS 分别配置 7 天、30 天保留,至少保留最新一份。

203/EXEC:检查脚本首字节

手动运行 --sync-only 成功,systemd 启动却报 203/EXEC 和 Exec format error。用 head -n 3 | cat -A 与 od 看到开头是 0a 23 21:换行在前,#! 在第二行。修正首行和 LF 换行,给 ExecStart 显式指定 /bin/bash。

备份成功,任务退出后却连不上 P4

Checkpoint 4 和 NAS 上传都成功了,备份单元退出后 P4 却停了。原脚本在任务里直接调用 p4dctl 启动守护进程,p4d 继承了备份单元的 cgroup。这个单元用的是 KillMode=control-group,退出清理时也会终止其中的进程。修复后,无论正常备份还是出错后的恢复,都通过 systemctl 启停软件包自带的 p4dctl.service。它会管理本机所有 P4 实例,实验主机只部署了一个。

服务已启动,状态检查仍判失败

Checkpoint 5 已生成,却卡在恢复检查。把 p4dctl status 的输出分流到两个文件后,确认退出码是 0、stdout 为空,service is running 在 stderr。最终用 status=$(p4dctl status "$SERVICE" 2>&1) 合并流,同时检查退出码、状态文字和 1666 实际监听。

systemctl show p4-backup.service -p Result -p ExecMainStatus -p ActiveState
p4dctl status Testlab 2>&1
ss -lnt 'sport = :1666'
ps -eo pid,user,cgroup,args | grep '[p]4d'

最终手动触发的 systemd 任务生成 Checkpoint 6、轮转 Journal 5。三份本地/NAS 备份通过 SHA256,任务退出码为 0,结束后 p4d 仍归属 /system.slice/p4dctl.service,SSL 连接正常。备份 timer 设置为每天 18:00 UTC(次日北京时间 02:00),Persistent=false,错过维护时段不会补跑。首次无人值守的每日执行在这次记录中仍待验收;软件包自带维护 cron 和自建 timer 的窗口也要分别核对。

五、Python 告警:区分服务故障与探测误报

本机 p4-health-check.py 每 5 分钟检查服务状态和 SSL 响应,还检查磁盘是否达到 85%、备份是否超过 26 小时、校验文件是否存在、备份任务退出状态,以及最近 27 小时的 NAS 同步成功记录。周期检查只确认校验文件存在和日志有记录,备份内容校验由备份与恢复流程执行。计划备份运行时,脚本暂时跳过 P4 连接探测。

配置 SMTP 时,用 heredoc 把脚本传给 python3,占用了 stdin,脚本里的 input() 读到末尾就报 EOFError。改用 python3 -c 传脚本,再用 getpass 读取授权码,配置存入权限受限的 JSON 文件。另一次误报出现在管理员票据过期时。p4 login -s 显示会话已过期,p4 info 仍返回退出码 0、ServerID 和加密状态,只是少了 Server root。去掉对该字段的检查后,SSL 探测依据退出码、ServerID: Testlab 和加密状态判定。它检查的是连接和实例身份,研发读写权限则由实际提交和恢复后的客户端操作验证。

告警脚本使用 Python 标准库 smtplib,用 flock 防止检查重叠,测试与正式告警各用自己的状态文件。healthy → failed 发故障邮件,failed → healthy 发恢复邮件,状态相同则跳过。邮件发送成功后才原子替换状态文件;发送或保存失败返回 2,下一轮可重试。正常返回 0,检测到故障返回 1,这样即使通知成功,监控任务仍能在 systemd 中反映故障。

六、演练:停止 P4 服务与关闭主 VM

本机演练暂停监控 timer 防止重叠,然后停止 p4dctl.service。手动触发正式检查时,脚本同时报告服务停止和 Connection refused,发出故障邮件;重复检查没有再发。重新启动 P4 并验证 SSL 后,收到恢复邮件,监控 timer 重新启用。

主虚拟机关机时,本机监控也会停,所以把外部探测放在 p4-monitor 上。它使用独立的 P4TRUST,每轮结束约两分钟后再运行。单次 p4 info 最多等 10 秒,首次失败后间隔 15 秒再查,连续两次失败才报故障。演练时通过 PVE 正常关闭主虚拟机,监控节点继续运行,探测由 timer 自动触发。

  • 14:32:52 开始本轮远程探测。
  • 14:33:02 第一次连接超时,等待 15 秒后复查。
  • 14:33:20 第二次返回 No route to host,状态转为故障。
  • 14:33:21 发送故障邮件。
  • 14:35:25 主 VM 重启后,外部 SSL 探测通过。
  • 14:35:26 发送恢复邮件。
  • 14:36:56 主节点的 P4、备份及监控 timer 已恢复,本机健康检查退出 0。

以上时间均为 2026 年 11 月 14 日北京时间。这一轮从开始探测到确认故障用了 28 秒,故障和恢复邮件的发送时间相隔 2 分 05 秒。没有记录关机和开机的准确时刻,实际停机时间和 RTO 无法据此计算。模拟测试验证过外部监控在故障持续时会抑制重复邮件,这次关机演练里没有观察到第二轮持续失败。

七、验收结果与待验证项

Checkpoint 4 那次故障之后,备份验收多看了两项:任务结束后 p4d 归属哪个 systemd 单元,以及客户端还能不能连上 SSL。异机恢复还要多走一步,在 P4V 里实际同步文件并查看发布历史。

备份 timer 已启用,首次每日自动运行和 30 天保留策略还没完成实测。维护窗口内如何抑制告警并保留原有状态,也还没验收。当前需要人工恢复,生产规模下的 RTO、RPO,以及跨物理主机和异地恢复均未验证。公开仓库中的脱敏脚本做过静态检查,尚未重新部署。

脱敏脚本、systemd 单元和部署文档见 GitHub · perforce-ops-lab。

转载请保留原文链接,谢谢!
  1. 本站所有资源文章出自互联网收集整理,本站不参与制作,如果侵犯了您的合法权益,请联系本站我们会及时删除。
  2. 本站发布资源来源于互联网,可能存在水印或者引流等信息,请用户擦亮眼睛自行鉴别,做一个有主见和判断力的用户。
  3. 本站资源仅供研究、学习交流之用,若使用商业用途,请购买正版授权,否则产生的一切后果将由下载用户自行承担。
  4. 联系方式: [email protected]