Arch Linux KVM 镜像离线初始化指南

适用范围:本文用于离线修改一份未运行的 Arch Linux KVM 镜像。先备份镜像,并根据实际镜像格式、分区和文件系统替换示例值。

重要限制:virt-customize 和 qemu-nbd 是两种不同的修改方式。使用 virt-customize 时不要同时挂载同一镜像;使用 qemu-nbd 挂载后,应直接编辑挂载目录,不要再对该镜像执行 virt-customize。

方案 B:使用 qemu-nbd 挂载镜像

使用 qemu-nbd 连接镜像

加载模块:

sudo modprobe nbd max_part=8

连接:

sudo qemu-nbd \
  --connect=/dev/nbd0 \
  /var/lib/libvirt/images/arch

查看磁盘分区

lsblk /dev/nbd0

示例:

NAME      SIZE TYPE
nbd0       2G disk
├─nbd0p1   1M part
├─nbd0p2 300M part
└─nbd0p3 1.7G part

确认根分区

查看:

blkid /dev/nbd0p*

通常:

分区用途
nbd0p1GPT 保留
nbd0p2EFI
nbd0p3Linux 根分区

挂载:

sudo mount /dev/nbd0p3 /mnt

如果有 EFI:

sudo mkdir -p /mnt/boot
sudo mount /dev/nbd0p2 /mnt/boot

方案 A:使用 virt-customize 设置 root 密码

推荐使用 virt-customize,但执行时镜像必须未被挂载、未被虚拟机使用。不要把真实密码写入命令历史或文章。

原文命令(保留,仅供对照;不要在实际系统中使用固定弱密码):

sudo virt-customize \
  -a /var/lib/libvirt/images/arch \
  --root-password password:12

补充:实际执行时,将 IMAGE_FILE 替换为实际镜像文件名,并使用受保护的密码文件:

sudo virt-customize \
  -a /var/lib/libvirt/images/IMAGE_FILE \
  --root-password file:/path/to/protected-password-file

密码文件应只允许当前管理员读取,并在操作完成后删除。不要使用示例中的固定弱密码。


方案 A:使用 virt-customize 配置 systemd-networkd DHCP

创建 networkd 配置文件

如果前面已经使用 qemu-nbd 挂载镜像,请先卸载并断开镜像,再执行下面的 virt-customize 命令;不要让两种修改方式同时操作同一镜像。

文件路径:

/etc/systemd/network/20-wired.network

内容:

[Match]
Type=ether

[Network]
DHCP=yes

不绑定网卡名称,自动匹配所有以太网设备,适合 KVM/libvirt 虚拟机。

写入镜像

创建临时文件:

cat >/tmp/20-wired.network <<EOF
[Match]
Type=ether

[Network]
DHCP=yes
EOF

写入:

sudo virt-customize \
  -a /var/lib/libvirt/images/arch \
  --write \
  /tmp/20-wired.network:/etc/systemd/network/20-wired.network

启用 systemd-networkd

sudo virt-customize \
  -a /var/lib/libvirt/images/arch \
  --run-command \
  'systemctl enable systemd-networkd'

启用 DNS:

sudo virt-customize \
  -a /var/lib/libvirt/images/arch \
  --run-command \
  'systemctl enable systemd-resolved'

修复 resolv.conf

sudo virt-customize \
  -a /var/lib/libvirt/images/arch \
  --run-command \
  'rm -f /etc/resolv.conf' \
  --run-command \
  'ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf'

方案 B:进入挂载的系统环境

sudo arch-chroot /mnt

修复 pacman keyring

遇到的问题

安装软件:

pacman -S fish

出现:

warning: Public keyring not found; have you run 'pacman-key --init'?
error: keyring is not writable
error: required key missing from keyring

原因

  • 镜像未初始化 pacman keyring
  • keyring 文件损坏
  • 镜像过旧

修复

初始化:

pacman-key --init

导入官方密钥:

pacman-key --populate archlinux

更新 keyring(保留原文命令):

pacman -Sy archlinux-keyring

补充:正常升级时使用完整升级命令:

pacman -Syu archlinux-keyring

不要长期执行部分升级。若旧镜像的密钥环已经无法验证仓库,先在隔离环境中确认当前 Arch 的密钥环恢复流程,再继续升级。


升级系统并安装软件

更新系统:

pacman -Syu

安装软件,例如 fish:

pacman -S fish

locale 警告处理

进入 chroot 时可能出现:

bash: warning: setlocale: LC_ALL: cannot change locale (zh_CN.UTF-8)

原因:locale 未生成。

修复:编辑 /etc/locale.gen,取消注释:

#zh_CN.UTF-8 UTF-8

改为:

zh_CN.UTF-8 UTF-8

生成 locale:

locale-gen

卸载镜像

退出 chroot:

exit

卸载文件系统:

sudo umount -R /mnt

如果卸载失败,先用 mount、lsof 或 fuser 找出仍在使用 /mnt 的进程并停止它。除非确认不会再访问镜像,否则不要直接使用懒卸载:

sudo umount -l /mnt

断开 nbd:

sudo qemu-nbd \
  --disconnect \
  /dev/nbd0

卸载 nbd 模块(可选):

sudo modprobe -r nbd

检查确认 nbd0 已消失:

lsblk

遇到的问题总结

问题原因解决
guestmount 下 pacman 下载失败FUSE 写入异常改用 qemu-nbd
mount /dev/nbd0pX 失败X 是占位符使用真实分区
pacman keyring 不可写keyring 未初始化pacman-key –init
locale 警告locale 未生成locale-gen
虚拟机无网络没有 networkd 配置systemd-networkd DHCP
root 无密码镜像未初始化virt-customize 设置密码

推荐初始化顺序

方案 A:使用 virt-customize

  1. 备份镜像,并确认虚拟机没有使用该镜像。
  2. 在镜像未挂载的状态下,用 virt-customize 设置密码和写入网络配置。
  3. 退出 virt-customize 后,才可以选择使用 qemu-nbd 挂载镜像进行检查,或直接启动虚拟机验证。

方案 B:使用 qemu-nbd 和 arch-chroot

  1. 备份镜像。
  2. 使用 qemu-nbd 连接并确认实际分区。
  3. 挂载根分区和 EFI 分区,直接编辑配置文件。
  4. 进入 arch-chroot,初始化 pacman-key 并执行完整系统升级。
  5. 安装所需软件,退出 chroot,卸载所有分区后断开 NBD。

原文推荐顺序(保留):

  1. qemu-nbd 挂载镜像
  2. virt-customize:设置 root 密码、写入 systemd-networkd 配置、enable networkd/resolved
  3. arch-chroot
  4. pacman-key 初始化
  5. pacman -Syu
  6. 安装软件
  7. 卸载镜像

上述顺序属于原文记录。由于 qemu-nbd 和 virt-customize 不能同时操作已挂载镜像,实际执行时必须选择上面的方案 A 或方案 B,不能照此顺序混用。

两种方案不要同时操作同一份已挂载镜像。该流程适合制作 KVM/libvirt Arch Linux 基础镜像。