← 发散思维

发散思维

A/B 系统备份:Deepin 20 的两块镜像分区怎么升级

·发散思维

Deepin 20 全盘安装会划出两块一样大的系统分区,标签一般是 Roota 和 Rootb。控制中心更新时,先把当前系统复制到空着的那一块,复制成功才在正在运行的那一块上装软件包。另一块留的是升级前的整份系统,用来在启动失败时从引导菜单回去。

A/B(两套系统槽位。一套是当前在用的,另一套留着上一份或下一份,坏了还能从剩下的那套启动)在桌面系统上常被说成「两个系统镜像分区」。Deepin 20 这一代全盘安装确实是这样摆的。它和后来 deepin 25 的磐石、也和手机上那种无缝升级,步骤不一样。下面按 20.x 全盘安装来写。

1. 磁盘里那两块是谁

全盘安装时,安装器自己切分区,用户不用指定 Rootb。分区工具里能看到一对大小相同的 ext4(第四代扩展文件系统,Linux 上常见的那种磁盘格式)分区,卷标写成 Roota 和 Rootb。正在用的系统挂在其中一块上,另一块平时不出现在文件管理器里。

其余空间大多给 data。用户目录、大量可变数据放在这块上,通过挂载进系统。EFI(可扩展固件接口用的系统分区,里面放引导程序,格式是 FAT(文件分配表,一种简单的磁盘格式,固件规定 EFI 分区用它))单独一块。有的机器还有 swap(把内存换出到磁盘的分区)。

20 这一代早期的全盘安装,两块系统分区经常各只有大约 15GB,磁盘剩下的都进 data。系统分区装软件一多就会满。想保持 A/B 备份,扩容时 Roota 和 Rootb 要一起加大,只扩正在用的那一块,另一块就装不下同样一份系统。

手动分区可以自己划 EFI、根目录和 home。安装器没有一项叫 Rootb。只加一个名叫 recovery 的分区,也接不上控制中心里的备份还原。recovery 和 Rootb 是两件事。

2. 业界其实是两条路

两套槽位都是为了「升级搞坏了,机器还能回到上一份完整系统」。写入的方向相反。

做法升级时写哪一块正在运行的那一块谁在用
先备份,再原地升级把当前系统复制到空槽复制完成后,软件包装在这块上Deepin 20、同时代的统信桌面
先写新系统,再切换新系统写进空槽,写完才重启进去升级过程中保持原样安卓 A/B、ChromeOS、SteamOS

第二条路里,空槽在重启前已经是完整的新系统。引导程序把下一次启动指过去。新系统自己报健康之后,这块才算提交。启动失败就仍从旧槽起来。旧槽在整个写入过程中没有被 apt 改过。

Deepin 20 走第一条。空槽里放的是升级开始之前的系统副本。新软件包装在你正在用的那块根分区上。副本的用途是:这次更新若把机器带到起不来,引导菜单还能进升级前的那一份。

两条路都把用户数据放在槽位外面。系统分区可以整块换、整块回,照片和家目录留在 data 或 home 上。

3. Deepin 20 一次更新的顺序

控制中心点系统更新时,备份服务 deepin-ab-recovery 先跑。它在仓库 linuxdeepin/deepin-ab-recovery 里,20 这一代和统信桌面专业版用的是同一套。

  1. 找到卷标为 Rootb 的分区。配置里用 UUID(分区的唯一编号,换盘符也不会变)记下哪一块是当前系统、哪一块是备份。
  2. 把备份分区挂到 /deepin-ab-recovery-backup。
  3. 用 rsync(远程同步工具,这里在本机把一个目录树镜像到另一个分区)把根目录同步过去。
  4. 把当前正在用的内核和 initrd(内核启动后最先用的内存盘,里面是驱动和挂载根分区所需的程序)拷到 /boot/deepin-ab-recovery。
  5. 改备份分区里的 fstab(开机时按这个表挂载磁盘),让它若被启动,根目录指向它自己。
  6. 写 GRUB(Grand Unified Bootloader,Linux 常用的引导菜单)配置,多一条从备份分区启动的菜单,然后更新引导。
  7. 备份成功,控制中心才继续装更新。备份失败,更新停在「系统备份失败」,正在用的系统还没开始换包。

所以升级的后半段仍是 Debian 系的 apt(Advanced Package Tool,用软件源安装、升级软件包的工具。装上去的是 deb(Debian 软件包格式)包)在当前根分区上改文件。A/B 保证的是有一份升级前的整系统可以启动,保证不了 apt 写到一半时当前这块已经完整。

控制中心点更新
  → 挂载 Rootb
  → rsync 当前根目录到 Rootb(升级前的副本)
  → 内核副本放进 /boot/deepin-ab-recovery
  → GRUB 增加「从备份分区启动」
  → 备份成功
  → apt 在 Roota 上安装更新
  → 重启后仍从 Roota 进入新系统
     起不来时,菜单里选备份分区,进的是 Rootb

