记录一次修复 init 脚本的经历
· 4 min read
问题产生过程
由于定制化过 /usr/share/mkinitfs/initramfs-init
脚本,在升级大版本后,该文件被覆盖,导致生成的 initramfs 无法启动系统。
正常情况升级 /etc
下会产生 .apk-new
后缀文件避免升级覆盖,但由于是 /usr/share
下文件,因此导致覆盖。
难点
- 系统使用静态 IP
- 需要提供相同 IP 段地址才能进行 SSH
- 假设 IP 为 192.168.66.99/22
- 系统在硬盘上 - M2
- 无法直接在其他系统上进行修复
- 通过提供 U 盘 系统在原地恢复
- root 盘有 luks 加密
- 依赖硬件环境
- QEMU 启动无法模拟相同环境
- 需要手动输入密钥进行挂载
- root 密码为 UUID
- 极其难输入
- 且需要输入多次,因此选择网络打通通过 SSH 登陆
操作过程
准备虚拟机和环境
# QEMU 虚拟机
apk add qemu-system-x86_64
# 桥接模拟网络环境 - 使用相同 IP 段访问
ip li add vmbr0 type bridge
ip li set vmbr0 up
# 桥接以便于访问 192.168.66.99/22
ip addr add 192.168.66.1/22 dev vmbr0
# 允许 qemu 桥接网卡
echo 'allow vmbr0' >> /etc/qemu/bridge.conf
启动系统
# 提供 vnc 方便查看
# -curses 方便输入 luks 密钥
# 桥接 vmbr0
qemu-system-x86_64 -accel kvm -m 2G \
-vnc 0.0.0.0:1 /dev/sda \
-netdev bridge,br=vmbr0,id=n1 -device virtio-net,netdev=n1 \
-curses
启动后无法进入系统,/sysroot 挂载失败,出现修复 shell
mount: mounting /dev/sda2 on /sysroot failed: Invalid argument
Mounting root failed.
initramfs emergency recovery shell launched. Type 'exit' to continue boot
sh: can't access tty; job control turned off
/ #
此时手动挂载 /sysroot
cryptsetup open /dev/sda2 cryptroot
# 粘贴密钥
# 挂载 sysroot
mount /dev/mapper/cryptroot /sysroot
# 退出 shell 正常进入系统
exit
修复 init 脚本
# 将准备好的 init 脚本直接覆盖
rsync --rsync-path='sudo rsync' --no-owner initramfs-init [email protected]:/usr/share/mkinitfs/initramfs-init
# 进入虚拟机
ssh [email protected]
# 从新生成 initramfs
mkinitfs
# 为安全起见,验证脚本正确
mkdir -p /tmp/init
cd /tmp/init
# 解压到当前目录
zcat < /boot/initramfs-lts | cpio -idmv
# 确保是正确的脚本
cat init
避免再次出现问题
/etc 下配置不会被覆盖,lbu 还能备份,因此将 init 脚本放到 etc,修改 mkinitfs 配置指向期望的脚本。
mkinitfs 的配置文件位于 /etc/mkinitfs/mkinitfs.conf
, 默认配置 features, 该文件会被脚本直接 source (mkinitfs.in#L237), 从脚本可看出配置变量为 init.
cp /usr/share/mkinitfs/initramfs-init /etc/mkinitfs/initramfs-init
echo "init=/etc/mkinitfs/initramfs-init" >> /etc/mkinitfs/mkinitfs.conf
总结
不要修改 /usr/share
下的默认配置,将配置放到 /etc
下,安全可靠,还可以使用 lbu 备份。