libvirt架构学习

Posted by Shi Hai's Blog on April 7, 2024

目标和术语

目标

提供一个公共且稳定的软件层,足以安全地管理节点(node)(可能是远程节点)上的域(domain)。

术语

  • 节点(node):是一台物理机器;
  • 虚拟机管理程序(hypervisor):是一个软件层,允许将一个节点虚拟化为多个虚拟机,且虚拟机中的配置可能与所在节点不同;
  • 域(domain):是虚拟机管理程序提供的虚拟机上运行的操作系统实例(或容器虚拟化情况下的子系统);

对外暴露的对象

  • virConnectPtr:表示与虚拟机管理程序的连接(connection);
  • virDomainPtr:表示一个活动的或者已定义的域(已定义的域:表示作为永久配置文件和存储存在,但当前未在该节点上运行);
  • virNetworkPtr:表示一个活动的或者已定义的网络;
  • virStoragePoolPtr:表示一个存储卷;
  • virStorageVolPtr:表示一个存储池,用于分配和存储存储卷的逻辑区域;

libvirt驱动程序

libvirt公共API将其实现委托给各个内部的驱动,当我们使用connection URI连接到libvirt时,相对应的虚拟化管理程序驱动就会被初始化。比如:我们指定如下的connection URI,那我们就连接到了一个test driver中(一个假的虚拟机管理程序驱动)。

virsh -c test:///default

libvirt实现了很多虚拟机管理程序驱动,如:LXC、QEMU/KVM、Xen、VirtualBox、Test等。详情

test driver

libvirt项目用test dirvier是用来做UT测试的。测试驱动中的所有状态都保存在内存中,我们可以通过test driver加载默认配置或者配置文件中定义的虚拟域数据。

详情

一个libvirt项目中使用到的一组test driver配置文件

driver加载代码走读

driver代码实现

每个driver都有一个xxx_driver.c文件,对应的就是各个驱动的实现逻辑。比如:test driver的实现代码在test_driver.c文件中。 而每个驱动源文件都有个注册函数用于把drivers都加载到内存中。还是以test driver为例,注册函数如下所示:

/**
 * testRegister:
 *
 * Registers the test driver
 */
int
testRegister(void)
{
    return virRegisterConnectDriver(&testConnectDriver,
                                    false);
}

virRegisterConnectDriver()中有个全局静态变量static virConnectDriver *virConnectDriverTab[MAX_DRIVERS],就是管理所有注册的drivers。

URI如何映射到注册driver

  • 获取到URL名
    当我们输入virsh --connect URI时,virsh就会获取到URI信息。源码
...
switch (arg) {
/* c表示的是执行virsh命令时带有connect指令 */
case 'c':
    VIR_FREE(ctl->connname);
    ctl->connname = g_strdup(optarg);
    break;
...
  • 通过uri获取connection
    在virsh的virshConnect()函数中通过调用virConnectOpenAuth()函数获取到connection。 源码
   do {
       ...
       virErrorPtr err;

       if ((c = virConnectOpenAuth(uri, virConnectAuthPtrDefault,
                                   readonly ? VIR_CONNECT_RO : 0)))
           break;

       if (readonly)
           goto cleanup;
       ...
   } while (authfail < 5);

...
cleanup:
   virPolkitAgentDestroy(pkagent);
   return c;
}
  • 加载驱动
    因为URI中已经有了各个驱动的前缀schema,所以我们就可以通过这个schema加载相关驱动。比如: 我们要连接到这个URI:test:///default,那这个前缀schema就是test,通过这个前缀我们就能连接到test driver中。 源码
static virConnectPtr
virConnectOpenInternal(const char *name,
                       virConnectAuthPtr auth,
                       unsigned int flags)
{
    ...
            /* 获取到各个驱动的注册函数,比如:test driver那注册函数就是testRegister() */
            regMethod = g_strdup_printf("%sRegister", ret->uri->scheme);

            /* 通过dlsym()动态加载注册函数 */
            if (virDriverLoadModule(ret->uri->scheme, regMethod, false) < 0)
                return NULL;
    ...
}

守护进程和远程访问

libvirt允许我们通过授权及加密的connection来访问运行在远端机器的虚拟机管理程序。

参考文档