来源:notes/topics/environment_backup_restore/deepin-immutable-and-reboot-verification.md

Deepin 25 不可变系统:修改层机制与重启连通性验证

日期:2026-09-18 · 状态:已通过一次真实重启验证

本文回答一个具体问题:备份机(虚拟机)重启后,ZeroTier 会不会自己起来? 结论:会,已实测。顺带把不可变系统的机制、一个长期失败的系统服务、 以及恢复包的位置记录下来。

1. 为什么这件事必须专门验证

备份机唯一的远程入口是 ZeroTier 分配的 10.71.48.31。一旦重启后 ZeroTier 没起来,就等于彻底失去这台机器的远程访问,只能靠飞牛 NAS 的虚拟机控制台救。

而这台机器是 Deepin 25 的不可变系统(ostree):/usr/opt/etc 都是 overlay 挂载,写入内容落在「修改层」里,表面看很像”重启就没了”。所以必须实测, 不能靠推测。

2. 存储布局:哪些是持久的

findmnt 实测(节选):

挂载点 来源 类型 说明
/ /dev/sda3 ext4 rw 持久
/persistent /dev/sda5 ext4 rw 持久(62 G 数据盘)
/usr usr-overlay overlay ro 在修改层
/opt opt-overlay overlay rw 在修改层
/etc etc-overlay overlay rw 在修改层
/var /dev/sda5[/ostree/deploy/deepin/var] ext4 rw 不在修改层
/home /dev/sda5[/home] ext4 rw 不在修改层

修改层实测只包含三个顶层目录:etc/opt/usr/ —— 不含 var/, 也不含 home/。这一点是整个连通性判断的基石。

修改层的物理位置在持久分区上:

/persistent/overlay/data/layer-<16位十六进制>/{etc,opt,usr}

3. 连通性链条

从加电到 SSH 可用的完整依赖链,逐项实测:

