服务器架设篇 - RockyLinux 9

第二章、第一个虚拟机的安装与调整

KVM host 搞定之后,再来就是要建置 VM 啦!VM 安装妥当,也需要调校性能一下喔!

最近更新时间: 2023/08/04

要让 KVM host 顺利的启动多部 VM 还是需要其他许多额外的软件支持才行,同时也需要可以安装 VM 的映像档等数据。 那么这些数据应该放在何处比较好呢?以前 SELinux 是有规范的,不过目前似乎取消了这样的规范,所以只要有空间,似乎都可以放置 image 与硬盘档。这一章就让我们来安装第一部 VM 系统吧!未来都用这部系统来增加或减少其他任务啰!另外, 安装完毕后,得要来重新调整硬件资源设置档啦!让性能更佳化比较好!

2.1、第一个虚拟机的建置

上一章节将基础的 KVM host 系统建置妥当之后,接下来就准备要创建第一部 VM 系统了。 我们不用原生的 qemu-kvm 指令来创建 VM,而是使用具有监督功能的 libvirtd 环境,搭配 qemu 相关周边硬件仿真功能, 来设置与创建相关的各项虚拟硬件。创建完成之后,我们就可以通过这台虚拟机,实际安装另一套 Linux 啰! 也就是在 Linux 上面产生另一个逻辑上面完全独立的 Linux 系统!

2.1.1、KVM host 上面的 libvirtd 与 virsh

从上一章节我们就知道,KVM host 应该是要安装 libvirtd 以及 qemu 的!现在就来看看到底安装好了没? 此外,也来看看到底我们的内核驱动的 kvm 是给 intel 用的,还是给 AMD 用的?

  • Linux 内核 kvm 功能与参数

前面讲到,KVM 其实是 Linux 内核的一部分,那么 kvm 模块到底有没有加载在目前的内核呢? 此外,KVM 有没有额外的参数可以修改呢?让我们来瞧一瞧:

# 先看看目前的内核有没有加载 kvm 的相关模块了?
[root@cloud ~]# lsmod | grep kvm
kvm_intel             364544  0
kvm                  1056768  1 kvm_intel
irqbypass              16384  1 kvm
# 有喔!有 kvm 以及 kvm_intel 这两个已经被加载的模块!

[root@cloud ~]# modinfo kvm
filename:       /lib/modules/5.14.0-284.18.1.el9_2.x86_64/kernel/arch/x86/kvm/kvm.ko.xz
license:        GPL
author:         Qumranet
rhelversion:    9.2
srcversion:     46781597FA45B5FA677EBFF
depends:        irqbypass
retpoline:      Y
intree:         Y
name:           kvm
vermagic:       5.14.0-284.18.1.el9_2.x86_64 SMP preempt mod_unload modversions
....
parm:           tdp_mmu:bool
parm:           mmio_caching:bool
parm:           nx_huge_pages:bool
....

[root@cloud ~]# modinfo kvm_intel
filename:       /lib/modules/5.14.0-284.18.1.el9_2.x86_64/kernel/arch/x86/kvm/kvm-intel.ko.xz
license:        GPL
author:         Qumranet
rhelversion:    9.2
srcversion:     D8E06AE51DCBA93DB578DF9
alias:          cpu:type:x86,ven*fam*mod*:feature:*0085*
depends:        kvm
retpoline:      Y
intree:         Y
name:           kvm_intel
vermagic:       5.14.0-284.18.1.el9_2.x86_64 SMP preempt mod_unload modversions
....
parm:           enable_apicv:bool
parm:           enable_ipiv:bool
parm:           nested:bool
parm:           pml:bool
....

你可以看到,目前已经有两个 kvm 关键字的模块加载了,通过 modinfo 也可以看到模块的相关信息。 在模块信息后面的 parm 就是可以修改的模块参数!举例来说, kvm_intel 有个 nested 的参数, 这个参数可以是启动 (1) 也可以是关闭 (0)。如果我们需要修改这个参数,那需要处理 /etc/modprobe.d 里面的文件! qemu-kvm-common 有提供名为 /etc/modprobe.d/kvm.conf 的设置档,你可以看一下:

# 修改 kvm.conf 之后,卸载再挂载 kvm_intel 看看:
[root@cloud ~]# vim /etc/modprobe.d/kvm.conf
# For Intel
options kvm_intel nested=1

[root@cloud ~]# modprobe -r kvm_intel
[root@cloud ~]# modprobe kvm_intel
[root@cloud ~]# cat /sys/module/kvm_intel/parameters/nested
Y
# 确实修订为 Y 了!

因为鸟哥的系统上面,目前尚未运行任何 VM 啊!所以 kvm_intel 模块可以随意抽换没问题! 如果已经有 VM 在你的系统上面跑,那要启动 kvm_intel 的参数,恐怕就得要重新 reboot 才行! 另外,如果想要确认设置值内容,可以到『/sys/module/模块名称/parameters/参数名称』查找一下!

你可能会好奇,为什么鸟哥特别拿 kvm_intel 这个 nested 参数做介绍? 这是因为,这个参数可以『让你的虚拟机里面再打开虚拟机』!所以才说是嵌套 (nested) 功能啊! 相当有趣喔!鸟哥用高内存容量的实体机器做学生训练的环境时,就会用到这东西了!

根据上面的数据看起来,我们目前的 Linux 内核确实已经支持 kvm 了!同时我们也知道如何调整内核参数, 接下来看一下其他的虚拟化服务与软件吧!

  • libvirtd 服务观察与 virsh 简易操作

libvirtd 提供的服务相当多!目前我们大多是『有用到才启动 libvirtd 服务』的概念,因此, 主要启动的服务就会是『 libvirtd.socket 』才对!无论如何,都用观察的方式来检查一下即可:

# 看看 libvirtd 有没有启动!?
[root@cloud ~]# systemctl status libvirtd.socket
○ libvirtd.socket - Libvirt local socket
     Loaded: loaded (/usr/lib/systemd/system/libvirtd.socket; enabled; preset: disabled)
     Active: inactive (dead)
   Triggers: ● libvirtd.service
     Listen: /run/libvirt/libvirt-sock (Stream)
# 特别注意,观察的是 libvirtd.socket 而不是 libvirtd.service 喔!
# 很奇怪的是,在某些时刻,服务就是启动不了!没关系,我们手动来重新启动它!

[root@cloud ~]# systemctl start libvirtd.socket

基本上,就是确定有激活就好!因为我们没有图形界面,因此,重点应该是在 virsh 这个指令的操作喔! 这个 virsh 就是 libvirtd 用来管理 VM 的工具软件!你可以使用『 virsh --help 』查看一下 virsh 的用法! 他的功能多到会让人头晕...所以鸟哥这里不打算仔细介绍 virsh 的详细用法~只介绍几个鸟哥常用的指令。 首先,让我们来分析一下, libvirtd 如果创建一台 VM 时,它最佳的硬件设置会是怎样?

# 检查本机的相关虚拟化能力
[root@cloud ~]# virsh capabilities
<capabilities>

  <host>   <!--跟本机 CPU 较为相关的参数部份-->
    <uuid>f113f623-9a64-7d4e-ae37-184224474e59</uuid>
    <cpu>
      <arch>x86_64</arch>
      <model>Skylake-Client-noTSX-IBRS</model>
      <vendor>Intel</vendor>
      <microcode version='244'/>
      <signature family='6' model='165' stepping='3'/>
      <counter name='tsc' frequency='3095999000' scaling='no'/>
      <topology sockets='1' dies='1' cores='6' threads='2'/>
      <maxphysaddr mode='emulate' bits='39'/>
      <feature name='ds'/>
      ....
    </cpu>
    ....
  </host>  <!--结束跟本机 CPU 较为相关的参数部份-->

  <guest>  <!--跟用户端 (Guest) 相关性较高的数据,这是 32 比特部份-->
    <os_type>hvm</os_type>
    <arch name='i686'>
      <wordsize>32</wordsize>
      <emulator>/usr/libexec/qemu-kvm</emulator>
      <machine maxCpus='240' deprecated='yes'>pc-i440fx-rhel7.6.0</machine>
      <machine canonical='pc-i440fx-rhel7.6.0' maxCpus='240' deprecated='yes'>pc</machine>
      ....
      <domain type='qemu'/>
      <domain type='kvm'/>
    </arch>
    ....
  </guest>

  <guest>  <!--跟用户端 (Guest) 相关性较高的数据,这是 64 比特部份-->
    <os_type>hvm</os_type>
    <arch name='x86_64'>
      <wordsize>64</wordsize>
      <emulator>/usr/libexec/qemu-kvm</emulator>
      <machine maxCpus='240' deprecated='yes'>pc-i440fx-rhel7.6.0</machine>
      <machine canonical='pc-i440fx-rhel7.6.0' maxCpus='240' deprecated='yes'>pc</machine>
      ....
      <machine maxCpus='710'>pc-q35-rhel9.2.0</machine>
      <machine canonical='pc-q35-rhel9.2.0' maxCpus='710'>q35</machine>
      ....
      <domain type='qemu'/>
      <domain type='kvm'/>
    </arch>
    ....
  </guest>

