KVM 虚拟平台

2020-07-16 linux kvm

全虚拟化最简单通俗的理解是,在一台机器上可以运行不同的平台 (例如Linux、Windows等),不过其对应的仍然是 x86_64 架构。另外,如果允许在机器上可以运行不同的架构(例如x86_64、PowerPC、ARM等),那么这种的虚拟化方式称为硬件仿真。

这里简单介绍下 Linux 中 KVM 的使用方式。

简介

Kernel-based Virtual Machine, KVM 是一款基于 Linux 内核的虚拟机,安装了 kvm.ko 内核模块后,直接将 Linux 内核改变成了一个 Hypervisor,此时宿主机上仍然可以运行进程,同时可以创建虚拟机,不过每个虚机就是运行在 Linux 内核之上的一个系统进程,可以随时使用 kill 命令杀死。

最初,Linux 内核只有用户、内核态,在引入了 KVM 模块之后,也就引入了 guest 模式,此模式只负责非 IO 类操作或者说是非特权指令操作,如算术运行。在 Guest 模式中,可以直接运行到物理 CPU 的 ring 3 上,但一旦有 IO 调用或者需要特权指令,就需要从用户态到内核态的调用。

基本组件

在使用时,一般包含了 KVM、QEMU、libvirt 三部分。

kvm qemu arch

简单介绍各个模块常用的功能。

  • KVM 就是 Linux 的内核模块,利用 CPU 的硬件辅助虚拟化技术 Intel-VT/AMD-V 以及内存相关 Intel-EPT/AMD-RVI 技术,这样 Guest OS 的 CPU 指令可以直接运行,大大提高了速度。
  • QEMU 其中 KVM 模块不能模拟 IO 设备,通常使用 QEMU 来模拟 IO 设备,包括网卡、磁盘等,这样 KVM 加上 QEMU 之后就能实现真正意义上服务器虚拟化。
  • libvirt 目前使用最为广泛的对 KVM 虚拟机进行管理的工具和 API,通过 libvirtd 的后台程序,可以被本地的 virsh 命令行调用。

其实 QEMU 完全可以模拟机器指令,所以,在 x86_64 平台上可以模拟 ARM 指令,只是通过 KVM 允许 CPU 指令直接运行,这样要快很多。

在 Linux 中使用时,KVM 通过 /dev/kvm 暴露接口,用户态程序可以通过 ioctl() 函数来访问这个接口。

总结

各个模块调用流程如下。

kvm qemu process

在运行的时候,包括了内核态和用户态,内核模块主要负责虚拟机的创建、虚拟内存分配、VCPU 寄存器的读写以及 VCPU 的运行;而 QEMU 用于模拟虚拟机的用户空间组件,提供 IO 设备模型,访问外设的途径。

KVM 所使用的方法是通过简单地加载内核模块而将 Linux 内核转换为一个系统管理程序,该模块会导出了一个名为 /dev/kvm 的设备,可以启用内核的 guest 模式。

通过 /dev/kvm 设备,可以使 VM 的地址空间独立于内核以及其 VM 的地址空间。

准备环境

先看看在 Linux 中如何配置 KVM 环境。

检查硬件支持

KVM 是基于 x86 虚拟化扩展 (Intel VT 或者 AMD-V) 技术的虚拟机软件,所以查看 CPU 是否支持 VT 技术,就可以判断是否支持 KVM 。在 /proc/cpuinfo 中,如果有 vmx(Intel) 或 svm(AMD) 字样,就说明 CPU 支持。

$ egrep -q 'vmx|svm' /proc/cpuinfo && echo yes || echo no

为了避免不必要的权限错误,可以暂时关闭 SELinux,也就是将 /etc/sysconfig/selinux 配置文件中的 SELinux=enforcing 修改为 SELinux=disabled

安装软件包

安装如下基础软件包,其中 CentOS7 需要安装 bridge-utils 包。

# yum -y install bridge-utils qemu-kvm libvirt virt-install
# apt -y install bridge-utils qemu-kvm virtinst libvirt-daemon-system

其中各个包的使用用途如下。

  • qemu-kvm 主要的 KVM 程序包;
  • virt-install virtinst 基于 libvirt 服务的虚拟机创建命令,主要使用命令行;
  • libvirt 工具包,提供 libvirt 服务;
  • libvirt-client 为虚拟客户机提供的 C 语言工具包;
  • virt-top 虚拟机统计命令,类似于 top 命令,可以显示当前虚拟机的资源使用情况;
  • bridge-utils 创建和管理桥接设备的工具;
  • virt-viewer 通过VNC连接连接到已配置好的虚拟机;
  • virt-manager 一个GUI虚拟机管理工具;

