openEuler KVM 部署与磁盘扩容实战

单网卡桥接、云镜像导入、大容量磁盘扩容与常见故障处理

本文记录 openEuler 24.03 LTS SP4 上使用 KVM 的实践:宿主机单网卡桥接、离线安装、导入 qcow2 镜像,以及虚拟磁盘扩容。示例中的网卡、地址、磁盘、分区和资源大小均需按实际环境调整;不同镜像的启动方式和磁盘布局可能不同。

网络切换和分区操作有中断服务或丢失数据的风险。开始前请确认带外管理或物理控制台可用,并完成可恢复的备份。

一、单网卡宿主机配置网桥

网桥模式下,宿主机 IP 配置在 br0,物理网卡作为网桥端口;虚拟机通过网桥接入同一二层网络。上游交换机或云平台必须允许同一端口出现多个 MAC 地址。远程操作前,务必准备好带外控制台;后台运行命令只能让命令在 SSH 断开后继续执行,不能保证网络恢复。

1. 记录现有网络配置

nmcli -f NAME,UUID,TYPE,DEVICE connection show --active
ip -br address
ip route
nmcli device show enp94s0f0

记录物理网卡、原连接名称、IP、网关、DNS、静态路由、MTU 和 VLAN 配置。下例假设原连接名为 原连接名,物理网卡为 enp94s0f0,使用静态 IPv4;不要直接照搬示例地址。

2. 创建并启用网桥

建议在控制台操作,并在维护窗口内切换。先确认 br0 和 br0-enp94s0f0 不是已有连接;保留原连接配置以便回退。

nmcli connection add type bridge con-name br0 ifname br0
nmcli connection modify br0 bridge.stp no \
  ipv4.method manual ipv4.addresses 192.0.2.10/24 \
  ipv4.gateway 192.0.2.1 ipv4.dns "192.0.2.53" \
  connection.autoconnect yes connection.autoconnect-slaves 1
nmcli connection add type bridge-slave con-name br0-enp94s0f0 \
  ifname enp94s0f0 master br0

# 切换时当前 SSH 可能断开;从控制台确认结果
nmcli connection up br0-enp94s0f0
nmcli connection up br0

示例关闭了 STP,仅适用于确认不会产生二层环路的网络;IPv6、额外路由、VLAN 和 MTU 需按照原环境单独配置。切换后从另一台机器测试 SSH、网关、DNS 和业务访问,并检查:

ip -br address
ip route
bridge link
nmcli connection show --active

确认新配置正常后,再禁用原连接的自动连接,避免重启时与网桥端口争用网卡:

nmcli connection modify "原连接名" connection.autoconnect no

若切换失败,通过控制台停用新连接并重新启用原连接:

nmcli connection down br0
nmcli connection down br0-enp94s0f0
nmcli connection modify "原连接名" connection.autoconnect yes
nmcli connection up "原连接名"

远程操作也可以研究 NetworkManager 的 checkpoint 回滚机制,但应先在测试环境验证其行为。

二、准备本地软件源和 KVM 环境

将与系统版本、架构匹配的 everything ISO 放在 /iso,并验证下载文件的校验值。下面的路径仅为示例:

mkdir -p /mnt/dvd /iso /kvm
mount -o loop,ro /iso/openEuler-24.03-LTS-SP4-everything-x86_64-dvd.iso /mnt/dvd
test -f /mnt/dvd/repodata/repomd.xml

如果 repodata 不在 ISO 根目录,应改用实际目录。临时挂载不会自动跨重启保留;需要持久挂载时,可配置 systemd mount 单元或在 /etc/fstab 使用 nofail,并测试 ISO 缺失时的启动行为。不要让可选安装介质阻塞宿主机启动。

cat > /etc/yum.repos.d/local-openeuler.repo <<'EOF'
[local-openeuler]
name=Local openEuler Repository
baseurl=file:///mnt/dvd
enabled=1
gpgcheck=1
EOF

dnf repolist
dnf makecache
dnf install -y qemu-kvm qemu-img libvirt libvirt-daemon \
  libvirt-daemon-driver-qemu virt-install libguestfs-tools cloud-utils-growpart
systemctl enable --now libvirtd
virsh list --all

