实践说明:基于RHEL9(AlmaLinux9.1)部署,同类系统(CentOS9,RockyLinux9等)适用,但适用场景是不限于此的。
文档说明:本文档旨在帮助快速应用KVM虚拟化技术,重在实践操作,提供了简要参考。
文档形成时期:2021-2023年
因系统或软件版本不同,构建部署可能略有差异,但本文未做细分,对稍有经验者应不存在明显障碍。
因软件世界之复杂和个人能力之限,难免疏漏和错误,欢迎指正。
KVM(Kernel-based Virtual Machine)即基于内核的虚拟机,是一种开源的虚拟化技术。
KVM技术由KVM引擎、虚拟化程序QEMU和管理工具Libvirt组成。
KVM引擎:它是一种基于虚拟化扩展(Intel VT或者AMD-V)的X86硬件的Linux原生全虚拟化引擎,在其中虚拟机被实现为常规的linux进程,由标准linux调度程序进行调度。
虚拟化软件QEMU:
虚拟化软件QEMU:开源的I/O虚拟化软件,可以对一个完整的计算机物理层环境进行虚拟化(如网卡、硬盘等)。QEMU原本并不是KVM的一部分,但其代码包含一套完整的虚拟化解决方案(处理机虚拟化、内存虚拟化、设备虚拟化),而被KVM所采用。
管理工具Libvirt
管理工具Libvirt:其设计目的是通过相同的方式管理不同的虚拟化引擎,如KVM、Xen、LXC等,是一种开源工具,主要由以下三部分功能组成:
(1)支持主流编程语言,如C、Python、Ruby等的API和库。
(2)Libvirtd服务。
(3)命令行工具virsh。
libvirt可以对虚拟机进行管理,包括虚拟机的创建、启动、关闭、挂起、恢复等,也可以对虚拟机的硬件设备进行添加和删除。
KVM相关软件的安装方法
检查宿主机BIOS设置
开启Inter VT-x/EPT或AMD-V/RVI(V)等,如果用vmware做实验,共有三项虚拟化引擎相关的配置可启用,除前面的一项,还有虚拟化CPU性能计数器(U),虚拟化IOMMU(IO内存管理单元)(I)。
安装VNC相关组件
yum -y install tigervnc tigervnc-server tigervnc-server-module
复制VNC配置模板文件
cp /lib/systemd/system/[email protected] /lib/systemd/system/vncserver@:1.service
可修改VNC配置文件
启动VNC服务
systemctl start vncserver@:1.service
systemctl enable vncserver@:1.service
防火墙开启5901端口
检查cpu是否支持虚拟化
egrep ‘(vmx|svm)’ /proc/cpuinfo
检查BIOS虚拟化
lsmod | grep kvm
以上有相关输出结果,则说明虚拟化开关已开启
加载KVM模块到宿主机系统中
Intel CPU使用:
modprobe kvm-intel
AMD CPU使用:
modprobe kvm-amd
sudo dnf install qemu-kvm virt-manager libvirt virt-install virt-viewer virt-top bridge-utils bridge-utils virt-top libguestfs-tools -y
暂略
安装Linux图形界面(也许可选)
yum groupinstall “X Window System”
创建网桥
首次创建虚拟机可以不自行创建网桥,使用默认网桥。
启动虚拟机管理界面(已安装图形界面的情况下)
virt-manager
接下来可安装配置各类虚拟机,就像esxi和vmware一样部署虚拟机。
启动 libvirtd 守护进程
systemctl start libvirtd
systemctl enable libvirtd
systemctl status libvirtd
设置桥接接口
nmcli connection show
创建网桥,首先,使用以下语法用其 UUID 删除连接(准备给客户机桥接的网卡):
nmcli connection delete UUID
创建一个新的桥接接口(也可以改用libvirtd默认网桥,即删除默认网桥再新建,或直接修改IP地址达到预期配置也行)
语法:
nmcli connection add type bridge autoconnect yes con-name BRIDGE NAME ifname BRIDGE NAME
实例:
nmcli connection add type bridge autoconnect yes con-name kvmbr0 ifname kvmbr0
配置桥接网卡
nmcli connection modify kvmbr0 ipv4.addr 192.168.130.85/24 ipv4.gateway 192.168.130.1 ipv4.method manual ipv4.dns 223.5.5.5,223.6.6.6
nmcli con reload kvmbr0
nmcli con up kvmbr0
添加网桥从属设备(把刚才删除UUID的宿主机网卡加入桥接网络来)
nmcli connection add type bridge-slave autoconnect yes con-name eth0 ifname eth0 master kvmbr0
查看网卡从属状态,可执行
ip a (其中master便是其主网卡)
或
brctl show
编辑网桥配置文件
vi /etc/qemu-kvm/bridge.conf
添加
allow all
重新启动虚拟化守护进程以应用更改
systemctl restart libvirtd
桥接网络受宿主机iptables的FORWARD链控制,在有docker环境情况下默认是DROP的,可以添加一条允许,或者更为严格的策略脚本。
详细参考脚本:/root/sh/iptables_for_kvm.sh
#!/bin/bash
###################################
# function iptables config for kvm
#
# create: touch /root/sh/iptables_for_kvm.sh; chmod 700 /root/sh/iptables_for_kvm.sh
#
# Change History:
# date author note
# 2012/08/31 N 创建
# 2013/07/04 N 为xen环境的iptables转发控制设计
# ……
# 2023/07/22 N 基于原xen环境的iptables转发控制,改编为适用于kvm
###################################
######### ENV #################
# 脚本名称,临时文件或邮件中可能用到,避免同类脚本的变量的冲突,下面直接取脚本名
export project_name=`echo ${0##*/} | cut -d'.' -f 1`
kvmbr_eth=kvmbr0
kvmbr_net=192.168.130.160/27 # 160-191
export LANG=C
export LC_ALL=C
export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
### filter table ###
## FORWARD chains ##
# 在默认配置中添加自定义链
iptables -F KVM_CUSTOM > /dev/null 2>&1
iptables -D FORWARD -j KVM_CUSTOM > /dev/null 2>&1
iptables -X KVM_CUSTOM > /dev/null 2>&1
iptables -N KVM_CUSTOM
iptables -I FORWARD -j KVM_CUSTOM
# 在自定义链中配置策略
# global
# iptables -A KVM_CUSTOM -j ACCEPT # allow all
iptables -A KVM_CUSTOM -d $kvmbr_net -o $kvmbr_eth -m state --state RELATED,ESTABLISHED -j ACCEPT
#iptables -A KVM_CUSTOM -s $kvmbr_net -i $kvmbr_eth -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A KVM_CUSTOM -s $kvmbr_net -i $kvmbr_eth -j ACCEPT
iptables -A KVM_CUSTOM -d $kvmbr_net -o $kvmbr_eth -p udp -m multiport --dport 53,5353,123,323 -j ACCEPT
iptables -A KVM_CUSTOM -p udp -m multiport --dport 67:68 -j ACCEPT
# 独立配置
iptables -A KVM_CUSTOM -s 0.0.0.0/0 -d 192.168.130.160 -p tcp -m multiport --dport 20,21,22 -j ACCEPT
# 记录将被阻止的连接
iptables -A KVM_CUSTOM -j LOG
### nat table ###
###### save ####################
iptables-save -c > /etc/sysconfig/iptables
iptables-save -c > /etc/sysconfig/iptables_$(date +%Y%m%d-%H%M)
例:Jul 22 15:42:51 vh0025 kernel: IN=kvmbr0 OUT=kvmbr0 PHYSIN=vnet3 PHYSOUT=eth0 MAC=ff:ff:ff:ff:ff:ff:52:54:00:e5:74:8f:08:00 SRC=0.0.0.0 DST=255.255.255.255 LEN=325 TOS=0x00 PREC=0xC0 TTL=64 ID=0 PROTO=UDP SPT=68 DPT=67 LEN=305
MAC前面ff:ff:ff:ff:ff:ff部分是广播地址,是源地址,后面是目标地址,08:00可能是保留用的,暂不用关心。
virt-install
–name kvm0001-Ubuntu20.04
–memory memory=4096,currentMemory=2048
–vcpus 1,maxvcpus=4,cpuset=1
–disk path=/var/lib/libvirt/images/ubuntu-20.04.qcow2,size=15,format=qcow2,bus=virtio,sparse=true
–os-variant ubuntu20.04
–hvm
–network bridge=kvmbr0,model=virtio
–graphics vnc,listen=0.0.0.0,port=5901,password=kvmpass
–console pty,target_type=serial
–cdrom /opt/isos/ubuntu-22.04.2-live-server-amd64.iso
一般选项
-n NAME, --name=NAME:虚拟机名称,需全局唯一
-r memory, --ram=MEMORY:虚拟机内存大小,单位MB
–vcpus=CPU: CPU模式及特性,如coreduo等;可以使用 qemu-kvm -cpu 来获取支持CPU模式
安装方法
-c CDROM, --cdrom=CDROM: 光盘安装介质
-l LOCATION, --location=LOCATION: 安装源URL,支持FTP、HTTP及NFS等
–pxe: 基于PXE完成安装
–livecd: 把光盘当做LiveCD
–os-type=DISTRO_TYPE: 操作系统类,如linux,unix,windows等
–os-variant=DISTRO_VARIANT: 某类型操作系统的变体,如rhel5, fedora8 可以通过"osinfo-query os"命令查看,该命令是libosinfo包的,A library for managing OS information for virtualization
-x EXTRA, --extra-args=EXTRA: 根据–location指定的方式安装GuestOS时,
用于传递给内核的额外选项,例如指定kickstart文件的位置 --extra-args “ks=http://172.16.0.1/class.cfg”
–boot=BOOTIOTS: 指定安装过程完成后的配置选项,如指定引导设备次序,使用指定的而非安装的kernel/initrd来引导系统启动等
–boot cdrom,hd,network: 指定引导次序
–boot kernel=KERNEL,initrd=INITRD,kernel_args=“console=/dev/ttyS0”: 指定启动系统的内核及initrd文件
存储配置
–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等,阿里云镜像导入支持RAW、QCOW2、VHD等格式,推荐传输内容更小的QCOW2或者VHD格式,如果考虑和vmware之间迁移,可采用vmdk格式
sparse: 磁盘镜像使用稀疏格式,即不立即分配指定大小的空间,实践中发现,仅采用sparse参数时,其值默认是false,应明确指定,sparse=true。查看实际大小时,用du -sh,而不要用ls -lh
–nodisks: 不使用本地磁盘,在LiveCD模式中常用
网络配置
-w NETWORK, --network=NETWORK opt1=val1, opt2=val2: 将虚拟机连入宿主机的网络中,其中NETWOR值可以为:
bridge=BRIDGE: 连接到名为"BRIDEG"的桥设备
network=NAME: 连接到名为"NAME"网络
其他常用的选项
model: GuestOS中看到的网络设备型号,ru e1000, rtl8139或virtio等
mac: 固定的MAC地址;省略此选项时将使用随机地址,但无论何种方式,对于KVM来说,其前三段必须时52:54:00
–nonetworks: 虚拟机不使用网络功能
图形配置
–graphics TYPE, opt1=val1,opt2=val2: 指定图形显示相关配置,此选项不会配置任何显示硬件(如显卡),而是仅仅指定虚拟机启动后对其进行访问的接口
TYPE: 指定显示类型,可以是vnc, sdl, spice, none,默认vnc
port: TYPE为vnc或spice时其监听的端口
listen: TYPE为vnc或spice时所监听的IP地址,默认为127.0.0.1,可以通过修改/etc/libvirt/qemu.conf定义新的默认值
password: TYPE为vnc或spice时,为远程访问监听的服务进行指定认证密码
–noautoconsole: 禁止自动连接到虚拟机的控制台
设备选项
–serial=CHAROPTS: 附加一个串行设备至当前虚拟机,根据设备类型的不同,可以使用不同的选项,格式为"–serial type, opt1=val1, opt2=val2"
例如:
–serial pty:创建伪终端;
–serial dev,path=HOSTPATH:附加主机设备至此虚拟机;
–video=VIDEO:指定显卡设备模型,可用取值为cirrus、vga、qxl或vmvga;
虚拟化平台
-v, --hvm:当物理机同时支持完全虚拟化和半虚拟化时,指定使用完全虚拟化;
-p, --paravirt:指定使用半虚拟化;
–virt-type:使用的hypervisor,如kvm、qemu、xen等;所有可用值可以使用’virsh capabilities’命令获取;
其它:
–autostart:指定虚拟机是否在物理启动后自动启动;
–print-xml:如果虚拟机不需要安装过程(–import、–boot),则显示生成的XML而不是创建此虚拟机;默认情况下,此选项仍会创建磁盘映像;
–force:禁止命令进入交互式模式,如果有需要回答yes或no选项,则自动回答为yes;
–dry-run:执行创建虚拟机的整个过程,但不真正创建虚拟机、改变主机上的设备配置信息及将其创建的需求通知给libvirt;
-d, --debug:显示debug信息;
进入系统后,检查/det/default/grub配置文件是否有异常。
经大佬排查,是grub中配置的console参数只配置了ttyS0,导致控制台所有的输出都去了串口,而没有在tty0(vnc)上显示,所以在引起系统启动后,vnc就看不到任何输出了。所以对于console参数至少需要配置一个tty0,ttyS0根据需求可选。
修改后,对于ubuntu系统可以执行 update-grub2 命令更新一下grub相关文件,之后重启测试,发现可以通过引导界面进入虚机。
可是按上面的操作后,情况依旧,
分析:推测是卡在grub倒计时,但是又没有显示,修改倒计时后,果然有变化,不过是“booting from hard disk”在倒计时后消失了,直接变成黑屏,还是要等很久。
有网友添加“–virt-type=qemu”的,实践发现,问题依旧,且这已经不是kvm了。
考虑到该问题不明显,暂时搁置。
安装客户端过程中,发现获取不到IP,网络也不通,查看宿主机防火墙iptables,有关于默认桥接网卡virbr0的配置,但没有自定义桥接网卡的配置。
(想不到libvirtd即使在rhel9上仍然利用的iptables而非firewalld,iptables的FORWARD在有docker环境情况下默认是DROP的,/etc/qemu-kvm/bridge.conf的配置似乎和iptables也没有直接关系。所以在非必要情况下,仍把iptables做为唯一防火墙配置仍然是个不错的选择)
解决办法:改用virbr0做为默认网桥(默认是用户网络,即NAT模式),或为自定义网桥添加iptables的FORWARD链策略。实践中选择了后者,详细参考脚本:/root/sh/iptables_for_kvm.sh
ubuntu20.04最小化安装完成后,最后提示是:final system configuration 或 restoring apt configuration
完整日志是:finish cmd-system-install: success: curtin command system-install
重启即可。
列出所有的虚拟机
virsh list --all
虚拟机改名
实践例:
cd /etc/libvirt/qemu
关机,导出xml virsh dumpxml kvm0001-Ubuntu20.04 > kvm0001-ubuntu20.04.xml
修改磁盘文件名(可选) cd /var/lib/libvirt/images
修改xml配置中的名称和磁盘文件名 vi kvm0001-ubuntu20.04.xml
删除老的 virsh undefine kvm0001-Ubuntu20.04
定义新的 virsh define /etc/libvirt/qemu/kvm0001-ubuntu20.04.xml
查看结果 virsh list --all
经过如上方法重命名的kvm机直接启动使用即可,不会影响udev动态管理设备的文件。和之前的虚拟机相比、仅仅就是名字变了而已。
显示虚拟机信息
virsh dominfo kvm0001
显示虚拟机内存和cpu的使用情况
yum install virt-top -y
virt-top
显示虚拟机分区信息
virt-df kvm0001
关闭虚拟机(shutodwn)
virsh shutdown kvm0001
强制关闭KVM虚拟机
virsh destroy kvm0001
启动虚拟机
virsh start kvm0001
设置虚拟机(kvm0001)跟随系统自启
virsh autostart kvm0001
关闭虚拟及自启
virsh autostart --disable kvm0001
删除虚拟机
virsh undefine kvm0001
通过控制窗口登录虚拟机
virsh console kvm0001
脚本:
#!/usr/bin/env bash
set -x
set -e
sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200"/g' /etc/default/grub
sed -i 's/^#GRUB_TERMINAL.*/GRUB_TERMINAL="console serial"/g' /etc/default/grub
sed -i '/GRUB_TERMINAL/aGRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"' /etc/default/grub
cat /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg
reboot
通过命令行更改创建之后虚拟机的内存,cpu等信息
1.查看虚拟机当前内存
[root@sh-kvm0001 ~]# virsh dominfo kvm0001 | grep memory
Max memory: 4194304 KiB
Used memory: 4194304 KiB
2.动态设置内存为512MB,内存减少
virsh setmem kvm0001 524288
#注意单位必须是KB,且不能超过最大内存限定,要超过的话,只能停止后修改
3.查看内存变化
#virsh dominfo kvm0001 | grep memory
Max memory: 14194304 KiB
Used memory: 524288 kiB
4.最大内存增加
virsh shutdown kvm0001
virsh edit kvm0001 # 直接更改memory
virsh create /etc/libvirt/qemu/kvm0001/xml
或 virsh setmaxmem kvm0001 4096M --config
#之后操作1,2,3步骤增加内存
需要修改配置文件,因此需要停止虚拟机
virsh shutdown kvm0001
virsh edit kvm0001
# 2 # 4 > 2
virsh create /etc/libvirt/qemu/kvm0001/xml
暂略
virsh edit kvm0001
第一步,停掉虚拟机
virsh shutdown kvm0001
第二步
virsh destroy kvm0001
第三步
virsh undefine kvm0001
第四部
rm /dev/vg_shkvm1/kvm0001 # 不建议删除硬盘
KVM 客户机网络连接有两种方式:
用户网络(User Networking)(NAT方式):让虚拟机访问主机、互联网或本地网络上的资源的简单方法,但是不能从网络或其他的客户机访问客户机,性能上也需要大的折扣。
虚拟网桥(Virtual Bridge)(Bridge方式):这种方式要比用户网络复杂一些,但是设置好后客户机与互联网,客户机与主机之间的通信都很容易。
virsh net-destroy default
virsh net-undefine default
service libvirtd restart 或 systemctl restart libvirtd
brctl delif kvmbr0 # 未成功
nmcli connection del kvmbr0
Bridge方式配置出来的接口对NAT方式没有影响,因为NAT方式并没有使用物理网卡。但作为客户机,每张网卡只能选择其中的一种。
我们只能对磁盘格式为 Qcow2 的虚拟机的进行快照,并且 kvm 的 virsh 命令不支持 raw 磁盘格式,可使用以下命令将原始磁盘格式转换为 qcow2。
qemu-img convert -f raw -O qcow2 image-name.img image-name.qcow2
创建快照语法:
virsh snapshot-create-as --domain {vm_name} --name {snapshot_name} --description “enter description here”
实例:
[root@kvm-hypervisor ~]# virsh snapshot-create-as --domain kvm0001-ubuntu20.04 --name kvm0001-ubuntu20.04_snap --description “snap”
Domain snapshot webserver_snap created
查看快照
virsh snapshot-list kvm0001-ubuntu20.04
要列出虚拟机快照的详细信息
virsh snapshot-info --domain kvm0001-ubuntu20.04 --snapshotname kvm0001-ubuntu20.04_snap
查看快照的大小(关机之后才能看)
qemu-img info /var/lib/libvirt/images/kvm0001-ubuntu-20.04.qcow2
还原 KVM 虚拟机快照
语法:virsh snapshot-revert {vm_name} {snapshot_name}
实践
[root@kvm-hypervisor ~]# virsh snapshot-revert kvm0001-ubuntu20.04 kvm0001-ubuntu20.04_snap
删除 KVM 虚拟机指定的快照
virsh snapshot-delete --domain kvm0001-ubuntu20.04 --snapshotname kvm0001-ubuntu20.04_snap
直接克隆虚拟机
先关机
语法:virt-clone -o 原虚拟机 -n 新虚拟机 -f 新虚拟机存放的路径
实践:
virt-clone -o kvm0001-ubuntu20.04 -n kvm0003-ubuntu20.04 -f /var/lib/libvirt/images/kvm0003-ubuntu-20.04.qcow2
完成
实践发现,vnc端口,mac地址都会自动变,但主机名和IP地址需要手动去改。