第 24/30 天(重访进阶版)
前言
在上一篇 KVM 虚拟化入门文章中,我们完成了从零搭建虚拟化环境、创建虚拟机的基本操作。但在生产环境中,仅仅能跑虚拟机远远不够——我们需要 GPU 透传让虚拟机拥有图形计算能力、需要实时迁移实现零停机维护、需要精细化 CPU 和内存调优榨干硬件性能。
本篇文章将带你进入 KVM 虚拟化的高阶世界,覆盖生产环境中最关键的六大主题:GPU 透传(VFIO)、实时迁移、存储池深度管理、CPU/内存性能调优、自动化部署(Cloud-Init + Terraform)以及监控告警。每一部分都配有完整的命令示例和踩坑经验。
一、GPU 透传(VFIO Passthrough):让虚拟机拥有原生图形性能
1.1 原理概述
GPU 透传的核心思想是将物理 GPU 设备直接分配给某个虚拟机,虚拟机内的驱动直接与硬件通信,性能几乎与物理机一致。这对于以下场景至关重要:
- AI/ML 训练:虚拟机内运行 PyTorch/TensorFlow 直接使用 GPU
- 视频渲染:Blender、DaVinci Resolve 等渲染农场
- VDI 桌面虚拟化:Windows 虚拟机获得原生 3D 加速
- 游戏串流:Steam 游戏在虚拟机中运行
1.2 前置条件检查
# 1. 检查 CPU 是否支持 IOMMU
dmesg | grep -i iommu
# 2. 检查 Intel 或 AMD 的 IOMMU 支持
cat /proc/cpuinfo | grep -E "vmx|svm"
# 3. 检查 GPU 所在 IOMMU 分组
# 安装必要的工具
sudo apt install -y pciutils
# 列出所有 PCI 设备及其 IOMMU 分组
for d in /sys/kernel/iommu_groups/*/devices/*; do
echo "IOMMU Group $(basename $(dirname $(dirname $d))): $(lspci -nns ${d##*/})"
done
⚠️ 关键坑:GPU 必须位于独立的 IOMMU 分组中才能透传。如果 GPU 和其 HDMI 音频设备在同一分组(通常如此),它们必须一起透传。如果与其它设备(如 NVMe 硬盘)共享分组,则无法透传。
1.3 配置 VFIO
# 1. 在 GRUB 中启用 IOMMU(Intel 为 intel_iommu=on,AMD 为 amd_iommu=on)
sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="[^"]*/& intel_iommu=on iommu=pt vfio-pci.ids=10de:1e04,10de:10f7/' /etc/default/grub
sudo update-grub
# 2. 找到 GPU 的 Vendor:Device ID
lspci -nn | grep -i nvidia
# 输出示例:01:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU104 [10de:1e04]
# 01:00.1 Audio device [0403]: NVIDIA Corporation TU104 HD Audio [10de:10f7]
# 3. 配置 VFIO 模块(将 GPU 绑定到 vfio-pci 驱动)
cat << 'EOF' | sudo tee /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:1e04,10de:10f7
softdep nouveau pre: vfio-pci
softdep nvidia pre: vfio-pci
EOF
# 4. 更新 initramfs
sudo update-initramfs -u
# 5. 重启
sudo reboot
1.4 验证 VFIO 绑定
# 重启后检查 GPU 是否已绑定到 vfio-pci
lspci -nnk -d 10de:1e04
# 期望输出中包含 "Kernel driver in use: vfio-pci"
# 检查 VFIO 设备是否可用
ls -la /dev/vfio/
# 应该看到 vfio 设备文件
1.5 创建带 GPU 透传的虚拟机
# 使用 virt-install 创建带 GPU 透传的虚拟机
sudo virt-install
--name ai-workstation
--ram 16384
--vcpus 8
--disk path=/var/lib/libvirt/images/ai-workstation.qcow2,size=100,bus=virtio
--os-variant ubuntu22.04
--network network=default,model=virtio
--graphics spice
--host-device 01:00.0
--host-device 01:00.1
--cdrom /var/lib/libvirt/images/ubuntu-22.04-live-server-amd64.iso
通过 XML 配置 GPU 透传(更精细控制):
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<rom bar='on' file='/usr/share/vgabios/vgabios.bin'/>
</hostdev>
# 编辑虚拟机 XML
sudo virsh edit ai-workstation
二、实时迁移(Live Migration):零停机维护
2.1 原理
实时迁移允许将运行中的虚拟机从一台宿主机迁移到另一台,无感知停机时间(通常 <100ms)。KVM 通过将内存页从源主机复制到目标主机,同时跟踪脏页(dirty pages),最终完成切换。
2.2 前提条件
# 两台宿主机都需要:
# 1. 共享存储(NFS、iSCSI 或 GlusterFS)
# 2. 同一网络子网或桥接网络
# 3. libvirtd 配置允许远程连接
# 配置 libvirtd 监听 TCP(默认只监听本地 socket)
sudo sed -i 's/#listen_tcp = 1/listen_tcp = 1/' /etc/libvirt/libvirtd.conf
sudo sed -i 's/#auth_tcp = "sasl"/auth_tcp = "none"/' /etc/libvirt/libvirtd.conf
# 重启 libvirtd
sudo systemctl restart libvirtd
2.3 执行实时迁移
# 源主机上执行迁移(目标主机需已配置 libvirtd 并监听 TCP)
sudo virsh migrate --live
--verbose
--persistent
--undefinesource
ubuntu-server-01
qemu+tcp://192.168.1.101/system
# 迁移进度监控
# 在另一个终端查看迁移状态
sudo virsh domjobinfo ubuntu-server-01
# 示例输出:
# Job type: Unbounded
# Operation: Outgoing migration
# Time elapsed: 4523 ms
# Data processed: 2.345 GiB
# Remaining data: 0.125 GiB
# Total bandwidth: 512.34 MiB/s
2.4 迁移调优参数
# 设置迁移带宽限制(默认 32 MiB/s)
sudo virsh migrate-setspeed ubuntu-server-01 --bandwidth 1024
# 设置最大停机时间(毫秒)
sudo virsh migrate-setmaxdowntime ubuntu-server-01 100
# 压缩迁移(减少网络传输量)
virsh migrate --live --compressed
--comp-methods xbzrle
ubuntu-server-01
qemu+tcp://192.168.1.101/system
生产经验:对于大内存虚拟机(>64GB),推荐先通过
--copy-storage-inc做增量存储迁移,再执行实时迁移,避免一次性传输大量数据造成网络拥塞。
三、存储池深度管理:LVM、NFS、iSCSI
3.1 libvirt 存储池架构
libvirt 的存储池抽象层支持多种后端:
| 后端类型 | 性能 | 特点 | 适用场景 |
|---|---|---|---|
dir |
中等 | 目录文件系统 | 开发测试 |
lvm |
高 | LVM 逻辑卷 | 高性能生产 |
nfs |
中低 | 网络文件系统 | 共享存储、迁移 |
iscsi |
高 | 块设备 | SAN 存储 |
rbd |
高 | Ceph 分布式存储 | 大规模集群 |
3.2 LVM 存储池
# 1. 创建 LVM 卷组(假设已有 /dev/sdb 磁盘)
sudo pvcreate /dev/sdb
sudo vgcreate vg_vms /dev/sdb
# 2. 定义 libvirt LVM 存储池
cat > /tmp/lvm-pool.xml << 'EOF'
<pool type='logical'>
<name>lvm-pool</name>
<source>
<name>vg_vms</name>
<format type='lvm2'/>
</source>
<target>
<path>/dev/vg_vms</path>
</target>
</pool>
EOF
sudo virsh pool-define /tmp/lvm-pool.xml
sudo virsh pool-start lvm-pool
sudo virsh pool-autostart lvm-pool
# 3. 在 LVM 存储池中创建虚拟机磁盘
sudo virsh vol-create-as lvm-pool vm1-disk.qcow2 50G --format qcow2
# 4. 创建虚拟机时使用 LVM 卷
sudo virt-install
--name vm-on-lvm
--disk vol=lvm-pool/vm1-disk.qcow2,bus=virtio
...
3.3 NFS 共享存储池
# 1. 在 NFS 服务器上导出目录
# /etc/exports 中添加:
# /srv/nfs/vms 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
# 2. 在 KVM 宿主机上定义 NFS 存储池
cat > /tmp/nfs-pool.xml << 'EOF'
<pool type='netfs'>
<name>nfs-pool</name>
<source>
<host name='192.168.1.100'/>
<dir path='/srv/nfs/vms'/>
<format type='nfs'/>
</source>
<target>
<path>/var/lib/libvirt/nfs-pool</path>
</target>
</pool>
EOF
sudo virsh pool-define /tmp/nfs-pool.xml
sudo virsh pool-start nfs-pool
sudo virsh pool-autostart nfs-pool
# 3. 查看存储池信息
sudo virsh pool-info nfs-pool
sudo virsh vol-list nfs-pool
3.4 存储池性能对比
# 测试磁盘性能(在虚拟机内运行)
# 安装 fio
sudo apt install -y fio
# 顺序读写测试
fio --name=seq-read --ioengine=libaio --direct=1 --bs=1m --size=1g
--numjobs=4 --runtime=30 --time_based --rw=read --group_reporting
# 随机读写测试(4K 混合读写)
fio --name=rand-rw --ioengine=libaio --direct=1 --bs=4k --size=1g
--numjobs=8 --runtime=60 --time_based --rw=randrw --rwmixread=70
--group_reporting
实测数据参考:相同硬件条件下,LVM 存储池的 4K 随机 IOPS 比目录文件系统高约 30-50%,因为 LVM 跳过文件系统层直接操作块设备。
四、CPU 与内存性能调优
4.1 CPU Pinning:绑定物理核心
CPU pinning 将虚拟机的虚拟 CPU 绑定到特定的物理 CPU 核心,避免缓存颠簸和上下文切换开销:
# 查看宿主机 CPU 拓扑
sudo virsh capabilities | grep -E "<cpu|<topology|<socket|<die|<core|<thread"
# 创建 CPU pinning XML 配置
cat > /tmp/cpu-pin.xml << 'EOF'
<vcpu placement='static' cpuset='0-7'>8</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='2'/>
<vcpupin vcpu='3' cpuset='3'/>
<vcpupin vcpu='4' cpuset='4'/>
<vcpupin vcpu='5' cpuset='5'/>
<vcpupin vcpu='6' cpuset='6'/>
<vcpupin vcpu='7' cpuset='7'/>
<emulatorpin cpuset='0-7'/>
<iothreadpin iothread='1' cpuset='0-1'/>
</cputune>
EOF
# 应用配置
sudo virsh edit your-vm-name
# 在 <domain> 标签内插入上述内容
4.2 NUMA 亲和性
# 查看宿主机 NUMA 拓扑
sudo numactl --hardware
# 示例输出:
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7
# node 1 cpus: 8 9 10 11 12 13 14 15
# node 0 size: 32768 MB
# node 1 size: 32768 MB
# node 0 free: 12288 MB
# node 1 free: 16384 MB
# 在虚拟机 XML 中配置 NUMA:
# <numatune>
# <memory mode='strict' nodeset='0'/>
# <memnode cellid='0' mode='strict' nodeset='0'/>
# </numatune>
# <cpu>
# <numa>
# <cell id='0' cpus='0-3' memory='8192' unit='KiB'/>
# </numa>
# </cpu>
4.3 透明大页(HugePages)配置
# 1. 在宿主机上分配 2MB 大页
echo 4096 | sudo tee /proc/sys/vm/nr_hugepages
# 永久配置
echo 'vm.nr_hugepages = 4096' | sudo tee -a /etc/sysctl.conf
# 2. 确认分配
cat /proc/meminfo | grep HugePages
# HugePages_Total: 4096
# HugePages_Free: 4096
# 3. 在虚拟机 XML 中启用大页
# <memoryBacking>
# <hugepages/>
# </memoryBacking>
# 4. 验证虚拟机是否使用大页
sudo virsh dommemstat your-vm-name
# 查看 'rss' 和 'actual' 值
4.4 KSM(Kernel Same-page Merging)
KSM 允许宿主机合并虚拟机之间的相同内存页,对运行多个相同操作系统(如同为 Ubuntu)的虚拟机效果显著:
# 启用 KSM
sudo systemctl start ksmtuned
sudo systemctl enable ksmtuned
# 手动控制 KSM 参数
echo 100 | sudo tee /sys/kernel/mm/ksm/pages_to_scan
echo 200 | sudo tee /sys/kernel/mm/ksm/sleep_millisecs
# 监控 KSM 效果
cat /sys/kernel/mm/ksm/pages_shared
cat /sys/kernel/mm/ksm/pages_sharing
# pages_shared 是合并后的唯一页数
# pages_sharing 是共享这些页面的总引用数
# 比值越大,节省的内存越多
数据参考:运行 5 个相同 Ubuntu Server 22.04 虚拟机(各分配 4GB),KSM 可节省约 40-50% 内存,实际物理使用从 20GB 降至 10-12GB。
五、自动化部署:Cloud-Init + Terraform
5.1 使用 Cloud-Init 自动配置虚拟机
Cloud-Init 是 Ubuntu 云镜像的标准初始化工具,可以自动设置主机名、用户、SSH 密钥、网络配置等:
# 1. 下载 Ubuntu Cloud 镜像
sudo mkdir -p /var/lib/libvirt/images/cloud
cd /var/lib/libvirt/images/cloud
sudo wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
# 2. 创建 Cloud-Init 元数据文件
cat > /tmp/meta-data << 'EOF'
instance-id: ubuntu-dev-01
local-hostname: dev-01
EOF
# 3. 创建 Cloud-Init 用户数据文件
cat > /tmp/user-data << 'EOF'
#cloud-config
ssh_pwauth: true
users:
- name: devops
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAA... your-public-key-here
chpasswd:
list: |
devops:TempPass123!
expire: false
packages:
- docker.io
- htop
- vim
- git
runcmd:
- systemctl enable docker
- systemctl start docker
- echo "Cloud-init setup complete" > /etc/motd
EOF
# 4. 创建 Cloud-Init ISO
sudo genisoimage -output /var/lib/libvirt/images/cloud/seed.iso
-volid cidata -joliet -rock
/tmp/user-data /tmp/meta-data
# 5. 创建虚拟机(使用 Cloud-Init)
sudo virt-install
--name ubuntu-cloud-01
--ram 2048
--vcpus 2
--disk path=/var/lib/libvirt/images/cloud/jammy-server-cloudimg-amd64.img,format=qcow2,bus=virtio
--disk path=/var/lib/libvirt/images/cloud/seed.iso,device=cdrom
--os-variant ubuntu22.04
--network network=default,model=virtio
--graphics none
--console pty,target_type=serial
--noautoconsole
# 6. 等待 Cloud-Init 完成后通过 SSH 连接
sudo virsh domifaddr ubuntu-cloud-01
ssh devops@192.168.122.xxx
5.2 Terraform + libvirt 管理虚拟机
通过 Terraform 的 libvirt provider 实现基础设施即代码:
# 1. 安装 Terraform
sudo apt install -y terraform
# 2. 创建 Terraform 配置
cat > /tmp/terraform-kvm/main.tf << 'TERRA'
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.7.6"
}
}
}
provider "libvirt" {
uri = "qemu:///system"
}
resource "libvirt_volume" "ubuntu_volume" {
name = "ubuntu-${count.index}.qcow2"
pool = "default"
source = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
format = "qcow2"
count = 3
}
resource "libvirt_cloudinit_disk" "commoninit" {
name = "commoninit.iso"
pool = "default"
user_data = <<-EOF
#cloud-config
ssh_pwauth: true
users:
- name: admin
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
EOF
}
resource "libvirt_domain" "ubuntu_vm" {
count = 3
name = "ubuntu-${count.index}"
memory = 2048
vcpu = 2
cloudinit = libvirt_cloudinit_disk.commoninit.id
disk {
volume_id = element(libvirt_volume.ubuntu_volume.*.id, count.index)
}
network_interface {
network_name = "default"
wait_for_lease = true
}
console {
type = "pty"
target_port = "0"
target_type = "serial"
}
}
output "vm_ips" {
value = libvirt_domain.ubuntu_vm.*.network_interface.0.addresses
}
TERRA
# 3. 部署
cd /tmp/terraform-kvm
terraform init
terraform plan
terraform apply
六、监控与告警
6.1 virt-top:实时监控
# 安装 virt-top
sudo apt install -y virt-top
# 实时监控(类似 top 但针对虚拟机)
sudo virt-top
# 显示信息包括:
# - 每个虚拟机的 CPU 使用率
# - 内存使用情况
# - 磁盘 I/O
# - 网络 I/O
6.2 libvirt 性能统计
# 获取虚拟机 CPU 统计
sudo virsh cpu-stats your-vm-name --total
# 获取内存统计
sudo virsh dommemstat your-vm-name
# 获取块设备统计
sudo virsh domblkstat your-vm-name vda --human
# 获取网络接口统计
sudo virsh domifstat your-vm-name vnet0
# 持续监控(每 2 秒刷新)
watch -n 2 "sudo virsh dommemstat your-vm-name"
6.3 集成 Prometheus 监控
# 安装 libvirt Prometheus exporter
sudo apt install -y prometheus-libvirt-exporter
# 配置 exporter
cat > /etc/default/prometheus-libvirt-exporter << 'EOF'
ARGS="--libvirt.uri=qemu:///system --web.listen-address=:9177"
EOF
sudo systemctl restart prometheus-libvirt-exporter
sudo systemctl enable prometheus-libvirt-exporter
# 验证指标
curl http://localhost:9177/metrics | head -20
# 输出示例:
# HELP libvirt_domain_cpu_time_seconds Domain CPU time
# TYPE libvirt_domain_cpu_time_seconds counter
# libvirt_domain_cpu_time_seconds{domain="ubuntu-server-01"} 12345.67
# HELP libvirt_domain_memory_usage_bytes Domain memory usage
# TYPE libvirt_domain_memory_usage_bytes gauge
# libvirt_domain_memory_usage_bytes{domain="ubuntu-server-01"} 2147483648
七、故障排查实战
7.1 IOMMU 分组问题
# 查看所有 IOMMU 分组,检查是否有设备冲突
#!/bin/bash
for g in $(find /sys/kernel/iommu_groups -maxdepth 1 -type d | sort); do
group=$(basename $g)
echo "IOMMU Group $group:"
for d in $g/devices/*; do
echo " $(lspci -nns ${d##*/})"
done
done
# 如果 GPU 与 NVMe 硬盘在同一分组,需要:
# 1. 使用 ACS 补丁内核(或通过 `pcie_acs_override=downstream` 内核参数拆分)
# 2. 升级主板 BIOS
# 3. 更换 PCIe 插槽
7.2 实时迁移失败排查
# 检查 libvirtd 日志
sudo journalctl -u libvirtd -f --no-pager -n 50
# 常见错误及解决:
# 错误:"cannot migrate: no shared storage"
# 解决:两台宿主机必须共享存储(NFS/iSCSI/Ceph)
# 错误:"migration job: failed to connect to remote libvirt"
# 解决:检查目标主机防火墙(默认为 TCP 16509 端口)
sudo ufw allow 16509/tcp
# 错误:"Timed out during operation"
# 解决:增加超时时间或降低迁移带宽
sudo virsh migrate-setmaxdowntime your-vm-name 500
sudo virsh migrate-setspeed your-vm-name --bandwidth 2048
7.3 GPU 透传后虚拟机内无法识别
# 检查 VFIO 设备是否正确透传
sudo virsh dumpxml your-vm-name | grep -A 10 hostdev
# 检查虚拟机内是否安装了正确的驱动
# 在虚拟机内:
lspci -nn | grep -i nvidia
# 如果看不到 GPU,可能需要在宿主机上设置 ROM Bar
# 在 <hostdev> 中添加 <rom bar='on'/>
# 如果 GPU 透传后宿主机黑屏
# 在 GRUB 中添加 video=efifb:off 或 vfio-pci.ids=... 禁用 GOP 驱动
总结
今天我们深入探讨了 KVM 虚拟化的高阶主题,远远超出了”创建虚拟机”的入门范畴:
| 技能 | 关键命令 | 生产场景 |
|---|---|---|
| GPU 透传 (VFIO) | vfio-pci.ids=... |
AI/ML 训练、渲染农场 |
| 实时迁移 | virsh migrate --live |
零停机维护 |
| LVM 存储池 | virsh pool-create-as |
高性能存储 |
| CPU Pinning | cputune > vcpupin |
延迟敏感应用 |
| 大页内存 | nr_hugepages |
数据库虚拟机 |
| Cloud-Init | #cloud-config |
自动化部署 |
| Terraform | terraform apply |
基础设施即代码 |
| Prometheus 监控 | libvirt-exporter |
生产监控告警 |
通过这些技能,你可以将 Ubuntu KVM 环境从简单的开发测试平台升级为生产级虚拟化基础设施。下一期将进入容器编排入门,探讨 Docker Compose 与 Kubernetes 单节点部署——敬请期待!
系列目录
| 天数 | 主题 | 状态 |
|---|---|---|
| 第 1 天 | Ubuntu 简介与版本选择 | ✅ |
| 第 2 天 | 手把手安装 Ubuntu | ✅ |
| 第 3 天 | Ubuntu 桌面环境初探 | ✅ |
| 第 4 天 | Ubuntu 终端基础 | ✅ |
| 第 5 天 | 用户与权限管理 | ✅ |
| 第 6 天 | 软件包管理 | ✅ |
| 第 7 天 | 文件与文本操作 | ✅ |
| 第 8 天 | 系统服务管理 | ✅ |
| 第 9 天 | 磁盘与文件系统管理 | ✅ |
| 第 10 天 | 网络配置与管理 | ✅ |
| 第 11 天 | 进程管理与监控 | ✅ |
| 第 12 天 | 计划任务与自动化 | ✅ |
| 第 13 天 | 备份与恢复策略 | ✅ |
| 第 14 天 | 系统更新与升级管理 | ✅ |
| 第 15 天 | SSH 远程管理与安全加固 | ✅ |
| 第 16 天 | Web 服务器搭建 | ✅ |
| 第 17 天 | 数据库服务器部署 | ✅ |
| 第 18 天 | Docker 安装与容器化管理 | ✅ |
| 第 19 天 | 文件共享服务 | ✅ |
| 第 20 天 | 监控与告警系统 | ✅ |
| 第 21 天 | 邮件服务器基础 | ✅ |
| 第 22 天 | 系统安全加固 | ✅ |
| 第 23 天 | 性能调优与内核参数 | ✅ |
| 第 24 天 | KVM 虚拟化高级实战(重访) | 🟢 今日 |
| 第 25 天 | 容器编排入门 | ✅ |
| 第 26 天 | 高可用与负载均衡 | ✅ |
| 第 27 天 | Ubuntu 自动部署 | ✅ |
| 第 28 天 | 故障排查实战 | ✅ |
| 第 29 天 | Ubuntu 社区与文档 | ✅ |
| 第 30 天 | 30 天回顾总结 | ✅ |















暂无评论内容