Ubuntu 系统系列 | 第 24 天(重访):KVM 虚拟化高级实战——生产级配置与性能调优

第 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 天回顾总结
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容