修复因存在多个内核版本导致的 ABB Agent 安装失败

  • ~9.95K 字
  • 次阅读
  • 条评论
  1. 1. 准备工作
  2. 2. 问题的累积
  3. 3. 错误出现
  4. 4. 分析与研究
  5. 5. 解决方案
  6. 6. 鸣谢
  7. 7. 版权提示

Debian 13 的正式发布意味着 12 进入了 old-stable 的维护阶段,加上 NVIDIA 的 CUDA 从 13.0 开始不再为 Pascal 架构的 GPU 提供支持,我想对于我的 GPU 服务器来说,是时候为它保留一份最终的全盘备份了。

准备工作

因为我的主力 NAS 是群晖的 DS920+ ,它作为一个功能丰富的成熟产品来说有相当高的可玩性,加上它自带的无需额外购买授权的 Active Backup for Business (ABB) 备份服务非常好用,所以我需要全盘备份的设备就都使用的是这套解决方案。

我的服务器上安装的操作系统是 Debian 12 :我不喜欢 Ubuntu 的臃肿(以及自带的 snap 这种脏东西),不想承担 RHEL 的昂贵( CentOS 则是直接查无此人),也担心其他并没有那么 production-ready 的发行版可能出现的过于激进的策略可能导致的不稳定问题; Debian 虽然有上古老包不更新的毛病,但它也因此变得相对稳定,加上因为非常知名所以各种软件的兼容性也挺优秀,我所有的服务器使用的都是这个系统。

问题的累积

其实早在服务器的初始安装之时起,我就有为它安装过 ABB Agent 并执行过全盘备份。

在 Windows 上工作的 ABB Agent 勤勤恳恳,为我的主力机提供每天稳定的快速的增量备份,让我基本不用担心可能出现的因为硬盘暴毙导致数据灰飞烟灭的情况,也因此直接间接地拯救了一些因为我手残误操作导致出现的被意外删除的文件。

但在 Debian 上,它有些不一样。或许是因为它是一个块级别的备份工具,所以它是直接工作在系统的内核驱动上的——安装时它需要使用 dkms 工具来编译与内核相关的模块,并且在升级内核时会出现需要人为介入的情况:它需要先手动卸载,再升级内核,再在重启服务器到新的内核之后重新安装(之后就会知道它为什么这样了)。这意味着每一次的内核升级都需要伴随着 卸载->更新->重启->重新安装 的过程。更令人尴尬的是,它在升级内核之后需要执行一次全量备份,这对于一般的服务器来说没什么——一直开着也就开着了;但我的 GPU 服务器因为本身架构比较老旧,空载状态下就有 600W+ 的恐怖功耗,因此我是希望它能尽量避免浪费能源。至于为什么全盘备份会耗时很久,因为 DS920+ 上唯二的端口都是千兆电口,单线程哪怕把速度拉满也就只有到千兆了。

往常,我都是要等它明确提示说出现问题需要介入的时候,才会懒洋洋地抬起眼皮,慢吞吞地去敲执行这个称不上是复杂但也不算简单的流程所需的指令。而这一次,我决定先下手为强——先把它卸载了,在升级完成之后再直接在新的系统上编译,那岂不是根本就不会遇到失败问题,省事多了?

很可惜,也就是我这种自作聪明的操作,成了压倒骆驼的最后一根稻草。

错误出现

这个错误最显著的特征,就是在安装 ABB Agent 所需的 synosnap 驱动时很快抛出错误:

安装失败,抛出错误

查阅其中提到的日志文件(此处是 /var/lib/dkms/synosnap/0.11.6/build/make.log ),可以看到形如这样的内容:

错误日志

无论清理或重启多少次,错误都不会改变,就在那里稳定地等我出现。

其实我早在几个月前的一次升级时候就遇到了这个错误。当时的我没有深入分析,只是觉得既然之前用着都好好的,这次出错了那可能只是什么升级不兼容导致的( Arch 的后遗症来了)。反正我有一份可用的全盘备份,等过几个月再试试也不迟。就这样,直到在我这次再遇到相同的问题,在几番搜索无果后,我选择开启了一个技术支持工单。