确认内核是否加载了 KVM 模块,如果没有,可以使用 modprobe kvm 来加载。

# lsmod |grep kvm
kvm_intel             170181  0
kvm                   554609  1 kvm_intel
irqbypass              13503  1 kvm

然后,通过 systemctl start libvirtd 命令启动 libvirtd 服务,否则会报 failed to connect to the hypervisor 的错误;然后用 virt-host-validate 检查环境是否准备好。

网络设备

当安装完 KVM 之后,一般会自动创建 virbr0 网桥设备,通常是用于测试环境,可以通过 ip addr 命令查看,如果不存在,那么可以通过 systemctl restart NetworkManager 命令重启下网络。

同时需要确保 vhost_net 模块已经加载到内核,命令如下。

# modprobe vhost_net
# echo vhost_net | tee -a /etc/modules
# lsmod | grep vhost

如果要虚拟机访问网络,那么就将网桥链接到物理网卡。

常用命令

每个虚拟机会在 /etc/libvirt/qemu 目录下生成一个配置文件 (运行时使用的是 /run/libvirt/qemu 中的内容),包括了 CPU、磁盘的配置信息等,可以直接进行修改,或者通过 virsh edit XXX 修改。

----- 确认各个版本号
$ virsh --version
$ virt-install --version

----- 查看虚拟机列表,需要root执行,通过--all查看所有,--inactive未运行虚拟机
$ virsh list --all
  Id    Name                           State
 ----------------------------------------------------
  7     centos7                        running

----- 连接到虚拟机,通过 Ctrl-] 退出
$ virsh console centos7

----- 虚拟机信息
$ virsh dominfo <VIRT-MACHINE>

----- 虚拟机的分区信息
$ virt-df <VIRT-MACHINE>

----- 删除虚拟机
$ virsh shutdown centos7             ← 关机
$ virsh destroy centos7              ← 强制关机
$ virsh undefine centos7             ← 只删除配置文件,磁盘文件未删除

----- 虚拟机其它操作
$ virsh start centos7                ← 开机
$ virsh reboot centos7               ← 重启
$ virsh suspend centos7              ← 挂起
$ virsh resume centos7               ← 恢复

----- 设置/关闭开机自动启动
$ virsh autostart centos7
$ virsh autostart --disable centos7

彻底清理虚拟机

在 KVM 中,通过 undefine 删除虚拟机即可,不过此时虚拟机的磁盘文件依然存在,支持对 KVM 来说这个虚拟机已经清理了配置,不能再直接启动了。

----- 查看虚拟机所使用的磁盘文件
# virsh dumpxml vm-name | grep 'source file'

----- 关闭虚拟机,也可以强制关闭
# virsh shutdown vm-name
# virsh destroy vm-name

----- 检查虚拟机是否做过快照,如果有则先删除快照
# virsh snapshot-list --domain vm-name

----- 假设有快照 3sep2016u1,则先删除
# virsh snapshot-delete --domain vm-name --snapshotname 3sep2016u1

----- 删除虚拟机
# virsh undefine vm-name

----- 根据上述的配置来删除磁盘文件
# rm -f /var/lib/libvirt/images/vm-name.img

安装操作系统

这里使用直接安装 CentOS、Puppy,直接从官方下载相关的安装包,然后执行如下命令。

----- 安装CentOS7
# virt-install --virt-type=kvm --name=centos7 --vcpus=2 --memory=4096 \
    --location=CentOS-7-x86_64-Minimal-2009.iso                       \
    --disk path=centos7.qcow2,size=40,format=qcow2                    \
    --network bridge=virbr0 --graphics none --extra-args='console=ttyS0' --force

# virt-install --virt-type=kvm --name=puppy --vcpus=2 --memory=4096   \
    --cdrom=fossapup64-9.5.iso                                        \
    --disk path=puppy.qcow2,size=40,format=qcow2                      \
    --network bridge=virbr0 --graphics spice

安装 Puppy 时必须要通过 --cdrom 选项加载,否则会报错。常用参数介绍如下。

----- 通用参数
-n NAME, --name=NAME
    虚拟机名称,需全局惟一
-r MEMORY, --ram=MEMORY
    内存大小,单位为MB