</capabilities>

从上面的表格,我们可以看到主要的 CPU 型号、参数等,你可以将上面的参数表出在之后的硬件设置档上面。 而在 <guest> 的项目中,我们也可以查到目前支持的虚拟机芯片组型号!一般缺省的硬件是较旧的 pc-i440fx 芯片, 但是新的应该使用 q35 比较好!你的虚拟机有没有支持相关的芯片,可以在这里看出来喔!

# 检查支持的 q35 芯片组,内部硬件仿真为何
[root@cloud ~]# virsh domcapabilities --machine q35 --arch x86_64
<domainCapabilities>
  <path>/usr/libexec/qemu-kvm</path>   <== KVM 主程序的路径
  <domain>kvm</domain>
  <machine>pc-q35-rhel9.2.0</machine>
  <arch>x86_64</arch>
  <vcpu max='710'/>
  <iothreads supported='yes'/>
  <os supported='yes'>
    <enum name='firmware'>
      <value>efi</value>   <==主要使用 EFI 的开机引导方式
    </enum>
    <loader supported='yes'>
      <value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value>  <== EFI 引导程序放置位置
      ....
    </loader>
  </os>
  <cpu>
    <mode name='host-passthrough' supported='yes'>  <==支持直接使用本机CPU
      <enum name='hostPassthroughMigratable'>
        <value>on</value>
        <value>off</value>
      </enum>
    </mode>
    <mode name='maximum' supported='yes'>
      <enum name='maximumMigratable'>
        <value>on</value>
        <value>off</value>
      </enum>
    </mode>
    <mode name='host-model' supported='yes'>
      <model fallback='forbid'>Skylake-Client-IBRS</model>
      <vendor>Intel</vendor>
      <feature policy='require' name='ss'/>
      ....
    </mode>
    <mode name='custom' supported='yes'>  <==有支持的其他仿真的 CPU 类型
      <model usable='yes' deprecated='yes' vendor='unknown'>qemu64</model>
      <model usable='yes' deprecated='yes' vendor='unknown'>qemu32</model>
      <model usable='no' deprecated='yes' vendor='AMD'>phenom</model>
      ....
    </mode>
  </cpu>
  ....
  <devices>  <==其他支持的周边设备
    <disk supported='yes'>  <==针对硬盘的支持类型
      <enum name='diskDevice'>
        <value>disk</value>
        <value>cdrom</value>
        <value>floppy</value>
        <value>lun</value>
      </enum>
      <enum name='bus'>     <==针对总线的支持类型
        <value>fdc</value>
        <value>scsi</value>
        <value>virtio</value>
        <value>usb</value>
        <value>sata</value>
      </enum>
    </disk>
    <graphics supported='yes'> <==针对图形显示的支持类型
      <enum name='type'>
        <value>vnc</value>
        <value>egl-headless</value>
      </enum>
    </graphics>
    <video supported='yes'>
      <enum name='modelType'>
        <value>vga</value>
        <value>cirrus</value>
        <value>virtio</value>
        <value>none</value>
        <value>bochs</value>
        <value>ramfb</value>
      </enum>
    </video>
  </devices>
  ....
</domainCapabilities>

其实仔细检查一下可以让用户端使用的仿真出来的硬件,感觉还是挺有趣的!目前虚拟化的环境中, 比较常用的是 virtio 这一个模块,在磁盘、网络、显卡等方面,都可以通过这个模块来支持!性能比较好! 至于图形连接的方式,根据 Red Hat 官网建议,使用 VNC 即可,若有特殊需求,则建议使用第三方软件来连接! 总之,通过这个检查,我们可以知道本机的 KVM host 能力,也可以知道虚拟机未来的可用硬件就是了! 而且通过这个观察,建议需要注意的主要内容有:

  • 使用 q35 这个主板芯片组
  • 上述 q35 缺省使用 efi 的方式启动硬件
  • q35 的 efi 主要 ROM 文件在 /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd
  • 使用 host-passthrough 可以直接套用本机 CPU 的效果
  • 大部分的周边可以的话,都使用 virtio 模块较佳!
  • 显卡的部份,可以使用 virtio/cirrus 搭配 VNC 看看。
鸟哥比较喜欢使用 spice 这个连接机制,搭配 qxl 虚拟显卡~不过,似乎因为是某些授权的问题, 导致 Red Hat 的虚拟化团队取消了 spice 与 qxl 虚拟显卡的支持了!详情请查阅底下的链接:
实做例题:
虚拟机用到的 host 资源当中,最重要的可能就是 CPU 与内存。请自行使用『 virsh sysinfo 』查找一下自己的主机环境, 看看自己的实体机器 CPU 与内存相关资源参数!

2.1.2、qemu 设置网络与磁盘

虽然说虚拟机使用的都是实体机器上面的硬件资源,但是某些组件还是得要『真实存在』才可以! 两种硬件在实体机器上面应该是得要被看见才行!那就是硬盘与网络卡。硬盘很容易处理,硬盘就是创建一个虚拟磁盘文件即可。 网络卡就比较特别!你必须要在实体机器上面先创建一张可以被『桥接』的网络界面,这样虚拟机才能通过这张桥接网卡, 实际的连接到互联网上,这也是为啥 libvirtd 缺省会给一张名为 default 的桥接界面的原因。(不过不巧的,我们之前删除了! 删除的原因是...鸟哥不喜欢别人控制我的设备..)。

另外,在我们这份文档中,跟 KVM 有关的数据,主要分布在三个基础子目录,方便未来的查找与管理:

  • /kvm/xml:主要放置各种 XML 规范的设置档,包括网络、虚拟机的设置等等
  • /kvm/iso:主要放置来自网络上的各种 ISO 映像档,例如 9.0, 9.2 的 RockyLinux 安装光盘档
  • /kvm/img:虚拟磁盘的放置目录,基本上,占用的文件系统容量最大!
  • 用 qemu-img 创建虚拟磁盘文件

libvirtd 支持的虚拟磁盘包括实体磁盘、LVM 实体设备、大型文件等等。使用实际的设备 (实际磁盘设备、LVM 设备等) 在性能上,应该是比较好的。不过在系统的移转与便利性上面,可能就有比较大的限制。因此,使用在虚拟机当中的磁盘, 大部分还是以虚拟磁盘文件较常见。

那么虚拟磁盘文件是怎么样的一个存在呢?举个例子来说,如果你需要一个 100G 的虚拟磁盘,你是不是就一定要提供一个 100G 容量大小的文件来给虚拟机使用?有没有『用多少、算多少』的虚拟磁盘类型呢?基本上,常见的就是这两种类型啦!

  • raw:预先给予固定的文件容量,如上所述,你得要创建一个 100G 的文件才行。
  • qcow2:一种 qemu copy on write 的虚拟磁盘文件格式,可以指定虚拟磁盘的容量,但是该容量占用的实际磁盘空间, 则是用多少算多少。

假设我一个虚拟磁盘里面只丢了 5G 的容量,在 raw 格式上,还是占用了 100G 的空间,但是在 qcow2 格式上, 就只会占用 5G 而已!所以,用容量使用效率来说,当然是使用 qcow2 比较好啊!而在读写性能上,因为 raw 是保有既有容量, 所以读写性能最佳!然而 qcow2 的性能也逼近 raw 了!因此,强烈建议直接使用 qcow2 虚拟磁盘格式即可!

创建虚拟磁盘可以使用 qemu-img 来处理!详细的指令用法请参考 qemu-img --help 或相关的 man page。 底下仅就创建与观察来使用 qemu-img!

# 创建一个 30G 的虚拟磁盘,名为 demo1.img
[root@cloud ~]# qemu-img create -f qcow2 -o cluster_size=[512,1K..2M] file.img size

[root@cloud ~]# mkdir /kvm/img
[root@cloud ~]# cd /kvm/img
[root@cloud img]# qemu-img create -f qcow2 -o cluster_size=2M demo1.img 30G
Formatting 'demo1.img', fmt=qcow2 cluster_size=2097152 extended_l2=off compression_type=zlib
  size=32212254720 lazy_refcounts=off refcount_bits=16