请使用发行版提供且已验证的 GPG 公钥;如果本地 ISO 或仓库没有可用的签名元数据,应先核实来源及完整性,再按组织策略决定是否关闭签名校验。包名、服务名称以及镜像内的软件包可能因版本或仓库而异;安装失败时先检查 dnf repolist 和 ISO 仓库内容。

三、导入云镜像创建虚拟机

1. 准备镜像

下载匹配架构的 openEuler 虚拟机镜像并核对官方校验值。保留原始镜像,为每台虚拟机创建独立副本:

cd /kvm
xz -dk openEuler-24.03-LTS-SP4-x86_64.qcow2.xz
cp --reflink=auto openEuler-24.03-LTS-SP4-x86_64.qcow2 vm01.qcow2
qemu-img info vm01.qcow2

镜像默认账号、密码、串口和 cloud-init 配置以该版本镜像说明为准。若镜像提供默认密码,首次登录后立即修改;也可按镜像支持情况预先配置 cloud-init 和 SSH 公钥。--graphics none 不会自动为客户机配置串口登录,镜像需要启用相应的 serial console 和 getty。

2. 创建并连接

下面分配 32 vCPU 和 256 GiB 内存,须根据宿主机容量调整:

virt-install \
  --name vm01 \
  --memory 262144 \
  --vcpus 32 \
  --cpu host-passthrough \
  --disk path=/kvm/vm01.qcow2,format=qcow2,bus=virtio \
  --network bridge=br0,model=virtio \
  --graphics none \
  --console pty,target_type=serial \
  --osinfo detect=on,require=off \
  --import \
  --noautoconsole

virsh list --all
virsh console vm01

按回车唤起登录,退出控制台使用 Ctrl + ]。如果没有输出,检查镜像中的串口启动参数及 serial-getty。连接配置名称不一定等于设备名,配置客户机网络前先用 nmcli connection show 查看。

四、扩容超过 2 TiB 的虚拟磁盘

在常见的 512 字节逻辑扇区下,MBR 的可寻址空间约为 2 TiB。超过该限制时通常需要 GPT;但直接转换系统盘还会影响启动,不能把它视为通用的无损在线操作。优先考虑新增一块 GPT 数据盘,避免更改系统盘分区表。

1. 确认磁盘布局和备份

宿主机上确认磁盘映射、镜像及可用空间;客户机中确认分区表、根文件系统及启动方式:

# 宿主机
virsh domblklist vm01 --details
df -h /kvm

# 客户机
lsblk -f
findmnt /
parted -l
test -d /sys/firmware/efi && echo UEFI || echo BIOS

确认已经有可恢复备份,并检查 qcow2 稀疏文件所在存储的实际可用空间。下面仅以 /dev/vda2 直接承载 ext4 为例:如果使用 LVM、XFS、加密、其他分区号或分区后面还有其他分区,必须按实际布局选择扩容方案,不能直接套用。

2. 扩大虚拟磁盘

离线方式:正常关闭虚拟机,确认已关机,再扩大镜像;不要对正在使用的镜像运行 qemu-img resize。

# 宿主机
virsh shutdown vm01
virsh domstate vm01   # 等待状态为 shut off
qemu-img resize /kvm/vm01.qcow2 3500G
virsh start vm01

如需在线扩容,使用 libvirt 支持的 virsh blockresize,并先确认磁盘类型、版本和存储后端支持;扩容后仍需在客户机内识别新容量及扩展分区和文件系统。

3. 检查 GPT、引导和分区

如果系统盘已经是 GPT,且目标分区后有连续空闲空间,可跳过转换。如果是 MBR 系统盘,先在克隆镜像上演练转换、重启和恢复流程:BIOS + GPT 需要可用的 BIOS boot 分区;UEFI 需要有效的 EFI 系统分区。没有合适的空闲位置时,不要强行创建分区。确认布局和引导修复方案后,再安排维护窗口转换,例如:

# 客户机:仅在确认转换前提和备份后执行
dnf install -y gdisk cloud-utils-growpart
sgdisk --print /dev/vda
sgdisk -g /dev/vda
sgdisk --verify /dev/vda

转换后必须检查分区表和引导配置;如内核仍使用旧分区表,安排重启或进入救援环境,不要继续扩容。对于 GPT 磁盘在扩容后提示备用 GPT 表不在磁盘末尾的情况,可在确认设备无误后使用 sgdisk -e /dev/vda 修正并再次验证。

