抢救 VPS 服务器

2023-05-23 ⏳4.2分钟(1.7千字)

昨天晚上例行更新 VPS,更新后重启,重启后失联了😂博客、梯子、ChatGPT全部失效。今天早上折腾了一番终于给抢救回来了。记录一下抢救过程,供大家参考。

我的系统是 Ubuntu 22.04,原则上用新不用旧,所以我会时常运行 apt-get update/upgrade。因为更新比较频繁,所以每次有更新的时候需要更新的包都比较少。但昨天有异常,系统提示有一大片包需要变更1没仔细看就按下了回车。因为有新版内核,所以我习惯性重启系统。然后就 ping 不通了。

首先我怀疑是服务商的问题,因为 Ubuntu 也算比较稳定的发行版,之前没碰到过升级系统出问题的情况。而且已经是半夜,我索性给服务商提了工单然后就睡觉了,等一手。

早上醒来发现问题依然存在。而且白天工作确实依赖梯子和 ChatGPT,所以等不及官方处理了。

我先在后台尝试使用 VNC Console,但发现 root 密码忘记了。也可能是没有设置。平时我都用 SSH 密钥登录,个人用户使用 sudo 也不需要输入密码,所以从来没有设置过 root 密码。

好在管理后台提供重置密码的功能。我重置了几次,都不生效2。于是我取消原来的工单,新提一个紧急工单,问为什么不能改 root 密码。但远水不解近渴,不能干等着。

后台无法重置那就想办法在 VPN 终端里重置。

首先在终端内重启系统(按 ctrl+alt+del),然后不停按 Shift 键,系统就会显示 GRUB 引导菜单。这个时候输入字母 e 就会打开类似 Nano 的编辑器,可以个性 GRUB 的启动配置。

找到启动 Linux 内核的配置(以 linux /boot/ 开头的那行)并在最后面追加 init=/bin/bash3。再按ctrl+x或者F10完成启动,系统会直接打开 bash 界面。

这个时候根文件系统处理只读模式,需要重新挂载为读写模式:

mount -o remount,rw /

最后运行passwd命令修改 root 用户密码。完成后重启系统就能登录了。

理论很丰满,现实很骨感。我在自己的 VPS 试了很多次,都无法正常进入 bash 界面,每次都提示登录。感觉像是前面的修改没有生效。

仔细观察 GRUB 引导配置发现它有两个分支:

# ...
if [ "${initrdfail}" = 1 ]; then
  echo  'GRUB_FORCE_PARTUUID set, initrdless boot failed. Attempting with initrd.'
  linux /boot/vmlinuz-5.19.0-42-generic root=PARTUUID=XXX ro net.ifnames=0 biosdevname=0 console=tty1 console=ttyS0
  initrd /boot/initrd.img-5.19.0-42-generic
else
  echo  'GRUB_FORCE_PARTUUID set, attempting initrdless boot.'
  linux /boot/vmlinuz-5.19.0-42-generic root=PARTUUID=XXX ro net.ifnames=0 biosdevname=0 console=tty1 console=ttyS0 panic=-1
fi
initrdfail

我只改了一个分支,可能导致不成功。两个分支都改了,还是不行。再仔细看,发现 VPS 每次启动的时候都会闪两次启动画面,应该是启动了两次。再看上面的引导配置,不难发现问题。

原来是 GRUB 会先尝试以不加载 initrd 镜像的方式直接启动一次,也就是 else 分支,如果不成功能再才加载。我们改的配置只在第一次启动生效,到第二次的时候还是用原来的配置引导。

所以我强行把 if 分支的 "${initrdfail}" 改为 "1",让 GRUB 直接直第一个分支。这次就打开 bash 界面了✌️

重置密码后重新启动,顺利登录系统。查看网络状态发现连ip这个命令都没有了。应该是昨天升级的过程中删除了。又试了一下,发现ifconfig命令还在,谢天谢地。