--vcpus=VCPUS[,maxvcpus=MAX][,sockets=#][,cores=#][,threads=#]
    CPU个数及相关配置
--cpu=CPU
    CPU模式及特性,可以使用qemu-kvm -cpu ?来获取支持模式
--os-type=DISTRO_TYPE
    操作系统类型,如linux、unix或windows等
-v, --hvm
    当物理机同时支持完全虚拟化和半虚拟化时,指定使用完全虚拟化
-p, --paravirt
    使用半虚拟化
--virt-type
    使用的hypervisor,如kvm、qemu、xen等,可选列表通过virsh capabilities查看
--graphics TYPE,opt1=val1,opt2=val2
    图形显示相关配置,不会配置硬件,仅指定虚拟机启动后对其进行访问的接口
    TYPE 显示类型,可以为vnc、sdl、spice或none等,默认为vnc:port


----- 启动方式
-c CDROM, --cdrom=CDROM
    光盘安装介质
-l LOCATION, --location=LOCATION
    安装源URL,支持FTP、HTTP及NFS等,如ftp://172.16.0.1/pub
--pxe
    基于PXE完成安装
--livecd
    把光盘当作LiveCD


----- 磁盘设置
--disk=DISKOPTS
    存储设备及其属性;格式为--disk /some/storage/path,opt1=val1,opt2=val2
    常用选项有:
        device 设备类型,如cdrom、disk或floppy等,默认为disk;
        bus    磁盘总结类型,其值可以为ide、scsi、usb、virtio或xen;
        perms  访问权限,如rw、ro或sh(共享的可读写),默认为rw;
        size   新建磁盘映像的大小,单位为GB;
        cache  缓存模型,其值有none、writethrouth(缓存读)及writeback(缓存读写);
        format 磁盘映像格式,如raw、qcow2、vmdk等;
        sparse 磁盘映像使用稀疏格式,即不立即分配指定大小的空间;


----- 网络设置
-w NETWORK, --network=NETWORK,opt1=val1,opt2=val2
    将虚拟机连入宿主机的网络中,其中NETWORK可以为
    bridge=BRIDGE 连接至名为"BRIDEG"的桥设备
    network=NAME  连接至名为"NAME"的网络

此时会启动一个 qemu-kvm 进程,然后,最终会进入到一个终端安装界面,可以按需配置,例如时区、安装目录、root 用户密码等等,安装完成后重启即可。

VNC

这里并非在虚拟机内部安装 VNC 服务器,默认使用 SPICE 协议。

----- 修改配置文件
# cat /etc/libvirt/qemu.conf
vnc_listen = "0.0.0.0"

默认绑定在 127.0.0.1 地址上,修改这里的配置后就无需在安装 KVM 虚拟机时指定 vnc_listen 参数了。另外,在虚拟主机上有很多个虚拟机的时候,需要指定每个虚拟机的端口,否则将会很乱。

----- 修改虚拟机的配置文件
# virsh edit centos7
<graphics type='vnc' port='5916' autoport='no' listen='0.0.0.0'>
    <listen type='address' address='0.0.0.0'/>
</graphics>

启动虚拟机可以查看其监听端口。

# netstat -tunlp | grep 5916
# ss -al | grep 5916

然后可以通过 Console 或者 VNC 客户端进行连接。

其它

问题排查

整理一些常见的异常排查方法。

已经在使用

报错信息为 Guest name 'XXX' is already in use ,一般是因为之前的虚拟机相关文件没有彻底清理掉,和 XEN 的半虚拟化不同,其中 XEN 只需要删除配置文件即可,而 KVM 需要执行如下的命令。

# virsh undefine <VIRT-NAME>

权限问题

默认使用 qemu 用户创建相关文件,所以需要确保目录权限,或者也可以修改 /etc/libvirt/qemu.conf 配置文件的如下内容,直接使用 root 用户。

user = "root"
group = "root"

然后重启 systemctl restart libvirtd

环境检查

----- 检查虚拟机环境是否准备好
# virt-host-validate

可能会出现如下的错误 IOMMU appears to be disabled in kernel ,此时需要通过如下方式进行设置。修改配置 /etc/default/grubGRUB_CMDLINE_LINUX 选项,添加 intel_iommu=on 内容。

接着通过 grub2-mkconfig -o /boot/grub2/grub.cfg 更新 grub2 ,然后重启。

virt-viewer

可以通过 VNC 链接到 KVM 虚拟机。

# virt-viewer --connect qemu:///system --wait puppy 2>/dev/null &