来源:
notes/topics/environment_backup_restore/nas-vm-sync.md宿主机存文件、虚拟机跑程序:NAS ↔ 虚拟机同步方案
日期:2026-09-18 · 状态:已跑通并挂上定时任务
1. 分工与数据流
- 虚拟机是唯一的运行环境,所有需要执行的东西都放在那里
- 虚拟机从 GitHub 拉取代码(
git pull),它是代码的消费端 - 宿主机(NAS)只从虚拟机同步,单向镜像,不反向推送
编辑(本机) → GitHub → 虚拟机(拉取、运行) → NAS(单向同步、仅存储)
方向是固定的:VM 是源,NAS 是目的地。NAS 不参与代码分发。
2. 拓扑
飞牛 fnOS 宿主机 andy6
10.71.48.167 (ZeroTier) / 192.168.110.147 (内网)
└── /vol1/1000/andy/1351800/cc-connect-work-space/ ← 只存文件
▲ SMB 共享名 andy,卷剩余 ~386 GB
│ rsync over SSH(免密),单向:VM → NAS
│
libvirt 虚拟机 192.168.110.91 (Deepin 25)
/home/admin/code/cc-connect-work-space/ ← 运行环境 + 拉代码
宿主机 andy 共享同时暴露为 SMB(//192.168.110.147/andy),Windows/macOS 也能直接访问。
3. 为什么用 rsync over SSH
先评估过其他路径,都不如它:
| 方案 | 结论 |
|---|---|
挂载 SMB(mount.cifs) |
❌ 两台机器都没有免密 sudo,备份机还没装 cifs-utils |
| 挂载 NFS | ❌ 同上,且备份机无 nfs-common |
| rclone(SMB 后端) | ⚠️ 用户态可行,但从备份机下载 rclone 实测只有 ~33 KB/s,20MB 下了 10 分钟没下完 |
| rsync over SSH | ✅ 两边都自带 rsync,免密打通后即用,增量、无需 root、无需下载 |
4. 凭据
- 备份机的
~/.ssh/id_ed25519.pub已写入宿主机~/.ssh/authorized_keys - 撤销方式:删掉宿主机该文件里对应的那一行即可
- 宿主机 SSH/SMB 账号:
admin
5. 脚本
位于虚拟机 /home/admin/code/cc-connect-work-space/nas-sync/:
| 脚本 | 方向 | 用途 |
|---|---|---|
sync-push.sh |
虚拟机 → NAS | 日常唯一操作,把工作区镜像到 NAS |
sync-pull.sh |
NAS → 虚拟机 | 仅灾难恢复时手工用,不属于日常流程 |
两者都支持 --dry-run 预览。
push 的关键行为:
--delete,NAS 侧与虚拟机严格一致- 但删除是软删除 —— 被删文件移入
/vol1/1000/andy/1351800/.rsync-trash/<时间戳>/,可随时找回 - 排除
.venv/、node_modules/、__pycache__/(这些应当重建而非同步)
sync-pull.sh 存在的理由:代码本身可以从 GitHub 重新 clone,
但未提交的改动、nas-sync/ 工具目录、以及工作区里的非 git 内容只能从 NAS 找回。
6. 实测结果
| 项目 | 结果 |
|---|---|
| 首次全量(543 MB / 2024 项) | 2.66 秒,~233 MB/s |
| 增量(无变化) | 秒级,零传输 |
| 新增文件 → push | NAS 正确出现 |
| 删除文件 → push | NAS 正确移除,且进入 .rsync-trash |
| 4 个仓库 commit 校验 | 全部与虚拟机 HEAD 一致 |
速度之所以这么快,是因为虚拟机跑在这台宿主机上,同一物理机内通信近乎本地磁盘速度。
7. 定时任务
虚拟机上的用户 crontab,每 30 分钟 push 一次:
*/30 * * * * /bin/bash -lc "cd /home/admin/code/cc-connect-work-space/nas-sync && bash sync-push.sh >> /home/admin/.local/log/nas-sync.log 2>&1"
日志:~/.local/log/nas-sync.log
8. 注意事项
- 不要在 NAS 侧直接改文件 —— 它是纯镜像,改动会被下次 push 覆盖。 要改就在虚拟机上改。
- 软删除目录会持续增长,
.rsync-trash/需要定期清理(尚未加自动清理)。 - 这套同步解决的是「文件放在有冗余的存储上」,不等于云备份。
虚拟机损坏时有两条恢复路径:从 GitHub 重新 clone(代码),
或
sync-pull.sh从 NAS 拉回(含未提交内容); 但 NAS 本身损坏时这份数据同样会丢,跨地域的云备份仍需另做 (见 cloud-options-research.md)。
9. 刻意的设计选择
9.1 git pull 不自动化
虚拟机上的 git pull 故意保持手动,不放进定时任务。
原因:虚拟机上经常有 agent 正在写代码,自动 git pull 会在它写到一半时
替换工作区文件,导致改动丢失或产生难以排查的冲突。拉取时机必须由
「当前没有任务在跑」这个人类判断来决定。
要自动化时请先想清楚这一点 —— 这是有意为之,不是遗漏。
9.2 保留 sync-pull.sh
虽然名为”灾难恢复”,但它覆盖了 GitHub 覆盖不到的部分:
未提交的改动、nas-sync/ 工具目录、工作区里的非 git 内容。
所以保留,但不纳入日常流程。
9.3 定时任务只做 VM → NAS
sync-push.sh 对虚拟机的文件是只读的(只读源、只写 NAS),
因此它在 agent 工作时也不会干扰,可以安全地每 30 分钟跑一次。