libvirt
Libvirt 是由 Redhat 开发的一套开源的软件工具,目标是提供一个通用和稳定的软件库来高效、安全地管理一个节点上的虚拟机,并支持远程操作。Libvirt 屏蔽了不同虚拟化的实现,提供统一管理接口。用户只关心高层的功能,而 VMM 的实现细节,对于最终用户应该是透明的。Libvirt 就作为 VMM 和高层功能之间的桥梁,接收用户请求,然后调用 VMM 提供的接口,来完成最终的工作。
Libvirt 是什么
为什么需要 Libvirt?
- Hypervisor 比如 qemu-kvm 的命令行虚拟机管理工具参数众多,难于使用。
- Hypervisor 种类众多,没有统一的编程接口来管理它们,这对云环境来说非常重要。
- 没有统一的方式来方便地定义虚拟机相关的各种可管理对象。
Libvirt 提供了什么?
- 它提供统一、稳定、开放的源代码的应用程序接口(API)、守护进程 (libvirtd)和和一个默认命令行管理工具(virsh)。
- 它提供了对虚拟化客户机和它的虚拟化设备、网络和存储的管理。
- 它提供了一套较为稳定的 C 语言应用程序接口。目前,在其他一些流行的编程语言中也提供了对 libvirt 的绑定,在 Python、Perl、Java、Ruby、PHP、OCaml 等高级编程语言中已经有 libvirt 的程序库可以直接使用。
- 它对多种不同的 Hypervisor 的支持是通过一种基于驱动程序的架构来实现的。libvirt 对不同的 Hypervisor 提供了不同的驱动,包括 Xen 的驱动,对 QEMU/KVM 有 QEMU 驱动,VMware 驱动等。在 libvirt 源代码中,可以很容易找到 qemu_driver.c、xen_driver.c、xenapi_driver.c、vmware_driver.c、vbox_driver.c 这样的驱动程序源代码文件。
- 它作为中间适配层,让底层 Hypervisor 对上层用户空间的管理工具是可以做到完全透明的,因为 libvirt 屏蔽了底层各种 Hypervisor 的细节,为上层管理工具提供了一个统一的、较稳定的接口(API)。
- 它使用 XML 来定义各种虚拟机相关的受管理对象。
目前,libvirt 已经成为使用最为广泛的对各种虚拟机进行管理的工具和应用程序接口(API),而且一些常用的虚拟机管理工具(如 virsh、virt-install、virt-manager 等)和云计算框架平台(如 OpenStack、OpenNebula、Eucalyptus 等)都在底层使用 libvirt 的应用程序接口。
Libvirt C API
Libvirti API 所管理的主要对象
| 对象 | 解释 |
|---|---|
| Domain | 指运行在由 Hypervisor 提供的虚拟机器上的一个操作系统实例或者用来启动虚机的配置 |
| Hypervisor | 一个虚拟化主机的软件层 |
| Node | 一台物理服务器 |
| Storage pool | 一组存储媒介的集合,比如物理硬盘驱动器。一个存储池被划分为小的容器称作卷。卷会被分给一个或者多个虚机 |
| Volume | 一个从存储池分配的存储空间。一个卷会被分给一个或者多个域,常常成为域里的虚拟硬盘 |
对象的管理模型
| 对象名称 | 对象 | Python 类 | 描述 |
|---|---|---|---|
| Connect | 与 Hypervisor 的连接 | virConnectPtr | 在调用任何 API 去管理一个本地或者远端的 Hypervisor 前,必须建立和这个 Hypervisor 的连接。 |
| Domain | Guest domain | virDomainPtr | 用于列举和管理已有的虚机,或者创建新的虚机。唯一标识:ID,Name,UUID。一个域可能是暂时性的或者持久性的。暂时性的域只能在它运行期间被管理。持久性的域在主机上保存了它的配置。 |
| Virtual Network | 虚拟网络 | virNetworkPtr | 用于管理虚机的网络设备。唯一标识:Name,UUID。一个虚拟网络可能是暂时性的或者持久性的。每个主机上安装 libvirt 后,它都有一个默认的网络设备“default”。它向该主机上运行的虚机提供 DHCP 服务,以及通过 NAT 连接到主机上。 |
| Storage Pool | 存储池 | virStoragePoolPtr | 用于管理虚拟机内的所有存储,包括 local disk, logical volume group, iSCSI target, FibreChannel HBA and local/network file system。唯一标识:Name,UUID。一个存储池可能是暂时性的或者持久性的。Pool 的 type 可以是 dir, fs, netfs, disk, iscsi, logical, scsi,mpath, rbd, sheepdog, gluster 或者 zfs。 |
| Storage Volume | 存储卷 | virStorageVolPtr | 用于管理一个存储池内的存储块,包括一个池内分配的块、磁盘分区、逻辑卷、SCSI/iSCSI Lun,或者一个本地或者网络文件系统内的文件等。唯一标识:Name,Key,Path。 |
| Host device | 主机设备 | virNodeDevPtr | 用于管理主机上的物理硬件设备,包括 the physical USB or PCI devices and logical devices these provide, such as a NIC, disk, diskcontroller, sound card, etc。唯一标识:Name。 |
Libvirt XML 定义
Libvirt 使用 XML 来定义各种对象,详细的 XML 定义说明在 https://libvirt.org/format.html。 这里列出几种主要的对象:
disk
任何磁盘设备,包括软盘(floppy)、硬盘(hard disk)、光驱(cdrom)或者半虚拟化驱动都使用 <disk> 元素来定义。 方式:
|
|
其中:
type用来指定 device source 的类型:file,block,dir,network或者volume"。具体的 source 由` 标签定义。 device用来指定 device target 的类型:floppy,disk,cdrom, and “lun”, 默认为disk。具体的 target 由<target>标签定义。
volume 类型的 disk
|
|
file 类型的 disk
|
|
block 类型的 disk
|
|
network 类型的 disk
|
|
host device assignment
|
|
network interface
有几种 interface 类型: (1)type = ‘network’ 定义一个连接 Virtual network 的 interface
|
|
|
|
(2)type=‘birdge’ 定义一个 Bridge to LAN(桥接到物理网络)的 interface:前提是主机上存在一个 bridge,该 bridge 已经连到物理 LAN。
|
|
|
|
(3)type=‘ethernet’ 定义一个使用指定脚本连接到 LAN 的 interface
|
|
(4)type=‘direct’ 定义一个直接连到物理网卡(Direct attachment to physical interface)的 interface:需要 Linux macvtap 驱动支持
|
|
(5)type=‘hostdev’ 定义一个由主机 PCI 网卡直接分配(PCI Passthrough)的 interface: 分配主机上的网卡给虚机
|
|
network
|
|
- bridge:定义一个用于构造该虚拟网络的网桥。
- domain:定义 DHCP server 的 DNS domain。
- forward: 定义虚拟网络直接连到物理 LAN 的方式. ”mode“指转发模式。
(1) mode=‘nat’:所有连接到该虚拟网络的虚拟的网络都会经过物理机器的网卡,并转换成物理网卡的地址。
|
|
也可以指定公共的 IP 地址和端口号。
|
|
(2) mode=‘route’:类似于 NAT,但是不使用 NAT,而是使用 routing table。
|
|
(3) mode=‘bridge’:使用不受 libvirt 管理的 bridge,比如主机上已有的 bridge;open vswitch bridge;使用 macvtap’s “bridge” 模式
|
|
(4) mode=‘passthrough’:使用 a macvtap “direct” connection in “passthrough” mode 指定主机上的特定网卡用于虚拟网络
|
|
(5) mode=‘hostdev’:直接分配主机上的网络设备。
|
|
Libvirt API 的实现
libvirt API 的实现是在各个 Hypervisor driver 和 Storage dirver 内。Hypervisor 驱动包括:
- LXC - Linux Containers
- OpenVZ
- QEMU
- Test - Used for testing
- UML - User Mode Linux
- VirtualBox
- VMware ESX
- VMware Workstation/Player
- Xen
- Microsoft Hyper-V
- IBM PowerVM (phyp)
- Parallels
- Bhyve - The BSD Hypervisor
原理实现
整体架构
kvm
kvm 是 linux 内核的模块,需要硬件辅助虚拟化技术,如 cpu 的 Intel-VT,AMD-V;内存相关如 Intel 的 EPT 和 AMD 的 RVI 技术。Guest OS 的 CPU 指令不用再经过 Qemu 转译,直接运行,大大提高了速度,KVM 通过 /dev/kvm 暴露接口,用户程序可以通过 ioctl 函数来访问这个接口。见如下伪代码:
|
|
kvm 内核模块本身只能提供 cpu 和 内存 的虚拟化,所以他必须搭配 QEMU 才能构成一个完整的虚拟化技术,这就是下面要说的 qemu-kvm。
qemu
Qemu 是一个模拟器,向 Guest OS 模拟硬件,Guest OS 认为自己和硬件直接打交道,其实是同 Qemu 模拟出来的硬件打交道,Qemu 将这些指令转译给真正的硬件。由于所有的硬件都要从 Qemu 里面过一手,因而性能较差。
qemu-kvm
Qemu 将 kvm 整合进来,通过 ioctl 调用 /dev/kvm 接口,将有关 cpu 指令的部分交由内核模块来做。kvm 负责 cpu 虚拟化+内存虚拟化,但 kvm 不能模拟其他设备。Qemu 模拟 IO 设备(网卡,磁盘等),kvm 加上 Qemu 之后就能实现真正意义上服务器虚拟化。因为用到了上面两个东西,所以称之为 qemu-kvm。
virtio
Qemu 模拟 I/O 设备,同样会影响这些设备的性能,于是又产生了半虚拟化设备 virtio_blk,virtio_net 来提高设备性能。
Libvirt
libvirt 是目前使用最为广泛的对 kvm 虚拟机进行管理的工具和 API
- Libvirtd 是一个 daemon 进程,可以被本地的 virsh 调用,也可以被远程的 virsh 调用
- Libvirtd 调用 qemu-kvm 操作 KVM 虚拟机
代码实现
Libvirt 代码里所定义的主要对象如下图所示。
- VirConnectPtr:代表了一个特定 VMM 建立的连接。每一个基于 Libvirt 的应用程序都应该先提供一个 URI 来指定本地或远程的某个 VMM,从而获得一个 VirConnectPtr 连接。比如 xen+ssh://host-virt/代表了通过 ssh 连接一个在 host-virt 机器上运行的 Xen VMM。拿到 virConnectPtr 连接后,应用程序就可以管理这个 VMM 的虚拟机和对应的虚拟化资源,比如存储和网络。
- VirDomainPtr:代表一个虚拟机,可能是激活状态或者仅仅已定义。
- VirNetworkPtr:代表一个网络
- VirStorageVolPtr:代表一个存储卷,通常被虚拟机当做块设备使用。
- VirStoragePoolPtr:代表一个存储池,用来分配和管理存储卷的逻辑区域。
本机之间的通信
在初始化的过程中,所有的驱动被枚举和注册。每一个驱动都会加载特定的函数为 Libvirt API 所调用。如下图所示,Application 通过 URI 调用 Public API,然后 PublicAPI 通过使用 Driver 提供的 API 接口调用正真的 Driver 实现。
远程主机之间的通信
Libvirt 的目标是支持远程管理,所以到 Libvirt 的驱动的访问,都由 Libvirt 守护进程 libvirtd 处理,libvirtd 被部署在运行虚拟机的节点上,通过 RPC 由对端的 remote Driver 管理,如下图所示。
在远程管理模式下,virConnectionPtr 实际上连接了本地的 remote Driver 和远端的特定 Driver。所有的调用都通过 remote Driver 先到达云端的 libvirtd,libvirtd 访问对应的 Driver。
对照关系
这里 有一个 virsh 命令、Libvirt C API、 QEMU driver 方法 和 QEMU Monitor 命令的对照表(部分):
| virsh command | Public API | QEMU driver function | Monitor command |
|---|---|---|---|
| virsh create XMLFILE | virDomainCreateXML() | qemudDomainCreate() | info cpus, cont, change vnc password, balloon (all indirectly) |
| virsh suspend GUEST | virDomainSuspend() | qemudDomainSuspend() | stop |
| virsh resume GUEST | virDomainResume() | qemudDomainResume() | cont |
| virsh shutdown GUEST | virDomainShutdown() | qemudDomainShutdown() | system_powerdown |
| virsh setmem GUEST MEM-KB | virDomainSetMemory() | qemudDomainSetMemory() | balloon (indirectly) |
| virsh dominfo GUEST | virDomainGetInfo() | qemudDomainGetInfo() | info balloon (indirectly) |
| virsh save GUEST FILENAME | virDomainSave() | qemudDomainSave() | stop, migrate exec |
| virsh restore FILENAME | virDomainRestore() | qemudDomainRestore() | cont |
| virsh dumpxml GUEST | virDomainDumpXML() | qemudDomainDumpXML() | info balloon (indirectly) |
| virsh attach-device GUEST XMLFILE | virDomainAttachDevice() | qemudDomainAttachDevice() | change, eject, usb_add, pci_add (all indirectly) |
| virsh detach-device GUEST XMLFILE | virDomainDetachDevice() | qemudDomainDetachDevice() | pci_del (indirectly) |
| virsh migrate GUEST DEST-URI | virDomainMigrate() | qemudDomainMigratePerform() | stop, migrate_set_speed, migrate, cont |
| virsh domblkstat GUEST | virDomainBlockStats() | qemudDomainBlockStats() | info blockstats |
| - | virDomainBlockPeek() | qemudDomainMemoryPeek() | memsave |
操作实践
安装 kvm 环境
KVM 是基于虚拟化扩展(Intel VT 或者 AMD-V)的 X86 硬件的开源的 Linux 原生的全虚拟化解决方案。KVM 中,虚拟机被实现为常规的 Linux 进程,由标准 Linux 调度程序进行调度; 虚机的每个虚拟 CPU 被实现为一个常规的 Linux 进程。这使得 KMV 能够使用 Linux 内核的已有功能。 但是,KVM 本身不执行任何硬件模拟,需要客户空间程序通过 /dev/kvm 接口设置一个客户机虚拟服务器的地址空间,向它提供模拟的 I/O,并将它的视频显示映射回宿主的显示屏。 目前这个应用程序是 QEMU。
Kvm 相关安装包及其作用:
- qemu-kvm 主要的 kvm 程序包
- Python-virtinst 创建虚拟机所需要的命令行工具和程序库
- virt-manager 用于管理虚拟机的 GUI 界面(可以管理远程 kvm 主机)
- virt-viewer: 通过 GUI 界面直接与虚拟机交互(可以管理远程 kvm 主机)
- virsh:基于 libvirt 的 命令行工具(CLI)
- virt-top 虚拟机统计命令
- virt-viewer GUI 连接程序,连接到已配置好的虚拟机
- libvirt:提供简单且统一的工具和 API,用于管理虚拟机,屏蔽了底层的复杂结构。(支持 qemu-kvm/virtualbox/vmware)
- libvirt-client 为虚拟客户机提供的 c 语言工具包
- virt-install 基于 libvirt 服务的虚拟机创建命令
- bridge-utils 创建和管理桥接设备的工具
|
|
#在根下创建一个目录/data ,(不把虚拟机的文件放在/root 或根主要是虚拟机启动之后 qemu 这些用户没有权限去/root 或根下读取虚拟机的配置文件)
|
|
安装虚拟机
安装前要设置环境语言为英文 LANG=“en_US.UTF-8”,如果是中文的话某些版本可能会报错。CentOS 7 在这里修改 /etc/locale.conf。
kvm 创建虚拟机,特别注意.iso 镜像文件一定放到/data 或者根目录重新创建目录,不然会因为权限报错,无法创建虚拟机。
|
|
首先学 virt-install 命令,在这里使用-help 查看,并且只学习重要的,其他的可以后面学习.
|
|
执行创建虚拟机命令
|
|
上面创建虚拟机命令最终需要配置一下系统基础设置,带 [!] 基本都是要配置的,可以参考 这篇文章 进行配置。配置完成后,即可进入虚拟机安装流程。
查看虚拟机
|
|
虚拟机基本操作学习
|
|
|
|
编辑 kvm 的 xml 文件, 更改虚拟机 CPU 配置
编辑 kvm 的 xml 文件,更改虚拟机 CPU 配置。配置虚拟机的 CPU 有两种方式
- 启动的时候指定核数
- 更改 xml
为了实现 CPU 的热添加,就需要更改 CPU 的最大值,热添加的个数不能超过最大值.
|
|
当前为 2,改为自动括容,最大为 4
|
|
重启虚拟机,查看 CPU 信息,确认 CPU 的个数,再进行 CPU 热添加
|
|
此时查看虚拟机 CPU
|
|
动态修改虚拟机 CPU:
|
|
再去虚拟机查看 CPU 信息
[root@localhost ~]# cat /proc/cpuinfo | grep processor
processor : 0
processor : 1
processor : 2
processor : 3
编辑 kvm 的 xml 文件, 更改虚拟机内存配置
内存的设置拥有一个气球(balloon)机制,可以增大减少,但是也要设置一个最大值,默认并没有设置最大值,也可以在安装的时候指定
|
|
可以修改 XML 更改虚拟机内存配置
|
|
通过镜像创建虚拟机
镜像制作原则
- 分区的时候,只分一个/根分区,并不需要 swap 分区,由于虚拟机的磁盘性能就不好,如果设置了 swap 分区,当 swap 工作的时候,性能会更差。例如阿里云主机,就没有交换分区。
- 镜像制作需要删除网卡(eth0)中的 UUID
- 关闭 selinux,关闭 iptables
- 安装基础软件的包:net-tools lrzsz screen tree vim wget
创建虚拟机镜像文件
|
|
编辑新虚拟机配置文件
主要是修改虚拟机文件名,UUID,镜像地址和网卡地址,其中 UUID 在 Linux 下可以使用 uuidgen 命令生成
|
|
如何根据 XML 定义虚拟机
|
|
参考资料
-
No backlinks found.