前言

libvirt是一套用于管理虚拟机平台的工具,其官网[6]包含更多详细信息。相比QEMU命令集,libvirt更方便,且支持不同的虚拟化技术,如QEMU/KVM、Xen和LXC容器等。

1. libvirt的安装和配置

根据官方文档[6],在Ubuntu上可以直接使用apt安装libvirt:

apt update && apt-get install -y libvirt-bin libvirt-doc

安装完成后,我们确认一下libvirt守护进程在正常运行:

➜  ~ pgrep -lfa libvirtd
3973 /usr/sbin/libvirtd

看一下默认配置(排除了注释内容):

➜  ~ cat /etc/libvirt/libvirtd.conf | grep -vi "#" | sed '/^$/d'
unix_sock_group = "libvirt"
unix_sock_ro_perms = "0777"
unix_sock_rw_perms = "0770"
auth_unix_ro = "none"
auth_unix_rw = "none"

修改配置文件/etc/libvirt/qemu.conf,将QEMU的安全驱动置空(这样做关闭了QEMU的SELinux、AppArmor等安全驱动):

sed -i 's/^#security_driver.*=.*"selinux"$/security_driver = "none"/g' /etc/libvirt/qemu.conf

重启libvirt守护进程:

➜  ~ /etc/init.d/libvirtd restart
[ ok ] Restarting libvirtd (via systemctl): libvirtd.service.

安装配置完成。最后再看一眼libvirt的配置目录吧:

➜  ~ ls /etc/libvirt/
hooks               libxl.conf        qemu               secrets
libvirt-admin.conf  libxl-lockd.conf  qemu.conf          virtlockd.conf
libvirt.conf        lxc.conf          qemu-lockd.conf    virtlogd.conf
libvirtd.conf       nwfilter          qemu-sanlock.conf  virt-login-shell.conf

2. 定义虚拟机实例

在libvirt中,创建虚拟机的操作被称为define(定义)。与Kubernetes依赖YAML描述文件创建Pod类似,libvirt创建虚拟机依赖XML文件。我们可以手写虚拟机的XML定义文件,也可以使用virt-install自动生成XML定义文件。

手动编写XML较为复杂,将以下内容保存为kvm1.xml

<domain type='kvm' id='1'>
    <name>kvm1</name>
    <memory unit='KiB'>1048576</memory>
    <vcpu placement='static'>1</vcpu>
    <os>
        <type arch='x86_64' machine='pc-i440fx-trusty'>hvm</type>
        <boot dev='hd'/>
    </os>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>restart</on_crash>
    <devices>
        <emulator>/usr/bin/qemu-system-x86_64</emulator>
        <disk type='file' device='disk'>
            <driver name='qemu' type='raw'/>
            <source file='/tmp/debian.img'/>
            <target dev='hda' bus='ide'/>
            <alias name='ide0-0-0'/>
            <address type='drive' controller='0' bus='0' target='0' unit='0'/>
        </disk>
        <interface type='network'>
            <source network='default'/>
            <target dev='vnet0'/>
            <model type='rtl8139'/>
            <alias name='net0'/>
            <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
        </interface>
        <graphics type='vnc' port='5900' autoport='yes' listen='172.16.56.201'>
            <listen type='address' address='172.16.56.201'/>
        </graphics>
    </devices>
    <seclabel type='none'/>
</domain>

可以将上述XML文件与Kubernetes Pod的YAML文件对照,会发现很多有意思的地方。

执行virsh命令创建虚拟机并查看:

➜  ~ virsh define ./kvm1.xml
Domain kvm1 defined from ./kvm1.xml

➜  ~ virsh list --all
 Id    Name                           State
----------------------------------------------------
 -     kvm1                           shut off

创建成功。不过,前面提到,我们可以使用virt-install命令将这个过程自动化。首先安装该工具:

apt install -y virtinst

来尝试一下:

➜  ~ virt-install --name kvm1 --ram 1024 --disk path=/tmp/debian.img,format=raw --graphics vnc,listen=172.16.56.201 --noautoconsole --hvm --import
WARNING  No operating system detected, VM performance may suffer. Specify an OS with --os-variant for optimal results.

Starting install...
Domain creation completed.

该命令将直接创建并启动虚拟机:

➜  ~ virsh list --all
 Id    Name                           State
----------------------------------------------------
 3     kvm1                           running

可以在/etc/libvirt/qemu下找到该虚拟机的定义文件kvm1.xml

<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
  virsh edit kvm1
or other application using the libvirt API.
-->

