oVirt之软件架构全剖析

本文是从软件架构设计的角度剖析oVirt的vdsm,另外还有一篇是从功能架构设计的角度剖析oVirt。

API调用流程之clientIF

在vdsm中API调用框架是由clientIF来两实现的,其中包含个Server,和两个对应的Detector,一个Acceptor,Acceptor接收到的客户端数据,根据协议的不同选择把数据发送到哪个Detector和Server进行处理。

Acceptor监听管理中心的ip和port(默认54321)。

Acceptor中添加了两个detector,Acceptor接收到管理中心的socket数据后(数据包含新的dispatcher的socket),分别使用两个detector去detect数据中协议是哪个detector,从而把数据交给对应detector去处理(handle_socket),然后detector通过handle_socket把dispatcher的socket放到了对应server的监听队列中。

API调用流程之BaseManager

在另一篇文章《一文看懂ovirt的supervdsmd服务》中已经详细讲解。主要实现了vsdmd服务和其他服务对supervdsmd服务的API调用框架。

Profile

启动程序优化和代码跟踪功能,主要包括cpu(yappi线程)和内存(memprofile线程)的profile。

Metrics

启动监控上报功能,实现了两个metrics客户端:hawkular和statsd。

发送metrics信息者:

periodic线程(发送虚拟机状态和主机状态)

health线程 (发送vdsmd服务的进程状态)

Scheduler

创建vdsm.Scheduler线程,这个线程的作用是:等待其他线程分配给自己要给callable的任务,并在delay时间之后在本线程中执行此callable。和内核中的scheduler相同原理。

使用方式为:

self._scheduler.schedule(self._period, self._try_to_dispatch)

#在schedule线程中_period时间后执行_try_to_dispatch。

Periodic

周期性任务,其中定义了若干Operation,每个Operation周期性的dispatch。是由Executor模型实现的。

Operation dispatch过程简介:

每个Operation被循环的dispatch到TaskQueue中,并对应Executor模型中TaskQueue的一个task。Operation被dispatch的方式有Exclusive和Non-exclusive模式。Exclusive模式是等当前Operation(task)被执行完毕后才再次被dispatch;Non-exclusive模式是当前Operation(task)被dispatch后接着被dispatch;

Executor模型简介:

Task:执行的一个任务。

TaskQueue:最大长度为max_tasks的task队列,提供put和get功能。

Worker:一个Worker是一个periodic/num线程,它不停的从TaskQueue中取task并执行task。中最多为max_workers

Executor:负责添加task到TaskQueue中(dispatch())。初始创建workers_count个worker,每个worker每处理一个task都有固定时间限制,worker1如果超出时间限制,立刻创建一个新的worker2,这个worker1在执行完本task后就退出。worker最多有max_workers个,当创建新worker时数目达到max_workers则暂不创建。这样就保证了worker数量会在workers_count和max_workers之间。

Libvirtconnection

主要功能有:创建libvirt/events线程,启动libvirt的event_loop和事件处理功能;还提供一个连接libvirt的接口libvirtconnection.get()供使用。

ChannelListener

ovit-guest-agent的宿主机端实现,启动了一个vmchannels线程,其中实现了一个channelListener,用来和宿主机上所有虚拟机内的ovirt-guest-agent交互。交互的数据回调给guestagent的GuestInfo中。

qga

qemu-guest-agent的宿主机端实现。实现机制和periodic一样,通过定义Opertion来使用Executor模型执行周期性的任务。主要有了下面连个任务:

1.逐个虚拟机进行CapabilityCheck:默认5分钟一次,调用libvirt的默认channel ovirt-guest-agent.0,把本主机上的虚拟机内qemu-guest-agent支持的supported_commands信息更新到_capabilities中。(这里使用了"guest-info"命令)

2._cleanup:默认1小时dispatch一次,负责把_capabilities、_guest_info、_last_failure中已被删除虚拟机的信息去除。

关于qga和vmchannels

首先看虚拟机的配置:

    <channel type='unix'>
      <source mode='bind' path='/var/lib/libvirt/qemu/channels/a610dd49-e1ce-4bb3-8e6e-925438e688a4.ovirt-guest-agent.0'/>
      <target type='virtio' name='ovirt-guest-agent.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>
    <channel type='unix'>
      <source mode='bind' path='/var/lib/libvirt/qemu/channels/a610dd49-e1ce-4bb3-8e6e-925438e688a4.org.qemu.guest_agent.0'/>
      <target type='virtio' name='org.qemu.guest_agent.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='2'/>
    </channel>

其中:

ovirt-guest-agent.0是vdsm监听的socket,也是与虚拟机内部的ovirt-guest-agent服务通讯的socket。

