QEMU 虚拟化完全指南 / 14 - 虚拟机测试
14 - 虚拟机测试
掌握使用 QEMU 进行自动化测试、CI/CD 集成,以及 libguestfs/guestfs-tools 的高级运维。
14.1 QEMU 测试概述
QEMU 在测试领域有广泛的应用:
| 场景 | 工具 | 说明 |
|---|---|---|
| 操作系统测试 | QEMU + cloud-init | 快速部署测试 VM |
| 内核测试 | QEMU + 内核编译 | 测试自编译内核 |
| 文件系统测试 | libguestfs | 无需启动 VM 操作磁盘 |
| 网络测试 | QEMU + TAP/桥接 | 虚拟网络拓扑 |
| 安全测试 | QEMU 沙箱 | 恶意软件分析 |
| CI/CD | QEMU + Docker | 多架构测试 |
14.2 自动化虚拟机测试
创建测试用虚拟机脚本
#!/bin/bash
# test-vm.sh - 自动化 VM 测试脚本
set -euo pipefail
VM_NAME="test-vm-$$"
DISK="test-disk-$$.qcow2"
CLOUD_IMG="ubuntu-22.04-cloudimg-amd64.img"
SSH_PORT=$((20000 + RANDOM % 40000))
TIMEOUT=300
cleanup() {
echo "清理资源..."
[ -f "${DISK}" ] && rm -f "${DISK}"
[ -f "seed-$$.iso" ] && rm -f "seed-$$.iso"
[ -f "meta-data.$$" ] && rm -f "meta-data.$$"
[ -f "user-data.$$" ] && rm -f "user-data.$$"
pkill -f "qemu.*${VM_NAME}" 2>/dev/null || true
}
trap cleanup EXIT
# 准备磁盘
qemu-img create -f qcow2 -b "${CLOUD_IMG}" -F qcow2 "${DISK}" 40G
# 准备 cloud-init
cat > "meta-data.$$" << EOF
instance-id: ${VM_NAME}
local-hostname: ${VM_NAME}
EOF
cat > "user-data.$$" << 'EOF'
#cloud-config
users:
- name: test
ssh_authorized_keys:
- ssh-rsa AAAA... # 替换为你的公钥
sudo: ['ALL=(ALL) NOPASSWD:ALL']
password: test
chpasswd:
expire: false
ssh_pwauth: true
EOF
cloud-localds "seed-$$.iso" "user-data.$$" "meta-data.$$"
# 启动虚拟机
qemu-system-x86_64 \
-name "${VM_NAME}" \
-enable-kvm \
-cpu host \
-m 2G \
-smp 2 \
-drive file="${DISK}",format=qcow2,if=virtio \
-drive file="seed-$$.iso",format=raw,if=virtio \
-netdev user,id=net0,hostfwd=tcp::${SSH_PORT}-:22 \
-device virtio-net-pci,netdev=net0 \
-display none \
-daemonize
# 等待 VM 启动
echo "等待 VM 启动 (端口 ${SSH_PORT})..."
for i in $(seq 1 ${TIMEOUT}); do
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=2 \
-p ${SSH_PORT} test@localhost "echo OK" 2>/dev/null; then
echo "VM 启动成功 (${i}秒)"
break
fi
sleep 1
done
# 执行测试
echo "执行测试..."
ssh -o StrictHostKeyChecking=no -p ${SSH_PORT} test@localhost << 'TEST'
echo "=== 系统信息 ==="
uname -a
cat /etc/os-release | head -5
echo "=== 磁盘使用 ==="
df -h
echo "=== 内存使用 ==="
free -h
echo "=== 运行测试命令 ==="
echo "安装测试包..."
sudo apt-get update -qq
sudo apt-get install -y -qq curl
echo "网络连通性测试..."
curl -s --max-time 10 http://example.com > /dev/null && echo "网络: OK" || echo "网络: FAIL"
echo "所有测试通过!"
TEST
echo "测试完成"
14.3 CI/CD 集成
GitHub Actions 集成
# .github/workflows/test.yml
name: VM Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test-vm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-x86 qemu-utils cloud-image-utils
- name: Download cloud image
run: |
wget -q https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
qemu-img resize jammy-server-cloudimg-amd64.img 20G
- name: Prepare cloud-init
run: |
cat > user-data << 'EOF'
#cloud-config
users:
- name: test
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ${{ secrets.SSH_PUBLIC_KEY }}
EOF
cat > meta-data << 'EOF'
instance-id: ci-test
local-hostname: ci-test
EOF
cloud-localds seed.iso user-data meta-data
- name: Run VM test
run: |
# 启动 VM
qemu-system-x86_64 \
-enable-kvm -cpu host -m 2G -smp 2 \
-drive file=jammy-server-cloudimg-amd64.img,format=qcow2,if=virtio \
-drive file=seed.iso,format=raw,if=virtio \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net0 \
-display none -daemonize
# 等待启动
for i in $(seq 1 120); do
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=2 \
-p 2222 test@localhost "echo OK" 2>/dev/null && break
sleep 1
done
# 执行测试
ssh -o StrictHostKeyChecking=no -p 2222 test@localhost \
"sudo apt-get update && sudo apt-get install -y curl && curl -s http://example.com"
GitLab CI 集成
# .gitlab-ci.yml
vm-test:
stage: test
tags:
- kvm
before_script:
- apt-get update && apt-get install -y qemu-system-x86 qemu-utils cloud-image-utils
script:
- wget -q https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
- qemu-img resize jammy-server-cloudimg-amd64.img 20G
- cloud-localds seed.iso user-data meta-data
- qemu-system-x86_64 -enable-kvm -cpu host -m 2G -smp 2 -drive file=jammy-server-cloudimg-amd64.img,format=qcow2,if=virtio -drive file=seed.iso,format=raw,if=virtio -netdev user,id=net0,hostfwd=tcp::2222-:22 -device virtio-net-pci,netdev=net0 -display none -daemonize
- sleep 60
- ssh -o StrictHostKeyChecking=no -p 2222 test@localhost "echo 'VM test passed'"
14.4 libguestfs 简介
libguestfs 是一个用于访问和修改虚拟机磁盘镜像的工具库,无需启动虚拟机。
安装 libguestfs
# Debian/Ubuntu
sudo apt install -y libguestfs-tools guestfs-tools
# Fedora/RHEL
sudo dnf install -y libguestfs-tools guestfs-tools
# Arch Linux
sudo pacman -S libguestfs
guestfish 交互工具
# 启动 guestfish 交互式 Shell
guestfish --ro -a disk.qcow2
# 在 guestfish 中操作
><fs> run
><fs> list-filesystems
><fs> mount /dev/sda1 /
><fs> cat /etc/hostname
><fs> ls /
><fs> download /etc/passwd /tmp/passwd
><fs> upload /tmp/test.txt /tmp/test.txt
><fs> exit
guestfish 单命令模式
# 读取文件
guestfish --ro -a disk.qcow2 -i cat /etc/hostname
# 列出文件
guestfish --ro -a disk.qcow2 -i ls /etc/
# 下载文件
guestfish --ro -a disk.qcow2 -i download /var/log/syslog /tmp/syslog
# 上传文件
guestfish --rw -a disk.qcow2 -i upload /tmp/config.conf /etc/app/config.conf
# 修改文件
guestfish --rw -a disk.qcow2 -i write /etc/hostname "new-hostname"
14.5 guestfs-tools 命令行工具
virt-cat - 读取虚拟机文件
# 读取虚拟机中的文件
virt-cat -a disk.qcow2 /etc/hostname
# 读取日志
virt-cat -a disk.qcow2 /var/log/syslog | tail -100
# 读取二进制文件
virt-cat -a disk.qcow2 /etc/shadow
virt-edit - 编辑虚拟机文件
# 编辑虚拟机中的文件
virt-edit -a disk.qcow2 /etc/hostname
# 使用 sed 替换
virt-edit -a disk.qcow2 /etc/ssh/sshd_config \
-e 's/^#PermitRootLogin.*/PermitRootLogin no/'
virt-df - 查看虚拟机磁盘使用
# 查看虚拟机磁盘使用情况
virt-df -a disk.qcow2
# 人类可读格式
virt-df -h -a disk.qcow2
# 输出示例:
# Filesystem Size Used Available Use%
# disk.qcow2:/dev/sda1 39.0G 3.2G 33.8G 9%
virt-filesystems - 查看文件系统
# 列出虚拟机中的文件系统
virt-filesystems -a disk.qcow2
# 显示详细信息
virt-filesystems -a disk.qcow2 --all --long
# 显示分区
virt-filesystems -a disk.qcow2 --partitions
virt-inspector - 检查操作系统
# 检测虚拟机的操作系统
virt-inspector -a disk.qcow2
# 输出 XML 格式
virt-inspector -a disk.qcow2 --xml
virt-customize - 自定义虚拟机镜像
# 安装软件包
virt-customize -a disk.qcow2 --install vim,curl,wget
# 设置主机名
virt-customize -a disk.qcow2 --hostname my-server
# 设置密码
virt-customize -a disk.qcow2 --root-password password:mysecret
# 添加 SSH 密钥
virt-customize -a disk.qcow2 --ssh-inject root:file:/path/to/id_rsa.pub
# 运行自定义脚本
virt-customize -a disk.qcow2 --run-command 'apt-get update && apt-get upgrade -y'
# 设置时区
virt-customize -a disk.qcow2 --timezone Asia/Shanghai
# 启用服务
virt-customize -a disk.qcow2 --firstboot-command 'systemctl enable nginx'
virt-sysprep - 清理虚拟机
# 清理虚拟机敏感信息(用于模板/克隆)
virt-sysprep -a disk.qcow2
# 查看支持的操作
virt-sysprep --list
# 选择性清理
virt-sysprep -a disk.qcow2 \
--operations bash-history,logfiles,tmp-files,hostname,ssh-hostkeys,machine-id
14.6 virt-builder 快速构建
# 列出可用的预构建镜像
virt-builder --list
# 构建 Ubuntu 22.04 镜像
virt-builder ubuntu-22.04 \
--output ubuntu-22.04-custom.qcow2 \
--format qcow2 \
--size 40G \
--hostname my-server \
--root-password password:secret \
--install vim,curl \
--timezone Asia/Shanghai \
--run-command 'apt-get update'
# 使用自定义脚本
virt-builder ubuntu-22.04 \
--output ubuntu-custom.qcow2 \
--format qcow2 \
--firstboot firstboot.sh
14.7 virt-v2v 虚拟机转换
# 将 VMware 虚拟机转换为 KVM
virt-v2v -i vmx VMware.vmx -o local -os /var/lib/libvirt/images/
# 将 VirtualBox 虚拟机转换
virt-v2v -i libvirt vbox-vm -o local -os /var/lib/libvirt/images/
# 通过 OVA 文件转换
virt-v2v -i ova vm-export.ova -o local -os /var/lib/libvirt/images/
14.8 测试框架集成
Avocado 测试框架
# 安装 Avocado
pip install avocado-framework
# 创建 QEMU 测试用例
cat > qemu_test.py << 'EOF'
from avocado import Test
class QEMUTest(Test):
def test_vm_boot(self):
"""测试虚拟机启动"""
import subprocess
result = subprocess.run(
['qemu-system-x86_64', '-enable-kvm', '-cpu', 'host',
'-m', '1G', '-drive', 'file=test.qcow2,format=qcow2',
'-display', 'none', '-serial', 'null', '-monitor', 'none',
'-no-reboot'],
capture_output=True, timeout=60
)
self.assertEqual(result.returncode, 0)
def test_disk_create(self):
"""测试磁盘创建"""
import subprocess
result = subprocess.run(
['qemu-img', 'create', '-f', 'qcow2', 'test.qcow2', '10G'],
capture_output=True
)
self.assertEqual(result.returncode, 0)
EOF
# 运行测试
avocado run qemu_test.py
pytest 集成
# test_qemu.py
import pytest
import subprocess
import os
@pytest.fixture
def test_disk():
"""创建测试磁盘"""
subprocess.run(['qemu-img', 'create', '-f', 'qcow2', '/tmp/test.qcow2', '10G'], check=True)
yield '/tmp/test.qcow2'
os.unlink('/tmp/test.qcow2')
def test_qemu_version():
result = subprocess.run(['qemu-system-x86_64', '--version'], capture_output=True, text=True)
assert result.returncode == 0
assert 'QEMU' in result.stdout
def test_disk_info(test_disk):
result = subprocess.run(['qemu-img', 'info', test_disk], capture_output=True, text=True)
assert result.returncode == 0
assert 'qcow2' in result.stdout
要点回顾
| 要点 | 核心内容 |
|---|---|
| 自动化测试 | cloud-init + SSH 实现无人值守测试 |
| CI/CD | GitHub Actions / GitLab CI 集成 QEMU |
| libguestfs | 无需启动 VM 即可操作磁盘文件 |
| guestfs-tools | virt-cat/edit/df/inspect/customize/sysprep |
| virt-builder | 快速构建预配置的 VM 镜像 |
注意事项
KVM 支持: CI/CD 环境中运行 QEMU 需要 KVM 支持。GitHub Actions 的 ubuntu-latest 支持 KVM,但需要确保
/dev/kvm权限正确。
测试隔离: 每个测试应该创建独立的虚拟机和磁盘,避免测试间干扰。
资源限制: CI/CD 环境资源有限,虚拟机配置不宜过大(建议 1-2G 内存,1-2 vCPU)。
扩展阅读
下一步
→ 15 - Docker 中的 QEMU:学习在 Docker 中使用 QEMU 进行多架构构建。