环节 实测状态 存放位置 重启后
网卡 ens8 自动连接 AUTOCONNECT=yes 层内 /etc/NetworkManager
ssh.service 自启 enabled + multi-user.target.wants 层内 /etc
zerotier-one.service 自启 enabled + multi-user.target.wants 层内 /etc
ZeroTier 节点身份 identity.secret /var(层外)
已加入网络 166359304eb8bd7a networks.d/*.conf /var(层外)
自管 IP 模式 allowManaged=1 /var(层外)
SSH 公钥 authorized_keys /home(层外)

关键设计红利:ZeroTier 的身份和网络成员资格都在 /var,不在修改层里。 即使某天丢失了 /opt/zerotier 这个二进制,重装后节点 ID 和 10.71.48.31 都会原样回来 —— 不需要去 ZeroTier 后台重新授权

4. 实测:一次真实重启

2026-09-18 21:05 执行 sudo systemctl reboot,记录如下:

指标 重启前 重启后
boot_id 64329d4d-735c-4afd-99ee-a19cea5f39f2 6eee882f-335b-4a41-ad80-6fd7ca45b635
ZeroTier active active
zerotier-cli listnetworks OK OK PRIVATE zteyw6ddju 10.71.48.31/24
ssh.service active active
cc-connect-qqbot active active
工作区 git HEAD 3cf01a3 3cf01a3
  • 关机耗时约 5 秒内不可达,开机到 SSH 可用约 2.5 分钟
  • 全程零人工干预,ZeroTier 自动重连并拿回同一个 IP

结论:重启安全性已由实测确认。

5. 修改层机制

这套系统的关键概念:

  • base commit —— 只读的 ostree 系统镜像(/ostree 只读挂载)
  • 修改层(modification layer) —— overlay 的 upperdir,记录 apt install 装的东西、手改的 /etc 配置等
  • 层在持久分区上,重启不会清空

刷新只在两种情况下发生,且都是”先合并、后清空”:

触发 条件 行为
deepin-immutable-ctl admin deploy 手动或开机自动 把「base + 修改层」合成新 commit → 新 deployment → 更新 GRUB
--auto-refresh 修改层体积 > 200 MiB 合并之后再刷新层,重新开一个干净的

阈值配置在 /etc/deepin-immutable-ctl/deepin-immutable-ctl.conf:

# auto-refresh-size-threshold, type int, default is 200,
# the size threshold in MiB for auto refresh modification layer.
[global]
merge-modification-enabled=true   # 注意:默认是 false,这台被显式打开了
#auto-refresh-size-threshold=200

5.1 实测到的合并行为

重启前修改层 267 M(usr/ 256 M、opt/ 11 M、etc/ 2 M)。重启后:

位置 重启前 重启后
活动层 layer-72c793575ac95928 267 M 1 M
af888468….0/opt-upper/zerotier 不存在 存在

也就是内容被搬进了 deployment,没有丢。这印证了”先合并、后清空”的语义。

6. 一个长期失败的系统服务(现已修复)

/usr/lib/systemd/system/deepin-immutable-cleanup.service,由同名 timer 在 每次开机 +5 分钟触发:

ExecStart=/usr/sbin/deepin-immutable-ctl admin deploy --auto-refresh --wait-boot
Restart=no

职责:把修改层合并进 ostree 仓库、按需刷新层、清理旧层目录释放空间。

历史失败记录:

启动 时间 原因
boot -1 13:53:19 deepin-immutable-cleanup.timer: Unit to trigger vanished + Failed with result 'resources'
boot 0 14:28:42 unable to acquire the operation lock (/var/lib/deepin-immutable-ctl/lock)

第二次不是偶然,是配置层面的缺陷:unit 只传了 --wait-boot(等 boot 锁), 没传 --wait(等操作锁)。工具自己的报错就提示了 use -w|--wait。 而 Restart=no + timer 只在开机后触发一次 —— 所以开机 5 分钟内只要撞上别的 deepin-immutable-ctl 调用者(同机还有 lastore-daemondeepin-locale-helperuos-recovery),它就当场失败且本次开机不再重试。

风险在于: deploy 从没成功 → 层只增不减 → 机器的”家底”一直处于 未提交状态,靠”层还在”维持,而不是靠”已固化进镜像”。

2026-09-18 21:10:18 首次成功:

Duration: 50.250s
Process: 3039 ExecStart=… admin deploy --auto-refresh --wait-boot
         (code=exited, status=0/SUCCESS)

日志里能看到完整的 grub-mkconfig / os-prober 过程。至此该隐患解除。

7. 恢复包

位置:/persistent/recovery-kit/(11 M,在修改层之外,不受层刷新影响)

文件 内容
zerotier/bin/ zerotier-onezerotier-clizerotier-idtool
zerotier-one.service systemd unit 原件
profile.d-zerotier.sh /etc/profile.d/zerotier.sh 原件
restore-zerotier.sh 一键恢复脚本
README.md 说明与验证方法

只有在确认 /opt/zerotier 或 unit 丢失时才需要:

sudo bash /persistent/recovery-kit/restore-zerotier.sh
sudo /opt/zerotier/bin/zerotier-cli listnetworks   # 期望 … OK … 10.71.48.31/24

如果 SSH 也连不上,用飞牛 NAS 的虚拟机控制台登进去再跑。

8. 待办

  • 新 deployment 待生效:本次 deploy 生成了 e1056880fca0a37d…(数据在 /persistent/ostree/data/e1056880….0/),当前仍在跑 af888468…(已标记 hidden)。新部署会在下一次重启时生效,届时需要再验证一次。 GRUB 有「回退到…」菜单项,auto-rollback-on-failure=true 仍为开, 兜底存在。
  • 系统自带缺陷:deepin-immutable-cleanup.service 只传 --wait-boot 不传 --wait,升级后可能复现,值得复查。
  • merge-modification-enabled=true 与默认不同,走的是较少人验证的分支。
  • /persistent 已用 15 G / 62 G,注意 deployment 数量增长 (max-backup-deploys 默认 1)。

9. 维护命令速查

# 部署状态(不要用 ostree admin status,这台机器的布局不支持)
sudo /usr/sbin/deepin-immutable-ctl admin status

# 手动提交修改层(慎用,是系统级操作)
sudo /usr/sbin/deepin-immutable-ctl admin deploy

# 快照(安装时已有一个 "First backup" / eb318047dc0ac882)
sudo /usr/sbin/deepin-immutable-ctl snapshot list

# ZeroTier 状态
sudo /opt/zerotier/bin/zerotier-cli info
sudo /opt/zerotier/bin/zerotier-cli listnetworks

# 修改层体积
sudo du -sm /persistent/overlay/data/layer-*

10. 一个容易踩的取证陷阱

虚拟机开机初期系统时钟是错的(boot -1 的日志里时间从 21:48:56 跳回 13:51:47,差约 8 小时,RTC/NTP 校正后恢复)。这导致启动早期创建的文件 带”未来时间戳”—— 例如修改层目录的 Birth 读数是 22:23:41,而当时真实 时间还不到 21:00。

排查这类机器时不要用文件时间戳做因果推断,以 journalctl -b 的日志 顺序和 boot_id 为准。当前时钟已校正正常。

11. 附:两台机器 sudo 密码不同

本机与虚拟机的 admin sudo 密码不是同一个,容易记混。 密码本身不入库,记录在各自的密码管理里。