0%

Linux双系统分区升级方案设计

背景

早期,客户的On-premises网关设备(基于CentOS 7)采用增量升级方案进行固件更新。 这种方案存在两个主要问题:

  • 一旦升级过程中出现问题,客户只能重新安装系统,严重影响用户体验。
  • 部分客户未开启自动升级功能,为了支持所有客户的升级需求,升级包必须保存从初始版本到最新版本的所有增量文件。随着版本迭代,升级包变得越来越大,难以维护。

为了解决这些问题,需要为客户网关设备设计一种新的升级方案。

A/B系统升级简介

A/B分区升级机制允许设备在不同分区上安装和运行系统的不同版本,从而实现无缝更新。这种方法常用于移动设备、物联网设备以及其他需要高可用性和减少停机时间的场景中。例如,Android系统、Chrome OS及许多嵌入式和IoT设备都采用了A/B系统升级方法。其主要优点包括:

  • 无缝更新:可以在系统运行期间进行更新,唯一的宕机发生在设备重启到更新后分区时。
  • 故障恢复:如果升级失败,设备仍可工作在旧分区,并允许客户继续尝试升级。

方案

升级方案中包含以下关键环节:

  • 设计分区
  • 构建升级包
  • 同步系统配置
  • 配置GRUB启动项

设计分区

客户的设备只有一个虚拟硬盘,因此需要重新设计分区方案,划出两个系统分区,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+--------------------------------------------------+
| /dev/sda |
| +-------------------+---------------------------+|
| | sda1 | sda2 |
| | BIOS Boot Partition | Boot Partition |
| +-------------------+---------------------------+|
| +-----------------------------------------------+|
| | sda3 ||
| | LVM Physical Volume ||
| | +----------------+--------------+-------------+|
| | | VA-root | VA-back | VA-data | VA-image |
| | +----------------+--------------+-------------+|
| +-----------------------------------------------+|
+--------------------------------------------------+
  • 使用BIOS+GPT分区格式,以兼容现有客户。分区方案包括:BIOS Boot分区(sda1)、Boot分区(sda2)和LVM分区(sda3)。LVM分区便于后期扩容。
  • 在LVM分区上创建名为VA的逻辑卷组,并进一步创建四个逻辑卷:VA-root和VA-back作为两个系统分区,VA-data存储公共数据,VA-image存储镜像文件。

构建升级包

构建原理如下:

  • 通过ISO安装虚拟机, 导出虚拟机OVA文件, 解压OVA文件得到VMDK磁盘文件
  • 使用guestmount挂载VMDK文件,对整个根文件系统进行打包,注意打包时需保留UID(numeric-owner)和文件扩展属性(xattr),解包时同样需要声明UID和xattr。

同步系统配置

在升级过程中需要同步当前系统的配置到目标系统,以确保无缝升级。这些配置包括:

  • 网络配置, 比如网卡, 路由, hostname, NTP Server, DNS
  • LVM配置(/etc/lvm)
  • 用户口令(/etc/shadow), SSH密钥, 以确保升级后客户仍能通过密码或SSH登录。
  • 同步证书(/etc/pki/tls/certs)
  • 正确设置备用分区的/etc/fstab,确保两个系统分区的正确挂载路径。

配置GRUB启动项

双系统升级需要配置GRUB以正确引导两个系统分区。这里存在一个难点:目标系统的GRUB版本可能比当前系统更高,使用当前系统的GRUB引导两个系统分区可能存在兼容性问题。针对这种情况,我采取了以下做法:

  • 使用目标系统的/boot引导两个系统的内核。
  • 通过chroot方式配置并重装GRUB。
  • 如果GRUB配置失败,回滚到升级前的状态。

为什么不直接把目标系统的/boot放在LVM系统分区?

  • 兼容性问题, 传统BIOS和某些UEFI固件不支持直接从LVM卷启动
  • 复杂性增加, 故障恢复困难

为什么不划两个/boot分区?

  • 手动修改风险:客户需要手动调整磁盘分区,这不仅增加了操作难度,还可能导致数据丢失或分区表损坏。
  • 实现复杂性:理论上划分两个/boot分区是可行的,但实现起来较为复杂。实际上,GRUB的一个/boot分区就可以引导多个版本的内核,支持向前兼容。

优化

当目标系统GRUB版本与当前系统不一致时,直接覆盖/boot分区可能导致旧系统的引导文件丢失,进而导致启动故障。
实际测试中,没有遇到客户出现此类问题。如果要避免覆盖问题,可以使用EFI系统分区,为每个系统创建独立目录存放引导文件:

1
2
/boot/efi/EFI/systemA/
/boot/efi/EFI/systemB/

问题记录

1. 客户升级后admin用户无法登录

定位:后台查看发现admin用户的文件UID不正确,升级前后两个系统的用户配置不一致。同样的admin用户在两个系统上的UID不同。由于升级包是通过tar备份的,而tar解压文件的默认行为是根据当前系统用户名进行映射,而不是UID,因此升级后文件的UID不正确。

解决方法:在tar解压时添加–numeric-owner参数,保留文件的UID,解决了问题。

2. 升级到Rocky Linux 9.4后, 客户设备启动故障, 报错: 虚拟机CPU不支持x86_64_v2

定位:查看/proc/cpuinfo,发现客户虚拟机不支持x86_64_v2指令集,但物理服务器支持该指令集。

解决方法:指导客户开启CPU虚拟化扩展,并在升级前验证虚拟机CPU是否支持所需指令集。如果不支持,则直接上报失败。

3: 配置了双网卡的客户, 在升级到Rocky Linux 9.4后有概率出现网络不通, 网卡的IP,Gateway配置看上去是正确的

定位: 首先ping网关, 发现网关不可达, 通过ethtool查看网卡信息, 发现升级后eth0和eth1顺序反了, 导致网络不通

原因: 现代Linux系统通常使用基于硬件信息(如PCI总线ID)的可预测网卡命名规则,保证OS升级后网卡顺序也是正确的。 但是我们的网关设备采用的还是传统的eth命名规则,这种规则不能保证网卡顺序的正确性。

解决方法: 升级后用ethtool判断网卡顺序是否正确, 如果不正确, 用modprobe按正确顺序加载网卡驱动; (如果两个网卡驱动相同, 还需要修改udev规则)

参考

Android A/B(无缝)系统更新