4. 复制过去的是什么

同步命令带 -a(保留权限、时间和符号链接)、-X(保留扩展属性)、-x(不跨到别的文件系统)、--delete-after(目标上多出来的文件在复制结束后删掉)。效果是把备份分区收成当前根分区的一份镜像,旧备份里多余的文件不会留下来。

排除列表里有这些目录:

/media  /tmp  /proc  /sys  /dev  /run  /mnt
/boot   /data /lost+found  /recovery  /opt

/proc、/sys、/dev、/run 是内核运行时造出来的,不能当成系统文件拷走。/data 是用户数据盘,和这次系统备份无关。/boot 不进这次 rsync,因为内核文件要按下一节单独留。/opt 常被第三方软件用来放大包,20 这一代的备份默认不带它。

-x 使单独挂载的 home 也不会跟着根分区走。家目录在 data 上时,还原系统不会把备份时刻之后新存的文件退回去,也不会用旧家目录覆盖现在的家目录。

备份分区上会放一个空标记文件 .deepin-ab-recovery-backup。服务靠它认出「这块是备份,不要再拿去当备份的备份」。

5. 内核和引导要单独留

apt 升级经常会换内核。新的 vmlinuz 和 initrd 写在当前系统的 /boot 里。若备份系统开机仍去读这份 /boot,它会拿到升级之后的内核,和 Rootb 里升级前的模块对不上。

所以备份时先按当前启动项找到正在用的内核文件,复制到 /boot/deepin-ab-recovery/。GRUB 里那条备份菜单使用这组内核,根分区参数写成 Rootb 的 UUID。

备份分区自己的 /etc/fstab 也会改:里面的根分区从「当前这块」改成「备份这块」。从菜单进入备份系统时,它把自己当成根,而不是再去挂 Roota。

udev(内核通知用户态「设备插上了」的那套规则)里还会藏起备份分区,避免文件管理器把它当成一块多余的盘挂出来,人随手往里写。

6. 还原是把两块身份对调

从备份启动成功之后,若在控制中心做还原,服务做的是换身份:

还原不必再 rsync 一次。升级前的文件已经在另一块分区上,差的是下一次从哪一块 UUID 启动。对调之后,被 apt 改过的那一块留在备份位,下次更新会用新的当前系统把它盖掉。

7. 手动分区通常没有这一对

这套备份绑在安装器造好的卷标和配置上。全盘安装才会出现 Roota、Rootb 和 ab-recovery 的配置。手动分区的系统,控制中心的备份还原入口会缺,系统更新仍可能去跑备份,然后报备份失败。

论坛里有人在手动方案里加了 /recovery,安装器并没有把 Rootb 交给这项功能。/recovery 可以拿来自己放镜像,它不会在点「系统更新」时自动收到一份 rsync。

想要同样的回退,就要自己留一份升级前的根分区副本,并在 GRUB 里写一条能从那份副本启动的菜单。Deepin 20 把这件事做进了全盘安装。

8. 空间和备份失败

Rootb 必须装得下 Roota 上要备份的那些文件,所以两块划成一样大。根分区只有十几 GB 时,装完系统和几个大软件,备份阶段就会因为空间失败,更新被拦住。这是 20 这一代全盘安装最常见的卡点。扩容要同时加大两块,data 上让出的空间要分给 Roota 和 Rootb。

控制中心报「系统备份失败」时,更新还没有换包。常见原因有三类,都出在备份这一步:

备份成功之后的风险在 apt。装包过程中断电,当前根分区可能停在一半新、一半旧的状态。这时应走引导菜单里的备份项,从 Rootb 启动升级前的系统,再按上一节把身份对调回来。

9. 23 以后换了存法

deepin 23 的原子更新说明写过:版本管理不再依赖一块和根分区一样大的 Roota/Rootb。备份改成快照,相同文件在磁盘上只存一份,手动分区也能用备份还原。deepin 25 的磐石在这之上把 /usr、/bin、/lib 设成只读,更新前做快照,并保留当前系统和上一份备份。

25 的全盘安装仍会分配它所谓的 A/B 镜像空间,用来在控制中心更新之后回退。那是后一代的布局。20.x 磁盘上那两块一样大的 Roota、Rootb,对应的是本文这条路径:先 rsync 出升级前的系统,再在当前分区上 apt。

Deepin 20 全盘安装先写新系统再切换
两块分区里有什么一块是今天的系统,一块是更新前的副本一块是旧系统,一块是已经写好的新系统
更新时改哪块备份完成后改正在运行的那块只改没在运行的那块
用户数据在 data,rsync 不跨过去在单独的数据分区
回退从 GRUB 进备份分区,再把两块身份对调引导切回旧槽,旧槽没被这次安装碰过
磁盘成本系统分区几乎要两份一样大也要两份系统区;后来的虚拟 A/B 用快照把空槽变小