4. 扩展分区和文件系统

确认客户机已识别新磁盘容量、分区表已生效、目标分区后有连续空闲空间,并且 /dev/vda2 确实是直接挂载的 ext4 分区:

lsblk -f
findmnt /
growpart /dev/vda 2
resize2fs /dev/vda2
df -h /

若是 XFS,使用 xfs_growfs 扩展挂载点;若是 LVM,还需要扩展 PV、LV 和文件系统。具体步骤应根据 lsblk -f、findmnt 的结果制定。

五、故障处理

BIOS + GPT 转换后进入 grub>

启动失败可能与 BIOS boot 分区或 GRUB 安装有关,但也可能有其他原因。先确认虚拟机确实使用 BIOS 启动、系统盘是 GPT,并通过只读检查确认有可用的未分配空间。BIOS + GPT 引导需要 BIOS boot 分区;UEFI 则使用 EFI 系统分区,不适用相同修复步骤。

通过控制台或 ISO 救援环境启动,识别正确的系统盘并挂载系统。若确实缺少 BIOS boot 分区,在确认空闲位置后创建类型为 ef02 的分区,再按实际挂载布局进入系统并重装 GRUB。原记录中使用的命令如下,分区起止位置必须依据实际布局明确指定,不能盲目执行:

sgdisk --print /dev/vda
# 在确认分区布局和空闲空间后创建 BIOS boot 分区
sgdisk -a 1 --new=0:起始扇区:结束扇区 --typecode=0:ef02 /dev/vda

# 在正确挂载的系统 chroot 内,确认磁盘和 GRUB 路径后执行
grub2-install /dev/vda
grub2-mkconfig -o /boot/grub2/grub.cfg

重启前恢复磁盘优先启动,并按实际情况移除救援介质。若不能确定分区或引导状态,优先从备份恢复。

Docker 提示 AppArmor 配置无法加载

现象与排查结论

虚拟机运行 Docker 时,出现 AppArmor enabled on system but the docker-default profile could not be loaded: apparmor_parser: executable file not found。这次排查发现:

  1. /etc/default/grub 和 grubby --info 显示已配置 apparmor=0,但当前运行内核的 /proc/cmdline 中没有该参数。
  2. AppArmor 的 enabled 状态仍为 Y,Docker 安全选项中也有 AppArmor。
  3. 虚拟机实际通过 BIOS 启动,参数却生成到了 EFI 那份 GRUB 配置;BIOS 实际使用的 /boot/grub2/grub.cfg 没有新增参数。

也就是说,问题在于配置写入了与实际启动方式不匹配的文件,而不是仅凭配置文件或 grubby 的内容就能证明参数已生效。可用以下命令核对:

test -d /sys/firmware/efi && echo UEFI || echo BIOS
cat /etc/default/grub
grubby --info=ALL
cat /proc/cmdline
cat /sys/module/apparmor/parameters/enabled
docker info --format '{{json .SecurityOptions}}'
command -v apparmor_parser

本次修复方法

这台虚拟机使用 BIOS 启动,且 /etc/default/grub 已包含 apparmor=0。先备份,再将配置生成到实际使用的 BIOS 配置文件:

cp /boot/grub2/grub.cfg /root/grub.cfg.bak.$(date +%Y%m%d%H%M%S) &&
grub2-mkconfig -o /boot/grub2/grub.cfg

生成后检查实际启动项的内核参数包含 apparmor=0,再安排重启。不同系统可能使用 BLS 启动项或 grubenv 保存参数,不能只以 grub.cfg 中是否直接出现字符串作为唯一判断;应结合 grubby --info=ALL、实际启动项以及发行版文档检查。对于 UEFI 或其他发行版,先确认实际使用的配置路径,不要直接套用本例。

重启后依次检查:

cat /proc/cmdline
cat /sys/module/apparmor/parameters/enabled
docker info --format '{{json .SecurityOptions}}'

验证时检查 /proc/cmdline 包含 apparmor=0、AppArmor 状态为 N,以及 Docker 安全选项不再包含 name=apparmor,然后重新尝试启动容器。本次问题已经修复。

关闭 AppArmor 会降低安全隔离;这是一项针对该环境的选择,并非通用修复。其他环境应先排查发行版的软件包、Docker 版本及 AppArmor 配置,确认影响和回滚方案后再调整安全机制。

参考资料