org.qemu.guest_agent.0是libvirt默认监听的socket,也是与虚拟机内部qemu-guest-agent服务通讯的socket。

guestagent其后端有qemuguestagent和vmchannels两种实现方式。其中qemuguestagent对应虚拟机内部的qemu-guest-agent,vmchannels对应虚拟机内部的ovirt-guest-agent。

qga = self._qgaGuestInfo()
if qga is not None:
    info.update(qga)
if self.isResponsive():
    info.update(self.guestInfo)

qemuguestagent:

由于qemuguestagent中没有实现update_guest_info没有任何地方调用,所以从qemuguestagent中的GuestInfo永远为空,所以只能从vmchannels中读取。

vmchannels:

每个虚拟机开始运行时会向vmchannels线程中注册一个${uuid}.ovirt-guest-agent.0和回调,vmchannels通过这个回调更新GuestInfo中本虚拟机的信息。使用方式为:

self._channelListener.register(
            self._create,
            self._connect,
            self._onChannelRead,
            self._onChannelTimeout)

#_create:创建sock回调

#_connect:连接sock到${uuid}.ovirt-guest-agent.0回调

#_onChannelRead:收到虚拟机内数据后的回调,会把虚拟机数据写入GuestInfo中。

HSM线程池设计

HSM是vdsm中的主机存储管理,HSM内部自己实现了一套线程池框架,用来完成被分配的任务:

Health

启动vdsmd服务的进程信息上报功能,根据health_monitor_enable配置来决定是否启用此功能。对应health线程,周期性的用metrics客户端发送vdsmd进程信息。

VM

vm线程用来管理和本虚拟机相关的所有事务,在另一篇文章中详细讲解。

最后附上一个查看vdsmd服务进程中线程的python脚本:

import os, commands
NAME = 'vdsmd'
procs = ['/proc/'+p for p in os.listdir('/proc') if os.path.isdir('/proc/'+p) and os.path.exists('/proc/'+p+'/comm')]
for proc in procs:
    with open(proc+'/comm', "rb") as f:
        fields = f.readline().split()
    if fields[0] == NAME:
        vdsm_proc = proc
        break
threads = [vdsm_proc+'/task/'+t for t in os.listdir(vdsm_proc+'/task')]
for thread in threads:
    status,result = commands.getstatusoutput('cat %s/comm'%thread)
    print thread + "    " + result

原文发布于微信公众号 - 虚拟化云计算(openstack_openstack)

原文发表时间:2018-05-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Brian

Linux 基础知识

---- 简要 作为了一个服务端开发人员而言,不仅有强大的内功而且也需要对一些工具和运维方面的知识。Linux毋容置疑是每一个后端开发人员必须熟悉或者精通的“大...

4165
来自专栏运维

DNS服务器(五):使用queryperf对DNS服务器作压力测试

   当我们把DNS服务器配置好后,我们肯定会想测试一下DNS服务器的性能如何,上线后如果请求数够多服务器还能否响应?于是,我们可以使用软件模拟环境,对DNS...

2433
来自专栏乐沙弥的世界

MySQL从库选项log-slave-updates未启用引发的异常

    最近核查一个基于从库复制某张特定的表到另外一个主库调整,未配置log-slave-updates导致表无法正常同步。我们的配置文件中使用了replica...

611
来自专栏vue学习

31、地址新增 — 定义数据结构与获取方式

(1)让我们进入addressEdit.vue页面填写一条地址,ok,现在假设你已经填写完毕。 (2)这个时候我们点击保存按钮且应该为这个按钮添加一个save...

1033
来自专栏拂晓风起

Hibernate配置access Hibernate 连接 access

1094
来自专栏一个爱瞎折腾的程序猿

初次尝试Linux并记录一二

若出现 服务器拒绝了SETP连接,但它监听FTP链接。。。没有安装sshd 解决方案

1041
来自专栏云计算教程系列

如何在Ubuntu 16.04上安装Bro

Bro是一个开源网络分析框架和安全监控应用程序。它将OSSEC和osquery的一些最佳功能集成到一个包中。

1915
来自专栏阮一峰的网络日志

Linux服务器的初步配置流程

开发网站的时候,常常需要自己配置Linux服务器。 本文记录配置Linux服务器的初步流程,也就是系统安装完成后,下一步要做的事情。这主要是我自己的总结和备忘,...

7266
来自专栏编程

爬虫基本原理完全梳理及常用解析方式

什么是爬虫:即网络爬虫,可以理解为在网络上爬行的一只蜘蛛,互联网可以比喻为一张大网,一只蜘蛛在爬行时遇到了所需的资源就可以把它爬取下来。简单来说,爬虫就是请求网...

2017
来自专栏JetpropelledSnake

Python Web学习笔记之Cookie,Session,Token区别

2387

扫码关注云+社区