分析与研究

分析的一开始我就走了不少弯路,也因此错怪了一些组件。

我最先怀疑的是 Debian 的内核是否存在什么过大的改动导致无法兼容。System.map ,光是听名字就知道,这是个和系统内核相关的东西,如果内核出问题了,那编译不过是很正常的情况。但几个月后的再一次升级还是相同的问题,加上群晖并没有推送针对 ABB Agent 的更新,全网也找不到什么和它相关的资料,我就开始怀疑或许并不是内核导致的错误。

也因此,我将目光转移到了 ABB Agent 所需的依赖组件上去。根据最后一次备份成功的 /var/lib/dpkg/status 文件与当前系统的这个文件交叉比对, gcc 没问题, make 没问题,好巧不巧的是 dkms 撞到了枪口上——上一次备份成功的 dkms 版本是 3.0.10-8+deb12u1 ,Section 是 kernel ;当前的版本它变成了 3.2.1-2 , Section 变成了 NVIDIA 。 NVIDIA 为什么会接管它,会不会是这个过于激进的版本更新导致出现了意料之外的兼容问题?再加上 Deprecated feature: CLEAN 这一行的提示,让它一度成为我心目中嫌疑最大的存在。

因为不敢轻易改动 CUDA 和显卡驱动相关的包(有过盲目升级把生产环境搞崩溃的历史教训),我决定先去翻阅 dkms 的发布日志。这是一个开源项目,可以在 GitHub 上找到它的信息。 3.2.1 比起 3.0.10 确实有些许项目结构上的变更,但我似乎并没有看到值得高亮标记的破坏性改动。这么看起来, NVIDIA 的接管似乎只是为了方便他们的驱动使用较新的特性,应该并不涉及与底层相关的改动。

当排除掉一切可能的原因之后,剩下的哪怕再荒谬,也是唯一的正解了。那就是,我的系统出问题了。但是,为什么呢?