鸟哥喜欢将区块 (cluster_size) 设置大一些,听说这样会有些许的性能提升!如果你不喜欢,可以直接省略这个参数即可。 demo1.img 就是虚拟磁盘文件,通常扩展名习惯取 .img 就是了。至于容量当然就是数字加上单位了!例如 1T 也是可接受的。 接下来,让我们来看看这个文件的相关规格:

[root@cloud img]# ll -h demo1.img
-rw-r--r--. 1 root root 6.1M Aug  4 11:28 demo1.img

[root@cloud img]# file demo1.img
demo1.img: QEMU QCOW2 Image (v3), 32212254720 bytes

[root@cloud img]# qemu-img info demo1.img
image: demo1.img
file format: qcow2
virtual size: 30 GiB (32212254720 bytes)
disk size: 6 MiB
cluster_size: 2097152
Format specific information:
    compat: 1.1
    compression type: zlib
    lazy refcounts: false
    refcount bits: 16
    corrupt: false
    extended l2: false
Child node '/file':
    filename: demo1.img
    protocol type: file
    file length: 6 MiB (6291968 bytes)
    disk size: 6 MiB
    Format specific information:
        extent size hint: 1048576

使用 file 可以看一下该文件是哪种格式的数据,然后通过 qemu-img info file.img 的方式来查看虚拟磁盘的总容量! 一目了然!虚拟磁盘容量有 30G,但是实际上只占用了 6M 左右的实际容量而已!这就是 qcow2 虚拟磁盘的格式功能!

  • 创建具有 NAT 功能的桥接界面 templan

鸟哥不是很爱使用 qemu 的网络,因为 qemu 的网络让 libvirtd 管理之后,由于太强大了! 接管了许多实体机器的端口口与防火墙,这让喜欢简单操控系统的鸟哥觉得很讨厌~不过,我们尚未谈到网络基础以及防火墙, 但是目前又需要创建一个桥接网卡,这样才能让虚拟机直接连接到外部去!唉~真是没办法!所以,只好再次创建一个桥接界面。 只是,这个桥接界面会在我们讲完网络基础后,就直接删除喔!所以鸟哥才取名为 templan 啦!

# 创建一个名为 templan 的网络界面
[root@cloud img]# mkdir /kvm/xml
[root@cloud img]# cd /kvm/xml
[root@cloud xml]# vim templan.xml
<network>
  <name>templan</name>
  <forward mode='nat'/>
  <bridge name='templan' stp='on' delay='0'/>
  <mac address='52:54:00:00:ff:01'/>
  <ip address='192.168.10.254' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.10.1' end='192.168.10.100'/>
    </dhcp>
  </ip>
</network>
  • name: libvirtd 管理这张网卡需要的名称
  • forward mode='nat': 虚拟机连上互联网的方式用 NAT(后面章节会提到)
  • bridge name='templan': 实体机器上面会生成一个名为 templan 的网络界面
  • mac address: 实体机器上面这张网络界面的卡号
  • ip address: 这张网卡的 IP 地址参数设置
  • dhcp...range..: 主动提供自动取得网络参数的服务 (dhcp 后面会提到)

创建妥当之后,我们就可以通过 virsh 来启动这张桥接卡了!

# virsh 管理网络的基本指令
[root@cloud xml]# virsh net-list
[root@cloud xml]# virsh net-create  file.xml  <==启动网络设置
[root@cloud xml]# virsh net-destroy file.xml  <==终止网络设置

# 实际创建桥接网络界面,并且观察创建后的网络参数
[root@cloud xml]# virsh net-create templan.xml
Network templan created from templan.xml

[root@cloud xml]# virsh net-list
 Name      State    Autostart   Persistent
--------------------------------------------
 templan   active   no          no

[root@cloud xml]# ip addr show
.....
4: templan: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:00:ff:01 brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.254/24 brd 192.168.10.255 scope global templan
       valid_lft forever preferred_lft forever

这样就搞定虚拟磁盘以及虚拟机需要的桥接网络界面了!接下来,让我们处理一下实际虚拟机的 XML 格式的硬件设置档!

2.1.3、虚拟机硬件设置档 (XML 格式)

我们可以从 virsh capabilities 以及 virsh domcapabilities 找到部份的虚拟机设备文件的硬件资源配置, 但是总是不直觉~有没有什么方法可以让 KVM host 直接产生一个虚拟机的设备 XML 设置档呢? 可以的,通过 virt-install 这个指令来产生即可!不过,要用这个指令之前,我们得要先处理好包括硬盘、网络卡、CDROM 映像档等等。 基本上,需要预先准备的数据大致有:

  • 经由 virsh capabilities 确认支持 q35 的虚拟机硬件
  • 经由 virsh domcapabilities 确认支持 efi 开机方式
  • 经由 virsh domcapabilities 确认支持 host-passthrough 功能
  • 光盘映像档放置成为: /kvm/iso/Rocky-9.0-x86_64-minimal.iso
  • 硬盘档放置在: /kvm/img/demo1.img
  • 桥接器名称为 templan

就开始让我们来玩玩使用 virt-install 创建一个 XML 的虚拟机硬件设置档吧!

# 安装 virt-install
[root@cloud ~]# yum -y install virt-install

# 开始使用 virt-install 规划一个用户端硬件资源
[root@cloud ~]# cd /kvm/xml
[root@cloud xml]# virt-install \
> --name demo1 --cpu host-passthrough,cache.mode=passthrough --vcpu 4 \
> --memory 2048 --memballoon virtio --machine q35 \
> --boot uefi,loader.type=pflash,nvram=/kvm/xml/demo1.uefi.fd,loader_secure=no \
> --controller virtio-scsi \
> --disk /kvm/img/demo1.img,cache=writeback,io=threads,device=disk,bus=virtio \
> --network network=templan,model=virtio \
> --graphics vnc,port=5911,listen=0.0.0.0,password=rocky9 \
> --cdrom /kvm/iso/Rocky-9.0-x86_64-minimal.iso --video virtio \
> --dry-run --print-xml > /kvm/xml/demo1.xml
# 详细的参数请参考 man virt-install。上述指令运行完毕,产生 demo1.xml 文件!

懒的打字的话,鸟哥也写文本档给大家下载~直接 wget 下载到你的系统上就可以了!

wget http://vbird.cn/linux_server/rocky9/0130vmtuning/virt-install.sh

设置档创建妥当后,很怪! XML 里面竟然会有两组设置!都是从 <domain...> 开始的项目! 因此,请先编辑该文件,让数据只存在一份,并且确认一下设置信息是否正确。

[root@cloud ~]# vim /kvm/xml/demo1.xml
<domain type="kvm">
  <name>demo1</name>
  <uuid>ad651202-0ff0-4214-88df-aa2fd0ce8611</uuid>
  <metadata>
    <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
      <libosinfo:os id="http://rockylinux.org/rocky/9"/>
    </libosinfo:libosinfo>
  </metadata>
  <memory>2097152</memory>
  <currentMemory>2097152</currentMemory>
  <vcpu>4</vcpu>
  <os>  <== 这一段前三行应该要修改一下喔!否则启动会失败!
    <type arch="x86_64" machine="q35">hvm</type>
    <loader readonly="yes" type="pflash" secure="no">/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</loader>
    <nvram>/kvm/xml/demo1.uefi.fd</nvram>
    <boot dev="cdrom"/>
    <boot dev="hd"/>
  </os>
  <features>
    <acpi/>
    <apic/>
  </features>
  <cpu mode="host-passthrough">
    <cache mode="passthrough"/>
  </cpu>
  <clock offset="utc">
    <timer name="rtc" tickpolicy="catchup"/>
    <timer name="pit" tickpolicy="delay"/>
    <timer name="hpet" present="no"/>
  </clock>
  <on_reboot>restart</on_reboot>  <== 建议增加这三行
  <on_poweroff>destroy</on_poweroff>
  <on_crash>restart</on_crash>
  <pm>
    <suspend-to-mem enabled="no"/>
    <suspend-to-disk enabled="no"/>
  </pm>
  <devices>
    <emulator>/usr/libexec/qemu-kvm</emulator>
    <disk type="file" device="disk"> <== 底下这两个是硬盘与光盘设计
      <driver name="qemu" type="qcow2" cache="writeback" io="threads"/>
      <source file="/kvm/img/demo1.img"/>
      <target dev="vda" bus="virtio"/>
    </disk>
    <disk type="file" device="cdrom">
      <driver name="qemu" type="raw"/>
      <source file="/kvm/iso/Rocky-9.0-x86_64-minimal.iso"/>
      <target dev="sda" bus="sata"/>
      <readonly/>
    </disk>
    <controller type="virtio-scsi"/> <== 这个可以拿掉
    <controller type="usb" model="qemu-xhci" ports="15"/>
    <controller type="pci" model="pcie-root"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <controller type="pci" model="pcie-root-port"/>
    <interface type="network">  <== 单纯观察一下网络卡界面
      <source network="templan"/>
      <mac address="52:54:00:ed:63:1f"/>
      <model type="virtio"/>
    </interface>
    <console type="pty"/>
    <channel type="unix">
      <source mode="bind"/>
      <target type="virtio" name="org.qemu.guest_agent.0"/>
    </channel>
    <input type="tablet" bus="usb"/>
    <tpm model="tpm-crb">
      <backend type="emulator"/>
    </tpm>
    <graphics type="vnc" port="5911" listen="0.0.0.0" passwd="rocky9"/> <== 显示界面与显卡
    <video>
      <model type="virtio"/>
    </video>
    <memballoon model="virtio"/>
    <rng model="virtio">
      <backend model="random">/dev/urandom</backend>
    </rng>
  </devices>
  <on_reboot>destroy</on_reboot>