执行ifconfig查看网络配置,只有一个回环网卡,压根就没有 eth0!我瞬间就慌了。不会是新内核不支持虚拟机的网卡或者升级的过程将驱动卸载了吧。如果真没有网,不但不能从外界下载数据,就连虚拟机内的数据也无法备份。

我首先怀疑可能是内核版本的问题。于是重启系统,使用老版内核启动,发现问题依旧。然后尝试查看 dmesg 日志,发现一个内核报错:

[    4.245199] unchecked MSR access error: RDMSR from 0xda0 at rIP: 0xffffffffb7c9f598 (native_read_msr+0x8/0x50)
[    4.245218] Call Trace:
[    4.245220]  <TASK>
[    4.245222]  kvm_arch_hardware_setup+0x1d7/0x240 [kvm]
[    4.245252]  kvm_init+0xb0/0x420 [kvm]
[    4.245272]  ? svm_hardware_setup+0x78a/0x78a [kvm_amd]
[    4.245279]  svm_init+0x26/0x34 [kvm_amd]
[    4.245283]  do_one_initcall+0x49/0x230
[    4.245289]  ? kmem_cache_alloc_trace+0x1a6/0x330
[    4.245294]  do_init_module+0x52/0x220
[    4.245299]  load_module+0xb56/0xd40
[    4.245301]  ? security_kernel_post_read_file+0x5c/0x80
[    4.245306]  ? kernel_read_file+0x245/0x2a0
[    4.245311]  __do_sys_finit_module+0xcc/0x150
[    4.245312]  ? __do_sys_finit_module+0xcc/0x150
[    4.245314]  __x64_sys_finit_module+0x18/0x30
[    4.245315]  do_syscall_64+0x5c/0x90
[    4.245325]  ? do_syscall_64+0x69/0x90
[    4.245326]  entry_SYSCALL_64_after_hwframe+0x63/0xcd
[    4.245333] RIP: 0033:0x7f6ae0d1ea3d

但我用老内核也有这个报错,感觉问题不在这里。 于是我有查看设备信息:

lspci |grep -i ETH
06:12.0 Ethernet controller: Red Hat, Inc. Virtio network device

发现系统能识别虚拟机网卡,问题不大。

ifconfig 没展示网卡信息可能是因为没有激活。使用ifconfig -a尝试查看所有网卡,确实有输出 eth0。

这就好说了,我用 ifconfig 激活网卡并手工设置 IP 地址:

ifconfig eth0 up
ifconfig eth0 XXX.XXX.XXX.XX
ifconfig eth0 netmask 255.255.255.0
route add default gw XXX.XXX.XXX.XXX

让我一个常年使用 iproute2 的用户输入 ifconfig/route 命令直是煎熬😩但比没有强。

设置完成后就有网了,ping 8.8.8.8 通了。

然后尝试重新安装 iproute2 包,提示无法访问。这肯定是 DNS 解析失败了。手工修改 /etc/resolv.conf 文件添加:

nameserver 8.8.8.8

现在就可以正常安装了😄装好 iproute2 后重启系统,发现问题没有解决。可能还有别的包没有装。我印象中 VPS 都需要安装 cloud-init 组合。我的 VPS 管理后台中重置密码的页面也有如下提示:

Attention needed

To modify the settings in this section, it is necessary to properly install and run the Cloud-Init component within the instance.

于是我尝试执行apt-get install cloud-init,还真有这个包。装好后重启,一切恢复正常了㊗️

以上就是整个抢救过程,希望能帮到大家。


  1. 其实有一部分是删除😂↩︎

  2. 其实是因为咋晚的更新误删了 cloud-init 组件,下文会细讲。↩︎

  3. init 参数指定内核完成启动后运行的第一个程序。这就是所谓的超级进程,是系统中所有进程的父进程。现在常见的发行版默认会启动 systemd。但普通的 init 进程在登录时会要求用户提供用户名和密码。这里让内核直接运行 bash 命令,启动后就可以直接修改 root 密码了。所以说为了安全起见,最好给 GRUB 再设个密码🪆↩︎