<domain type='kvm'>
  <name>kvm1</name>
  <uuid>b283c958-2e93-4ebe-a7bc-cf2982e938cf</uuid>
  <memory unit='KiB'>1048576</memory>
  <currentMemory unit='KiB'>1048576</currentMemory>
  <vcpu placement='static'>1</vcpu>
  <os>
    <type arch='x86_64' machine='pc-i440fx-3.1'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
    <apic/>
  </features>
  <cpu mode='custom' match='exact' check='partial'>
    <model fallback='allow'>Broadwell-noTSX-IBRS</model>
  </cpu>
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>
  </pm>
  <devices>
    <emulator>/usr/bin/kvm-spice</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw'/>
      <source file='/tmp/debian.img'/>
      <target dev='hda' bus='ide'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <controller type='usb' index='0' model='ich9-ehci1'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci1'>
      <master startport='0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci2'>
      <master startport='2'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci3'>
      <master startport='4'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
    </controller>
    <controller type='pci' index='0' model='pci-root'/>
    <controller type='ide' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <interface type='network'>
      <mac address='52:54:00:57:3c:24'/>
      <source network='default'/>
      <model type='rtl8139'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
    <serial type='pty'>
      <target type='isa-serial' port='0'>
        <model name='isa-serial'/>
      </target>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>
    <graphics type='vnc' port='-1' autoport='yes' listen='172.16.56.201'>
      <listen type='address' address='172.16.56.201'/>
    </graphics>
    <video>
      <model type='cirrus' vram='16384' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>
    <memballoon model='virtio'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
    </memballoon>
  </devices>
</domain>

可以看到,比我们自己写的要复杂一些。

3. 管理虚拟机实例

官方文档[3]对libvirt创建的虚拟机的生命周期做了很清晰的描述,如下图所示:

images/Vm_lifecycle_graph.png

这张图与Kubernetes中Pod的生命周期有很多相似之处,我们可以类比学习。

上一节,我们尝试了手动编写XML文件并创建虚拟机,创建后的虚拟机处于shut off状态。我们尝试启动它,然后查看QEMU进程,可以发现libvirt已经通过QEMU启动了虚拟机:

➜  ~ virsh start kvm1
Domain kvm1 started

➜  ~ pgrep -lfa qemu
6821 /usr/bin/qemu-system-x86_64 -name guest=kvm1,debug-threads=on -S -object secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-6-kvm1/master-key.aes -machine pc-i440fx-trusty,accel=kvm,usb=off,dump-guest-core=off -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid a80e675c-45aa-4f6a-a37e-f98a03627ac2 -no-user-config -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-6-kvm1/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc -no-shutdown -no-acpi -boot strict=on -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -drive file=/tmp/debian.img,format=raw,if=none,id=drive-ide0-0-0 -device ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 -netdev tap,fd=25,id=hostnet0 -device rtl8139,netdev=hostnet0,id=net0,mac=52:54:00:d7:93:9a,bus=pci.0,addr=0x3 -vnc 172.16.56.201:0 -device cirrus-vga,id=video0,bus=pci.0,addr=0x2 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 -msg timestamp=on

➜  ~ virsh list --all
 Id    Name                           State
----------------------------------------------------
 6     kvm1                           running

最后,强制关机并销毁该虚拟机:

➜  ~ virsh destroy kvm1
Domain kvm1 destroyed

➜  ~ virsh undefine kvm1
Domain kvm1 has been undefined

4. 审查和编辑虚拟机配置

前面曾说过,已创建的虚拟机的定义文件都在/etc/libvirt/qemu/目录保存。

我们可以到上述目录查看,也可以直接通过以下命令获得虚拟机实例的定义文件(可以用于创建一台新的虚拟机,但是要注意修改<name><uuid>值确保不同):

virsh dumpxml kvm1 # kvm1 is name of vm instance

该命令与Kubernetes中获取Pod的YAML声明文件的操作十分相像:

kubectl get pods pod-name -o yaml

除此之外,我们还可以借助virsh命令更新配置,从而更新虚拟机:

virsh edit kvm1 # kvm1 is name of vm instance

参考文献

  1. https://libvirt.org
  2. https://wiki.libvirt.org/page/VirtualNetworking
  3. https://wiki.libvirt.org/page/VM_lifecycle
  4. https://wiki.libvirt.org/page/FAQ
  5. https://wiki.libvirt.org/page/SSHSetup
  6. https://wiki.libvirt.org/page/UbuntuKVMWalkthrough