</domain>

懒的打字的话,鸟哥也写文本档给大家下载~直接 wget 下载到你的系统上就可以了!

wget http://vbird.cn/linux_server/rocky9/0130vmtuning/demo1.xml

上述 XML 各项功能与数据,可以参考 libvirtd 提供的 XML 设置参考:

该删除的项目与该注意的项目如上,这样大致就创建好 q35 的 uefi 的虚拟机硬件配置环境!

2.1.4、虚拟机的启动、安装、连接与观察

上个小节的动作,等于是『采购一部主机』的概念,同时假装你已经将 RockyLinux 9.x 光盘片放置到光驱内! 另外, UEFI BIOS 的闪存,假装放置到 /kvm/xml/demo1.uefi.fd 纪录档~这个 UEFI 纪录档会自动产生! 那现在当然就是要按下虚拟机的『 power 』啰!如何观察呢?

# virsh 针对虚拟机启动、关闭、观察的指令
[root@cloud ~]# virsh create file.xml  <==从 xml 文件启动虚拟机
[root@cloud ~]# virsh list [--all]     <==列出目前 host 上面运作的虚拟机
[root@cloud ~]# virsh shutdown VMname  <==让某个名为 VMname 的虚拟机关机
[root@cloud ~]# virsh destroy VMname   <==强迫某个名为 VMname 的虚拟机断电
  • 使用 virt-viewer 提供的 remote-viewer 当作虚拟机的『屏幕』

虚拟机如果启动了,按下 power 了!那怎么取得虚拟机的屏幕画面?注意喔!底下的连接『感觉』像是在电脑主机前面操作系统喔! 而『不是远程连接』喔!这个概念要了解。所以,你可以说,我们就是将云里面的虚拟机拿到我们面前来操作! 因此,等等要介绍的 remote-viewer 就是『将虚拟机的屏幕拿到我面前来』的意思,而不是远程登录!

我们在虚拟机 XML 里面设置,缺省使用 VNC 协定连接『取得屏幕』,且开在 KVM host 的 port 5911, 假设你在外部使用具有 GUI 界面的环境,那么就能使用底下的方式作为取得屏幕的网址:

  • vnc://192.168.201.249:5911

至于 vnc 的连接软件,很多第三方软件可以使用!也可以使用 virt-viewer 提供的 remote-viewer 软件来处理!这个软件目前仅提供 Linux/Windows 使用, 相关 virt-viewer 的软件下载可参考底下链接:

上述链接在 windows 底下可以下载『 Win x64 MSI 』文件,如果是 Linux,直接安装 virt-manager 软件即可! 不过就是要注意,你的 Linux 或其他操作系统,一定要有 GUI 啊!现在,让我们来启动这个虚拟机, 然后赶紧链接上吧!因为只有 60 秒钟可以选择安装环境而已呢!