我开始从头再仔细阅读整个安装日志。这一次,一些不一样的东西吸引了我的注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Verifying archive integrity...  100%   MD5 checksums are OK. All good.
Uncompressing Active Backup for Business Agent 100%
* current installing folder is /tmp/selfgz2612
Hit:1 https://mirrors.tuna.tsinghua.edu.cn/debian bookworm InRelease
Get:2 https://mirrors.tuna.tsinghua.edu.cn/debian bookworm-updates InRelease [55.4 kB]
Hit:3 https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security InRelease
Hit:4 https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian bookworm InRelease
Hit:5 https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64 InRelease
Fetched 55.4 kB in 2s (36.7 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
47 packages can be upgraded. Run 'apt list --upgradable' to see them.
* start checking dependency of (3)
* checking linux-headers-6.1.0-38-amd64
* linux-headers-6.1.0-38-amd64 has already installed
* checking dkms
* dkms has already installed
* checking make
* make has already installed
* start installing snapshot driver and agent service
* snapshot driver has not installed, installing snapshot driver
* installing synosnap-0.11.6.deb
Selecting previously unselected package synosnap.
(Reading database ... 98146 files and directories currently installed.)
Preparing to unpack synosnap-0.11.6.deb ...
Unpacking synosnap (0.11.6) ...
Setting up synosnap (0.11.6) ...
Loading new synosnap/0.11.6 DKMS files...
Deprecated feature: CLEAN (/usr/src/synosnap-0.11.6/dkms.conf)
Building for 6.1.0-37-amd64 and 6.1.0-38-amd64

Building initial module synosnap/0.11.6 for 6.1.0-37-amd64
Deprecated feature: CLEAN (/var/lib/dkms/synosnap/0.11.6/source/dkms.conf)
Sign command: /usr/lib/linux-kbuild-6.1/scripts/sign-file
Signing key: /var/lib/dkms/mok.key
Public certificate (MOK): /var/lib/dkms/mok.pub

Running the pre_build script...(bad exit status: 1)
Failed command:
cd /var/lib/dkms/synosnap/0.11.6/build/ && /var/lib/dkms/synosnap/0.11.6/build/genconfig.sh 6.1.0-37-amd64
Consult /var/lib/dkms/synosnap/0.11.6/build/make.log for more information.
dpkg: error processing package synosnap (--install):
installed synosnap package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
synosnap
* failed to install snapshot driver
* Please review the highlighted information above.
**********************************************************************
* Please check if your distro / kernel version is supported in detail at
* https://sy.to/abb_requireandlimit
* or the "System requirement" part in README.md
**********************************************************************

我的系统使用的内核版本是 6.1.0-38-amd64 ,而它却先在尝试为 6.1.0-37-amd64 进行构建。这是谁,从哪里来,要到哪里去?

好在这个问题不难回答。系统中保留有旧的内核,那一定有它的深意。进行一番简单的搜索,不难发现这确实是意料之中的行为—— Debian 会保留一个旧版本的内核,以便在新内核出现问题时可以回滚到旧版本继续使用。

在通常的时候,这种方案非常合理,它不但考虑到了可能出现的兼容性问题的临时应对方案,并且兼顾了使用的便利。

然而,当我们打开 genconfig.sh 这个脚本文件(此处仅摘录相关的代码段,并不是完整的文件),配合简洁而干练的注释内容阅读,我们就会发现群晖的工程师为了方便用户使用而做出的妥协,以及造成这个问题的根源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
KERNEL_VERSION=$(uname -r)

if [ ! -z "$1" ]; then
KERNEL_VERSION="$1"
fi

SYSTEM_MAP_FILE="/lib/modules/${KERNEL_VERSION}/System.map"

# Use standard location at the /boot
[ ! -f "$SYSTEM_MAP_FILE" ] && SYSTEM_MAP_FILE="/boot/System.map-${KERNEL_VERSION}"
if [ ! -f "$SYSTEM_MAP_FILE" ] || [ $(cat "$SYSTEM_MAP_FILE" | wc -l) -lt 10 ]; then
# The build is running on Debian 11+. File /boot/System.map-${KERNEL_VERSION} exists, but it
# contains just a single line.
# Maybe package linux-image-$(uname -r)-dbg is installed...
SYSTEM_MAP_FILE="/usr/lib/debug/boot/System.map-${KERNEL_VERSION}"

if [ ! -f "$SYSTEM_MAP_FILE" ]
then
# Detect if runing kernel version and `uname -r` are different, which meaning an upgrade is on-going
RUNNING_KERNEL_VERSEION="$(cat /proc/version | sed 's|^Linux\ version\ \([^ ]\+\)\ .*|\1|g')"
if [ "${RUNNING_KERNEL_VERSEION}" != "${KERNEL_VERSION}" ]
then
# No valid SystemMap found and /proc/kallsyms not usable becuase it's upgrade, so we make build fail
echo_highlight "* System map at /boot/System.map-${KERNEL_VERSION} does not contain valid symbol table, and "
echo_highlight "* we are trying to install synosnap driver for a differnt kernel than the one currently running."
echo_highlight "* Synosnap driver will encounter error during installation."
echo_highlight "* We will try to install/build driver when next booting into new kernel."
echo_highlight "* After rebooting into the new kernel, if the backup process still fails,"
echo_highlight "* please uninstall and reinstall Active Backup for Business Linux Agent."

exit 1
fi
fi

# For non-upgrading case, we can use /proc/kallsyms for target kernel systemmap because it's the same as current one
# Use fallback option
[ ! -f "$SYSTEM_MAP_FILE" ] && SYSTEM_MAP_FILE="/proc/kallsyms"
fi

结合上文中详细的构建日志:

/var/lib/dkms/synosnap/0.11.6/build/make.log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DKMS (dkms-3.2.1) make.log for synosnap/0.11.6 for kernel 6.1.0-37-amd64 (x86_64)
Tue Aug 19 05:04:34 PM CST 2025

Running the pre_build script
# command: cd /var/lib/dkms/synosnap/0.11.6/build/ && /var/lib/dkms/synosnap/0.11.6/build/genconfig.sh 6.1.0-37-amd64
* System map at /boot/System.map-6.1.0-37-amd64 does not contain valid symbol table, and 
* we are trying to install synosnap driver for a differnt kernel than the one currently running.
* Synosnap driver will encounter error during installation.
* We will try to install/build driver when next booting into new kernel.
* After rebooting into the new kernel, if the backup process still fails,
* please uninstall and reinstall Active Backup for Business Linux Agent.

# exit code: 1
# elapsed time: 00:00:00
----------------------------------------------------------------

不难发现,这一大段话就是摘录出的这段代码里的这一大段提示文本。也因此,顺着这一大段往上倒推,看看它发生了什么:

  1. 使用 genconfig.sh 6.1.0-37-amd64 调用时,传入了一个内核的版本号参数。这个参数会替换掉 uname -r 的结果。
  2. 对于 Debian 12 来说,应该是为了缩减不必要的体积占用,它的 /boot/System.map-* 文件并不是一个完整的符号表,而是一个只有一行的提示文件:
    1
    ffffffffffffffff B The real System.map is in the linux-image-<version>-dbg package
  3. 因此,这个脚本会尝试寻找是否存在 /usr/lib/debug/boot/System.map-6.1.0-37-amd64 这个真正的符号表文件。这个文件的存在依赖 linux-image-*-dbg 这个包(也正是前一条中提到的包),在没有安装这个包的情况下,自然也就找不到它。
  4. 最后的希望是当前的运行中的系统内核,这样的话它就可以直接从 /proc/kallsyms 中获取符号表了。很可惜的是,当前系统运行的内核是 6.1.0-38-amd64 ,没法提供所需的 6.1.0-37-amd64 的内容。

也因此,脚本给出了一大串提示,然后通过 exit 1 中止了整个执行流程。

解决方案

既然问题的根源是旧内核没有可用的符号表,那我们补上它就好了。也因此,我想到了三种解决方案:

  1. 安装 linux-image-*-dbg 包。这个包非常庞大(我这边有 5GB+ ),并且它只是为了构建一次使用,实际运行时候用不到它,有点高射炮打蚊子的感觉。
  2. 回滚到旧版本的内核先构建旧版本的配置文件,再回到新版本内核构建新版本的配置文件。这样太麻烦了。
  3. 直接卸载旧版本的内核。这样最方便并且不需要重启,但缺点是如果新内核出现任何问题,就失去了快速回滚的可能性。

最终,因为偷懒,我选择了方案 3 ,直接卸载了 6.1.0-37-amd64 版本的内核。在此之后再安装 ABB Agent 以及执行全盘备份,一切就都变得非常顺利,完全不存在任何问题了。如果是生产环境里,我会选择方案 1 ,因为它不会破坏回滚的机制,同时在安装构建完成后也可以再卸载掉来回收空间。

如果以后再遇到需要升级内核的情况,我想还是依旧沿袭旧的升级方案吧,至少它不会出现这个问题,也能保留回滚的可能性,那就再好不过了。

同时,我也怀疑我是不是在上一次升级的时候使用了诸如 dpkg --prune 之类会把生成的配置文件一起清理掉的命令,否则只是使用 dpkg -r 的话,不应该会清理掉旧内核的配置文件才对。不过因为已经是几个月前的事了,我也记不太清楚,也就懒得深究了。

鸣谢

非常感谢群晖的技术工程师提供的帮助。与我对接的这位工程师非常耐心地和我交流,及时回应我的更新,帮助我收集信息确定具体情况,一步步讨论与分析可能的原因和可能的解决方案,并在我解决问题之后和工程师团队一起确认了这个罕见的边缘案例。

版权提示

文中提到的 genconfig.sh 文件版权归群晖公司所有,此处仅作分析展示使用。

分享这一刻
让朋友们也来瞅瞅!