# 1. 在 KVM host 上面,你要先放行来自用户端的连接
[root@cloud ~]# firewall-cmd --permanent --add-port=5911/tcp
[root@cloud ~]# firewall-cmd --reload
[root@cloud ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eno1
  sources:
  services: cockpit dhcpv6-client ssh
  ports: 5911/tcp
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
# 后面章节谈到防火墙的时候,会重新回来介绍!现在这里先确认有 5911 存在即可!

# 立刻启动虚拟机 
[root@cloud ~]# virsh create /kvm/xml/demo1.xml
Domain 'demo1' created from /kvm/xml/demo1.xml

[root@cloud ~]# netstat -tlunp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 192.168.10.254:53       0.0.0.0:*               LISTEN      6105/dnsmasq
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1194/sshd: /usr/sbi
tcp        0      0 0.0.0.0:5911            0.0.0.0:*               LISTEN      6819/qemu-kvm
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      4803/master
tcp6       0      0 :::22                   :::*                    LISTEN      1194/sshd: /usr/sbi
tcp6       0      0 ::1:25                  :::*                    LISTEN      4803/master
udp        0      0 192.168.10.254:53       0.0.0.0:*                           6105/dnsmasq
udp        0      0 0.0.0.0:67              0.0.0.0:*                           6105/dnsmasq
udp        0      0 127.0.0.1:323           0.0.0.0:*                           1115/chronyd
udp6       0      0 ::1:323                 :::*                                1115/chronyd

接下来,启动你用户端的 remote-viewer 软件,并且填入上述的 vnc 网址,如下所示:

使用 VNC 连接 使用 VNC 连接
使用 VNC 连接
图 2.1.4、通过 remote-viewer 软件,取得虚拟机的屏幕画面

然后做什么呢?没做什么,就是按照正常的安装流程,一项一项安装好你的 VM 服务器即可。

  • 第一个虚拟机的安装

为了完整仿真 UEFI 的环境,因此我们使用了 q35 的虚拟机硬件设计,所以,安装时的磁盘分割,需要很注意! 同样需要保留大约 200M 给 /boot/efi 会比较好!你也可以指定 100M 即可!至于其他分割, 建议只须保留根目录即可,swap 大概只给 1G 就好!毕竟还是担心系统内存不足导致的当机问题。

  • 语言同样选择繁体中文台湾,或者是直接选择英文,自由选择即可
  • 语言支持,请同样增加另一个项目,保留繁体中文与美式英文两者
  • 时区请选择亚洲台北
  • 磁盘分割使用自订,使用标准分割,创建:
    • /boot/efi 大概 200M
    • swap 1G
    • / 其他容量都给这个
  • 关闭 kdump
  • 自行设置 root 密码与创建自己的专属一般帐号

然后就让系统开始自己安装起来!安装的过程中,我们在 KVM host 上面,可以使用底下的方式来观察看看这个 VM 的操作情况:

  • 观察虚拟机的运作 (在 KVM host 上面)

使用 virsh 就可以有很多观察的数据了:

# 看有哪些 VM 在运作
[root@cloud ~]# virsh list
 Id   Name    State
-----------------------
 2    demo1   running

# 看上面 demo1 的状态
[root@cloud ~]# virsh dominfo demo1
Id:             2
Name:           demo1
UUID:           4e8312e9-ef32-4edc-8dc0-651d7e13a51c
OS Type:        hvm
State:          running
CPU(s):         4
CPU time:       173.7s
Max memory:     2097152 KiB
Used memory:    2097152 KiB
Persistent:     no
Autostart:      disable
Managed save:   no
Security model: selinux
Security DOI:   0
Security label: system_u:system_r:svirt_t:s0:c145,c782 (enforcing)

# 看 demo1 的内存使用状态
[root@cloud ~]# virsh dommemstat demo1
actual 2097152
swap_in 0
swap_out 0
major_fault 226
minor_fault 302780
unused 1521280
available 1854924
usable 1558904
last_update 1658386440
disk_caches 188752
hugetlb_pgalloc 0
hugetlb_pgfail 0
rss 2139836
  • VM 运作过程当中,进行光盘片的抽换

安装好 rockylinux 之后,自然是需要重新开机的。这时,最好的方法当然是去 /kvm/xml/demo1.xml 里面, 将光驱的磁盘文件名注解掉,这样就不会每次开机都进入安装画面。如果不想要关闭这部 VM 时, 则可以通过底下的手段,将光盘退出即可!(注意,安装完毕准备开机前,才进行光盘片移除!)

# 看看目前 demo1 所使用到的磁盘方面的设备有哪些
[root@cloud ~]# virsh domblklist demo1
 Target   Source
-------------------------------------------------
 vda      /kvm/img/demo1.img
 sda      /kvm/iso/Rocky-9.0-x86_64-minimal.iso

# 当 VM 内的 RockyLinux 9.x 安装完毕后,在开机过程中,将光盘 sda 抽出不用
[root@cloud ~]# virsh change-media demo1 /kvm/iso/Rocky-9.0-x86_64-minimal.iso --eject
Successfully ejected media.

[root@cloud ~]# virsh domblklist demo1
 Target   Source
------------------------------
 vda      /kvm/img/demo1.img
 sda      -

此时由于没有光盘片的存在了,因此 VM 重新开机之后,就可以直接进入新的 rockylinux 的开机流程中啰!此外, 你最好也将 demo1.xml 内的光盘片移除!这样下次启动这个 VM 时,就不会使用这个光盘片开机了:

[root@cloud ~]# vim /kvm/xml/demo1.xml
    <disk type="file" device="cdrom">
      <driver name="qemu" type="raw"/>
      <source file=""/>
      <target dev="sda" bus="sata"/>
      <readonly/>
    </disk>

有时候,你的 VM 安装完毕之后,可能重新开机会失败...几率不高,但是就是可能会发生...这个时候, 似乎也只能将这个 VM 强制关机后在重新打开了!操作的方法有点像这样:

[root@cloud ~]# virsh destroy demo1
Domain 'demo1' destroyed

[root@cloud ~]# virsh create /kvm/xml/demo1.xml
Domain 'demo1' created from /kvm/xml/demo1.xml

2.1.5、虚拟机 guest OS 的后续整理

虚拟机内的操作系统我们称为 guest OS 就是了,这个 guest OK 可以操作的方式,除了通过 remote-viewer 直接取得『屏幕』的终端画面之外,事实上,也可以通过 ssh 之类的远程连接服务器进行连接,这样就不用使用 remote-viewer 了! 也能够在 KVM host 上面,直接转以 ssh 来处理即可。虽然我们可以在 host 上面使用类似 arp 的指令去找出来网卡卡号与 IP 的对应, 不过还是不太直觉~所以,还是先到 remote-viewer 去查找到正确的 IP 地址之后,再来后续的其他处置吧!

  • 使用 remote-viewer 界面,启动虚拟机网络

从 remote-viewer 的屏幕画面登录虚拟机后,变更身份成为 root,接下来,让我们来启动网络一下:

# 1. 先查阅一下网络卡的代号
[root@localhost ~]# ip addr show
....
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:ed:63:1f brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.52/24 brd 192.168.10.255 scope global dynamic noprefixroute enp1s0
       valid_lft 2857sec preferred_lft 2857sec
    inet6 fe80::5054:ff:feed:631f/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
# 网卡的代号为 enp1s0, 且 rockylinux 9 缺省会启动网络卡喔! 

# 2. 检查一下网络连接的名称为何
[root@localhost ~]# nmcli connection show
NAME    UUID                                  TYPE      DEVICE
enp1s0  0b413fa2-5192-3674-a542-323759ec20bc  ethernet  enp1s0
# 果然顺利找到正确的网卡代号!

[root@localhost ~]# nmcli connection show enp1s0 | grep IP4
IP4.ADDRESS[1]:     192.168.10.52/24
IP4.GATEWAY:        192.168.10.254
IP4.ROUTE[1]:       dst = 192.168.10.0/24, nh = 0.0.0.0, mt = 100
IP4.ROUTE[2]:       dst = 0.0.0.0/0, nh = 192.168.10.254, mt = 100
IP4.DNS[1]:         192.168.10.254
# 这样就可以看到我们这部主机的 IP 地址是 192.168.10.52 喔!

这样就可以暂时启动网络了!同时也能抓到虚拟机的 IP 地址哩。

  • 在 KVM host 观察找寻 VM 网络地址的方法

我们知道虚拟机使用的网络,其实是依附在实体机器上面的那个 templan 界面的! 如果 VM 真的有启动网络,那么该网络卡『应该就会纪录 VM 发送来的网卡纪录』才对! 这时,我们可以反向的,从实体机器的网卡卡号纪录功能,找到对应的 IP 地址喔!

# 这是在『实体机器』也就是在那部 KVM host 上面,实做 arp 探索!
[root@cloud ~]# arp -n
Address             HWtype  HWaddress           Flags Mask      Iface
192.168.201.252     ether   94:57:a5:9c:df:60   C               eno1
192.168.201.254     ether   18:31:bf:45:58:f2   C               eno1
192.168.10.52       ether   52:54:00:ed:63:1f   C               templan

[root@cloud ~]# arp -n -i templan
Address             HWtype  HWaddress           Flags Mask      Iface
192.168.10.52       ether   52:54:00:ed:63:1f   C               templan

通过 arp 的网卡卡号与该网卡上面的 IP 地址对应纪录,就可以找到用户端的 IP 地址了。 相关的 ARP 功能,我们会在后续的章节再跟大家讨论一下。现在,你大概知道一下,局域网路里面, 每张网络卡要沟通之前,都得要知道对方网卡的卡号所在就是了。

除了使用 arp 之外,从前一章学习到的 nmap 也能够用在这里了!

# 在 KVM host 上面,探索 192.168.10.0/24 上面的活动中的主机
[root@cloud ~]# nmap -sP 192.168.10.0/24
Starting Nmap 7.91 ( https://nmap.org ) at 2023-08-04 13:29 CST
Nmap scan report for 192.168.10.52
Host is up (0.00017s latency).
MAC Address: 52:54:00:ED:63:1F (QEMU virtual NIC)
Nmap scan report for 192.168.10.254
Host is up.
Nmap done: 256 IP addresses (2 hosts up) scanned in 2.41 seconds

为什么 nmap 会知道这张 192.168.10.52 的网卡是 QEMU 的虚拟网卡呢?原因是,每张网卡都会有个卡号, 该卡号的前两个字节 (bytes) 就是制造商的识别码啦!就这么单纯! ^_^

  • 从 KVM host 连接到 VM 进行操纵的方法: ssh

为什么我们要去启动 VM 的网络,并且还得要了解 VM 的 IP 地址呢?这是因为...鸟哥不喜欢启动太多的图形界面! 当然啦,使用 remote-viewer 连接,网络传输的数据量比较大,我也不喜欢!所以,想要使用纯文本的方式连接到 VM, 这样操作起来就简单很多!而使用纯文本登录的方法,大概就使用缺省的 ssh 最简单!

我们后面还会探索 ssh 这个服务,在这里,你先知道我们可以通过 ssh 以及 scp 进行登录或数据传输即可。 既然已经知道 VM 的 IP 是 192.168.10.52 了,让我们登录一下!

# ssh 的基本语法
[root@cloud ~]# ssh username@host.name.or.ip

# 在 KVM host 登录 VM 取得控制权
[root@cloud ~]# ssh vbird@192.168.10.52
The authenticity of host '192.168.10.52 (192.168.10.52)' can't be established.
ED25519 key fingerprint is SHA256:Va9Z79fb2Kv16vT9QP46iRksvxDayLYKcMY64n3txv8.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.10.52' (ED25519) to the list of known hosts.
vbird@192.168.10.52's password:  <==这里输入用户的密码
[vbird@localhost ~]$

第一次登录到这部 VM 时,ssh 会问你『要不要纪录这个主机的公钥码』,这里一定要写『 yes 』才行! 完整的 yes 喔!多一字、少一字都不行!哈哈哈!然后才是输入密码,如果密码正确, 你的画面帐号、主机名称就会修改!这时,你的操作行为就都在 VM 上面,而不是在 KVM host 上面喔! 很重要!

从现在开始,要进行各项操作之前,得要注意你的所在终端机是在那一个系统内喔! 因为我们会慢慢的掌握很多很多部主机,如果一个没有掌握好...那就真的伤脑筋了!哈哈!
  • 在虚拟机里面进行管理任务

事实上,一部系统安装妥当之后,许多任务都需要进行才行!包括升级、关闭网络服务、日期时间校正、 性能大致调整等等。即使是虚拟机,这些动作也不能忽视。现在,就让我们一步一步慢慢处理!

# 先进行全系统更新
[root@localhost ~]# yum -y update

# 常用软件安装
[root@localhost ~]# yum -y install bind-utils vim-enhanced \
> bash-completion net-tools yum-utils tuned

# 网络时间校正
[root@localhost ~]# vim /etc/chrony.conf
#pool 2.pool.ntp.org iburst
server ntp.ksu.edu.tw iburst
server tock.stdtime.gov.tw iburst
server time.stdtime.gov.tw iburst
#sourcedir /run/chrony-dhcp

[root@localhost ~]# systemctl start chronyd
[root@localhost ~]# systemctl enable chronyd
[root@localhost ~]# chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
^? dns3.ksu.edu.tw               3   6     3     0  +1028us[+1028us] +/-  122ms
^? 211-22-103-157.hinet-ip.>     2   6     3     1   -190us[ -190us] +/-   22ms
^? 118-163-81-61.hinet-ip.h>     2   6     3     1   +308us[ +308us] +/-   33ms

# tuned 性能调整
[root@localhost ~]# tuned-adm profile virtual-guest

# 管理员惯用操作环境处理
[root@localhost ~]# vim ~/.vimrc
"将自动缩进、自动移动画面的功能关闭!请自行处理!
setl noai nocin nosi inde=

# 一般帐号操作环境处理
[vbird@localhost ~]# vim ~/.vimrc  <==这里跟管理员相同动作
[vbird@localhost ~]# vim ~/.bashrc
....
alias rm='rm -i'
alias mv='mv -i'
alias cp='cp -i'

最后进行重新开机,重新开机时,你会被 VM 系统丢出来...同时,因为我们没有设计让 VM 自动激活网络, 这时再也无法直接以网络连接到 VM 系统了!请爱用 remote-viewer 连接啊!另外,如果真的没有其他要进行的任务, 那要关闭 VM 系统时,也可以在 KVM host 上面使用 virsh 指令关机即可。

# 在 KVM host 上面关闭 VM 系统
[root@cloud ~]# virsh list
 Id   Name    State
-----------------------
 3    demo1   running

[root@cloud ~]# virsh reboot demo1
Domain 'demo1' is being rebooted

[root@cloud ~]# virsh shutdown demo1
Domain 'demo1' is being shutdown

VM 的关机可能并不会成功!因为有的 VM 系统上面可能会有图形化界面正在操作中,该情境就不会让 virsh 直接关闭。 因此,下达 virsh shutdown 一段时间 (大部分只需要 1 分钟内),再次 virsh list 之后,没看到 demo1 继续跑, 那就是正确关闭 VM 系统了!

2.2、虚拟机重建暨性能测试与调整

前一章节,我们知道如何在 KVM host 里面安装一部虚拟机,包括虚拟机的 XML 硬件资源设置档、 虚拟机的 qemu 创建的 qcow2 虚拟磁盘格式、通过 qemu 创建的虚拟机网络桥接器,同时知道如何启动与观察虚拟机, 然后才在虚拟机里面安装了一套完整的 RockyLinux 系统!很赞!然后呢?

想一想,我们如何采购硬件?当然,在电脑教室里面,相同机型的桌机来几套,里面的硬件配备都一样, 这样比较好管理,而且最好里面的操作系统通通安装好了!那真是太棒了!好的!那么,我们如何通过刚刚上一小节的成果, 『拷贝』出一堆一模一样的桌机呢?此外,这个虚拟机到底好用不好用?能不能再进行性能调校呢? 就让我们继续看下去。

2.2.1、虚拟机的重制:通过 backing_file 副本

想一想,如果我们将上一小节的 XML 文件拷贝且稍微修改一下,然后将 qemu 映像档拷贝成不同文件名, 那不就可以创建新的虚拟机的嘛?没错喔!有这个想法真是很赞!不过...有没有更快速的方法? 因为那个 demo1.img 的虚拟磁盘映像档,其实安装好就已经是 2G 以上了!如果还有其他数据, 这个文件会越长越大~如果都使用拷贝,那会不会占用太多容量?

没错!很赞,你发现重点了!如果能够像 LVM 里面的快照 (snapshot) 一样,就是使用原始 LVM (original) 的数据而已, 快照只是将相同区块的数据映射到原始碟而已!相同的东西都不会重制的!只有不同的数据才会放置到快照区而已! 那 qemu 的虚拟磁盘有这功能嘛?有的!那就是副本 (backing_file)。

  • 使用 backing_file 机制创建 overlay 新档

在 qemu 的环境中,有一个名为 backing file 的机制,这个机制可以让原始的虚拟磁盘 (称为基本映像档, base image, 也就是副本, backing file),被一个可叠加的新映像档所利用 (overlay),这个结果很像 LVM 的 original 被快照所应用类似! 有兴趣的朋友可以到文末的参考数据瞧一瞧。我们可以将 demo1.img 当作是 backing file 基础副本文件, 其他的 overlay 新映像档,通通是参照 backing file 来的!

实做上倒也是很简单!创建一个新映像档,并指定其副本来源文件名即可!

# 在 KVM host 上面,参考正确的 backing file 创建第一个 overlay
[root@cloud ~]# ll -h /kvm/img/demo1.img
-rw-r--r--. 1 root root 2.3G Aug  4 13:47 /kvm/img/demo1.img

[root@cloud ~]# qemu-img info /kvm/img/demo1.img
image: /kvm/img/demo1.img
file format: qcow2
virtual size: 30 GiB (32212254720 bytes)
disk size: 2.26 GiB
cluster_size: 2097152
....

# 前往 /kvm/img 创建名为 test1.img 的 ovarlay 映像档
[root@cloud ~]# cd /kvm/img
[root@cloud img]# qemu-img create -f qcow2 test1.img -F qcow2 -o backing_file=/kvm/img/demo1.img
Formatting 'test1.img', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=32212254720
  backing_file=/kvm/img/demo1.img backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16

[root@cloud img]# ll -h *.img
-rw-r--r--. 1 root root 2.3G Aug  4 13:47 demo1.img
-rw-r--r--. 1 root root 193K Aug  4 15:23 test1.img

[root@cloud img]# qemu-img info test1.img
image: test1.img
file format: qcow2
virtual size: 30 GiB (32212254720 bytes)
disk size: 196 KiB
cluster_size: 65536
backing file: /kvm/img/demo1.img
backing file format: qcow2
....

你可以看到,这个 test1.img 实际占用的容量仅有 196K 喔!另外参照文件为 demo1.img 喔! 只是千万要记得,由于 xml 可能来自任何地方,因此,许多的 backing_file 可能得要使用绝对路径才好! 无论如何,我们先创建好这颗 overlay 映像档喔!

  • 修改 xml 硬件文件与启动 VM

我们这个小章节的目的是想要让 VM 的性能好,也就是说,想要修改 xml 硬件资源设置档啦! 但是,又担心操作过程会让我们原本的硬盘文件出问题,所以,简单的想法就是拿一个一模一样的硬盘文件来取代掉原本的文件即可! 然后,就可以开始随便测试了!因此,直接修改 demo1.xml 文件,将硬盘文件改掉之外, 也将光盘片文件名清空,就等于将光盘片退出了!很单纯!之后就可以重新使用 vnc 登录, 启动网络来让我们从 KVM host 登录了!整体流程有点像这样:

# 创建 test1.xml 文件,并且修改完毕之后启动虚拟机
[root@cloud ~]# cd /kvm/xml
[root@cloud xml]# cp demo1.xml test1.xml
[root@cloud xml]# vim test1.xml
  2   <name>test1</name>
  9     <nvram>/kvm/xml/test1.uefi.fd</nvram>
 36       <source file="/kvm/img/test1.img"/>
 41       <source file=""/>
 63       <mac address="52:54:00:ed:63:aa"/>
 75     <graphics type="vnc" port="5912" listen="0.0.0.0" passwd="rocky9"/>

[root@cloud xml]# virsh create /kvm/xml/test1.xml
Domain 'test1' created from /kvm/xml/test1.xml

[root@cloud xml]# virsh domblklist test1
 Target   Source
------------------------------
 vda      /kvm/img/test1.img
 sda      -

[root@cloud xml]# netstat -tlunp | egrep 'kvm|Proto'
Proto Recv-Q Send-Q Local Address  Foreign Address  State  PID/Program name
tcp        0      0 0.0.0.0:5912   0.0.0.0:*        LISTEN 5601/qemu-kvm

懒的打字的话,鸟哥也写文本档给大家下载~直接 wget 下载到你的系统上就可以了!

wget http://vbird.cn/linux_server/rocky9/0130vmtuning/test1.xml

这次我们就不使用 VNC 连接,直接用 arp 查看 IP 之后,使用 ssh 直接连接到 test1 系统内部吧!

# 先查看一下刚刚修改的网卡卡号对应 IP 地址的情况:
[root@cloud ~]# arp -n -i templan
Address             HWtype  HWaddress           Flags Mask   Iface
192.168.10.52       ether   52:54:00:ed:63:aa   C            templan

# 看起来 IP 没有修改!OK!那我们就直接连接到 192.168.10.52 看看:
[root@cloud ~]# ssh vbird@192.168.10.52
vbird@192.168.10.52's password:
Last login: Fri Aug  4 13:47:28 2023 from 192.168.10.254
[vbird@localhost ~]$ 

如果一切都顺利正常,那么我们就可以开始准备来测试这部虚拟机啰!

2.2.2、简易性能测试软件:dd, fio, iperf3

要判断性能有没有改变,总是需要一些测试数据的,不是用『感觉的』。鸟哥听过最有趣的测试, 是早期还没有这么多可用测试工具前,有些大公司为了需要数据佐证,因此,测试人员是拿着码表做时间纪录的! 这真是时代的眼泪啊...目前大部分都有可用软件可以拿来测试结果~只是,数据方面不见得是真实的! 因为各种数据都有其适用性喔!

  • 使用 dd 测试读写性能

有一个最笨的方法来测试写入性能,那就是 dd 这个指令。不过要注意的是, 因为我们有磁盘数组,而磁盘数组的 chunk 容量并不固定!因此可能会导致不同的写入区块 (block) 容量, 会有性能上的差异。无论如何,让我们找出最佳的性能有接近理论性能即可。

dd 有个很有趣的参数,称为 oflag=direct,这个参数可以让写入时,不用到内存的辅助! 因此,所有的数据会同步写入到磁盘,不会先写入内存。这样的测试会比较正常!

# 在 VM 环境下,实做一个连续运行的 dd 测试!记得切换成为 root
[root@localhost ~]# vim checkdd.sh
#!/bin/bash
for block in 32 64 128 512 1024
do
   count=$(( 1024 * 500 / ${block} ))
   echo "test: ${block}k"
   dd if=/dev/zero of=./test.img bs=${block}k count=${count} oflag=direct
done

[root@localhost ~]# sh checkdd.sh
test: 32k
16000+0 records in
16000+0 records out
524288000 bytes (524 MB, 500 MiB) copied, 3.91262 s, 134 MB/s
test: 64k
8000+0 records in
8000+0 records out
524288000 bytes (524 MB, 500 MiB) copied, 3.70768 s, 141 MB/s
test: 128k
4000+0 records in
4000+0 records out
524288000 bytes (524 MB, 500 MiB) copied, 3.4443 s, 152 MB/s
test: 512k
1000+0 records in
1000+0 records out
524288000 bytes (524 MB, 500 MiB) copied, 1.00981 s, 519 MB/s
test: 1024k
500+0 records in
500+0 records out
524288000 bytes (524 MB, 500 MiB) copied, 0.109536 s, 4.8 GB/s

随便测试一个 run,结果就显得很不同!回想一下,我们的 XML 硬件资源档的设置当中,关于虚拟磁盘的设计是这样的:

    <disk type="file" device="disk">
      <driver name="qemu" type="qcow2" cache="writeback" io="threads"/>
      <source file="/kvm/img/test1.img"/>
      <target dev="vda" bus="virtio"/>
    </disk>

这个 writeback 的设置值,会让 KVM host 切出一块内存,用来缓存住 VM 的磁盘读写!以加快速度。 只是效果如何?老实说,要看 KVM host 的本体性能了!所以,上面这个测试,基本上,比较适合 KVM host 本身的测试! 鸟哥使用了 RAID10,用了 4 颗传统磁盘,若以每颗磁盘最大具有 200Mbyes/s 的话,最高理论速度会在 400Mbytes/s 左右。

使用了 KVM host 的内存在缓存上面,所以在用户端的磁盘测试,基本上都是不准的!上面只是提供一个测试参考而已。 在鸟哥的 KVM host 当中,测试的结果,最高写入发生在 block 为 128K 到 512K 之间,大概都有 350Mbytes/s 的写入速度,老实说, 很棒了!若要取得较为准确的数据,可能得要多跑几次喔!然后取平均~
  • 使用 fio 运行磁盘 I/O 的测试方式:

除了使用 dd 测试连续读写之外,RockyLinux 有提供名为 fio 的软件来让我们测试磁盘读写喔!安装 fio 之后, 可以通过如下的指令直接测试即可:

[root@localhost ~]# yum -y install fio

# 测试随机写入的方式
[root@localhost ~]# fio --name=randwrite --ioengine=libaio --iodepth=1 --rw=randwrite --bs=64k \
> --direct=1 --size=1G --numjobs=1 --runtime=240 --group_reporting
randwrite: (g=0): rw=randwrite, bs=(R) 64.0KiB-64.0KiB, (W) 64.0KiB-64.0KiB, (T) 64.0KiB-64.0KiB, 
  ioengine=libaio, iodepth=1
fio-3.27
Starting 1 process
randwrite: Laying out IO file (1 file / 1024MiB)
Jobs: 1 (f=1): [w(1)][83.3%][eta 00m:02s]
randwrite: (groupid=0, jobs=1): err= 0: pid=1109: Fri Aug  4 15:42:49 2023
  write: IOPS=1600, BW=100MiB/s (105MB/s)(1024MiB/10239msec); 0 zone resets
    slat (usec): min=5, max=191, avg= 6.48, stdev= 2.05
    clat (usec): min=30, max=8895.4k, avg=617.41, stdev=69531.25
     lat (usec): min=40, max=8895.5k, avg=624.04, stdev=69531.25
    clat percentiles (usec):
     |  1.00th=[    37],  5.00th=[    38], 10.00th=[    38], 20.00th=[    39],
     | 30.00th=[    41], 40.00th=[    43], 50.00th=[    45], 60.00th=[    53],
     | 70.00th=[    74], 80.00th=[    77], 90.00th=[    83], 95.00th=[    90],
     | 99.00th=[   122], 99.50th=[   133], 99.90th=[   184], 99.95th=[   229],
     | 99.99th=[287310]
   bw (  KiB/s): min=251520, max=1003648, per=100.00%, avg=544682.67, stdev=402544.44, samples=3
   iops        : min= 3930, max=15682, avg=8510.67, stdev=6289.76, samples=3
  lat (usec)   : 50=58.58%, 100=38.58%, 250=2.81%, 500=0.01%
  lat (msec)   : 10=0.01%, 500=0.01%, >=2000=0.01%
  cpu          : usr=0.34%, sys=1.10%, ctx=16386, majf=0, minf=14
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,16384,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=100MiB/s (105MB/s), 100MiB/s-100MiB/s (105MB/s-105MB/s), io=1024MiB (1074MB), run=10239-10239msec

Disk stats (read/write):
  vda: ios=0/14831, merge=0/0, ticks=0/18435, in_queue=18435, util=97.85%

# 测试随机读与写的方式
[root@localhost ~]# fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 \
> --name=test --filename=random_read_write.fio --bs=64k --iodepth=64 \
> --size=512M --readwrite=randrw --rwmixread=75

跟上面 dd 的状态一样,由于用户端设计了 writeback 的关系,所以读写速度飞快!很吓人! 其实是假的!哈哈!所以, fio 也是比较适合用在测试 KVM host 上面啰。

  • 使用 iperf3 测试网络状态

网络是双向的,从服务器 (server) 到用户端 (client),或者是从用户端到服务器,所以,得要有 server 与 client 才行!目前我们有一部实体 KVM host,以及一部虚拟 VM 系统,刚刚好有两部呢!

网络性能测试,可以使用 iperf3 这个惯用的软件!不论 server/client 都要装上这个软件! RockyLinux 缺省就有支持这个 iperf3,所以直接安装即可。安装完毕之后,在 server 端运行监听功能, 在用户端则运行下载或上传两者任何一个动作即可。只是要注意,Server 仍需要放行防火墙规则! 否则仍然会挡住 client 的连接要求喔!

# Server 端:先安装,开放防火墙,然后监听一个 5201 的端口口:
[root@cloud ~]# yum -y install iperf3

[root@cloud ~]# firewall-cmd --add-port=5201/tcp --zone=libvirt

[root@cloud ~]# iperf3 -s &
[root@cloud ~]# netstat -tlunp | egrep 'Proto|iperf'
Proto Recv-Q Send-Q Local Address  Foreign Address  State   PID/Program name
tcp6       0      0 :::5201        :::*             LISTEN  5913/iperf3

这时 server 会启动一个 port 5201 等待来自 client 的检测。要注意的是,因为这次的连接仅止于测试,所以, 我就没有将防火墙规则写入到设置档当中!下次 KVM host 重新开机后,这条防火墙规则就会消失喔! 然后开始在 client 端进行测试!

# Client 端:安装好 iperf3 之后,先尝试下载处理,测试流量
[root@localhost ~]# yum -y install iperf3

[root@localhost ~]# iperf3 -c 192.168.10.254 -p 5201 -t 10 -i 5 -R
Connecting to host 192.168.10.254, port 5201
Reverse mode, remote host 192.168.10.254 is sending
[  5] local 192.168.10.52 port 52216 connected to 192.168.10.254 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-5.00   sec  51.9 GBytes  89.1 Gbits/sec
[  5]   5.00-10.00  sec  51.5 GBytes  88.4 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.04  sec   103 GBytes  88.4 Gbits/sec    0             sender
[  5]   0.00-10.00  sec   103 GBytes  88.8 Gbits/sec                  receiver

[root@localhost ~]# iperf3 -c 192.168.10.254 -p 5201 -t 10 -i 5
Connecting to host 192.168.10.254, port 5201
[  5] local 192.168.10.52 port 42186 connected to 192.168.10.254 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-5.00   sec  29.5 GBytes  50.7 Gbits/sec    0   2.41 MBytes
[  5]   5.00-10.00  sec  29.6 GBytes  50.9 Gbits/sec    0   2.41 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  59.2 GBytes  50.8 Gbits/sec    0             sender
[  5]   0.00-10.04  sec  59.2 GBytes  50.6 Gbits/sec                  receiver

用户端相关的选项与参数功能如下:

  • -c Server_IP:使用 client 模式,连接到 Server 去;
  • -t 10:仅侦测 10 秒钟
  • -i 5:每 5 秒钟报告一次传输的信息

当然啦,还是那句话,因为我们的连接系统也都是在一部主机的内部虚拟环境, 所以,很多速度感觉上都是不可思议的!哈哈哈!这里只是让大家了解一下如何测试而已,数据方面可能不是太准确! 没关系!未来你有很多机会可以用到实体机器,或者是分别位于不同实体机器内的虚拟机, 那就有可能会用到这些测试工具啰!

2.2.3、虚拟机性能调整

就跟实体机器一样,虚拟机也是有部份数据可以调整的!举例来说,最重要的就是 cpu 的对应了! 我们未来会使用超多 VM 在一部 KVM host 上面。以目前鸟哥的系统为例,我的 i5 第 10 代,虽然说是 6 核 12 绪, 老实说,运算内核也才 6 个而已。目前一个 VM 给予 4 个内核,如果未来有超过 4 个 VM 时,可能会用到 16 个内核! 那也超过 6 个实体内核太多了!如果 4 个 VM 都是全速运算某些数据,那我原本的 KVM host 可能就没有足够的资源了! KVM host 如果出问题,那整体的系统就会非常怪异!因此,为了避免系统的损毁, 我们可能还是得要针对某些资源给予 VM 限制一下,比较保险一点。

  • CPU 资源限制

为了担心让 VM 搞到原始的 KVM host 出问题,所以,保留 1~2 个完整的实体内核给 KVM host,让所有的 VM 只使用固定的内核,那不就可以使得系统更加稳定了!没错!就是这样。但是,怎么知道哪个线程是共用实体 CPU 内核呢? 简单的说,你可以直接看 /proc/cpuinfo 或者是查找 virsh 的输出结果喔!

# 这是在 KVM host 上面进行的查找喔!
[root@cloud ~]# virsh capabilities | grep siblings
            <cpu id='0' socket_id='0' die_id='0' core_id='0' siblings='0,6'/>
            <cpu id='1' socket_id='0' die_id='0' core_id='1' siblings='1,7'/>
            <cpu id='2' socket_id='0' die_id='0' core_id='2' siblings='2,8'/>
            <cpu id='3' socket_id='0' die_id='0' core_id='3' siblings='3,9'/>
            <cpu id='4' socket_id='0' die_id='0' core_id='4' siblings='4,10'/>
            <cpu id='5' socket_id='0' die_id='0' core_id='5' siblings='5,11'/>
            <cpu id='6' socket_id='0' die_id='0' core_id='0' siblings='0,6'/>
            <cpu id='7' socket_id='0' die_id='0' core_id='1' siblings='1,7'/>
            <cpu id='8' socket_id='0' die_id='0' core_id='2' siblings='2,8'/>
            <cpu id='9' socket_id='0' die_id='0' core_id='3' siblings='3,9'/>
            <cpu id='10' socket_id='0' die_id='0' core_id='4' siblings='4,10'/>
            <cpu id='11' socket_id='0' die_id='0' core_id='5' siblings='5,11'/>
# cpu id 是 CPU 线程编号,而 core_id 则是 CPU 实体内核编号!

[root@cloud ~]# cat /proc/cpuinfo | egrep '^processor|^core id'
processor       : 0
core id         : 0
processor       : 1
core id         : 1
processor       : 2
core id         : 2
processor       : 3
core id         : 3
processor       : 4
core id         : 4
processor       : 5
core id         : 5
processor       : 6
core id         : 0
processor       : 7
core id         : 1
processor       : 8
core id         : 2
processor       : 9
core id         : 3
processor       : 10
core id         : 4
processor       : 11
core id         : 5
# processor 是 CPU 线程编号,而 core id 则是 CPU 实体内核编号!

从上面这几个指令,就可以看到,在鸟哥的这个 KVM host 系统中,CPU 号码从 0 到 11 号,以 6 个切开,0, 6 为共用同一个实体运算内核, 1, 7 共用一个这样。如果我想要让所有的 VM 系统使用编号 1~5 号的实体内核,保留 0 号实体内核给原生系统使用的话, 那么你的 XML 应该要修改成这样:

# 让 CPU 仅使用 1~5 号,第 0 号保留给系统使用,不要让虚拟机使用
[root@cloud ~]# vim /kvm/xml/demo1.xml
  <vcpu>4</vcpu>
  <vcpu placement='static' cpuset='1-5,7-11'>4</vcpu>

如此一来,编号 0 与 6 的 CPU 就不会被 VM 利用了!无论 VM 的 CPU 怎么忙,都会有一个实体内核保留给 KVM host 喔!

  • 节省带宽的方式:视频压缩

因为我们用的毕竟是 remote-viewer 的连接协定,缺省的情况下,传输的过程当中,视频是没有压缩的!对於单一连接来说,基本上,问题不大! 但是如果你的互联网连接速度较慢,那么可能将你的视频数据压缩再发送出去,会节省比较多带宽喔!

[root@cloud ~]# vim /kvm/xml/demo1.xml
    <graphics type="vnc" port="5911" listen="0.0.0.0" passwd="rocky9"/>
      <image compression='auto_glz' />
      <jpeg compression='auto' />
      <zlib compression='auto' />
      <playback compression='on' />
      <streaming mode='filter' />
    </graphics>

大概调整成这样就好,同样的,请将你的 test1.xml 也调整如上的内容, 接下来,请完整关闭 VM 之后,再次启动。为何需要完整的关机再启动呢?这是因为修改 XML 相当于『重新处理硬件』的意思, 所以,你得要完整关机,再次激活时,才会使用新的 XML 文件的内容!之后查查看是否可以看到底下的相关信息:

# 在 KVM host 上面,看看 test1 是否真不使用 0, 6 号实体内核
[root@cloud ~]# virsh shutdown test1
[root@cloud ~]# virsh create /kvm/xml/test1.xml
[root@cloud ~]# virsh vcpuinfo test1
VCPU:           0
CPU:            1
State:          running
CPU time:       9.0s
CPU Affinity:   -yyyyy-yyyyy

VCPU:           1
CPU:            11
State:          running
CPU time:       1.5s
CPU Affinity:   -yyyyy-yyyyy

VCPU:           2
CPU:            3
State:          running
CPU time:       1.3s
CPU Affinity:   -yyyyy-yyyyy

VCPU:           3
CPU:            2
State:          running
CPU time:       1.5s
CPU Affinity:   -yyyyy-yyyyy

这就是 CPU 被定位好的证明啰!

参考数据

修改历史:
  • 2022/05/15:母机器安装妥当之后,处理一下 VM 的安装与调校!这终于搞定!
  • 2022/07/21:更改成为 RockyLinux9 的版本。这个版本的 VM 环境,竟然已经不支持 spice 了!还好,之前有写得很详细!不然,还真的会卡住...
  • 2023/08/04:因为已经有 9.2 版本,许多的设置档内容会改变,所以重新处理!
2022/05/15以来统计人数
计数器
其他链接
环境工程模式篇
鸟园讨论区
鸟哥旧站

今日 人数统计
昨日 人数统计
本月 人数统计
上月 人数统计