服务初始化阶段

nova-compute服务启动时调用manager中的host初始化函数

self.manager.init_host()

在host初始化函数中完成如下操作:

#初始化libvirt的事件处理
self.driver.init_host(host=self.host) #注册生命周期事件的处理函数
self.init_virt_events() #处理evacuated的虚拟机
通过libvirt接口获取本节点上所有的虚拟机,再查询这些虚拟机在数据库中的host信息。如果host与当前节点不一致,说明是已经撤离的虚拟机,直接destroy。
self._destroy_evacuated_instances(context) #虚拟机状态同步
for instance in instances:
wait_ticks = self._init_instance(context, instance,
wait_ticks=wait_ticks)

_init_instance完成了虚拟机状态的同步,同步规则如下:

  1. 如果数据库中虚拟机已经shutdown,或者处于error状态,并且任务状态不是resize_migrating,则不做任何处理。如果任务状态是resize_migrating的话,后续还要做一些处理。
  2. 如果虚拟机已经处于deleted状态,但是数据库中还没有标记为删除,则需要更新配额相关的数据,并且删除数据库中的记录。此虚拟机状态即恢复完毕,开始恢复下一台。
  3. 如果虚拟机不是上面的状态,则需要恢复云主机的网络设备(当前配置的vif_driver是LibvirtGenericVIFDriver,tap设备由libvirt生成管理,这里就直接跳过了)
  4. 如果虚拟机的task状态为resize_migrating,说明在迁移过程中服务关闭了,保险起见需要恢复虚拟机状态finish_revert_migration
  5. 以上操作完成之后,如果数据库中记录的状态是running但是节点上虚拟机状态不为running,通过resume_state_on_host_boot启动虚拟机(实际上是hard_reboot操作,但是不更新数据库状态)。

init_host中对事件处理的初始化:

#注册异常处理函数,这里的libvirt_error_handler是空的,也就是异常不做处理
libvirt.registerErrorHandler(libvirt_error_handler, None)
#向libvirt注册一个事件
libvirt.virEventRegisterDefaultImpl()
#
self._init_events()

_init_events中:

#创建一个队列,用于存储事件消息
#创建一对管道,用于事件消息的通知
self._init_events_pipe()
#启动一个系统原生线程,线程内用循环监听上面注册的libvirt事件。
_native_thread
libvirt.virEventRunDefaultImpl()
#启动一个绿色线程,线程内用一个循环分发监听到的libvirt事件。
eventlet.spawn(self._dispatch_thread)

事件分发流程_dispatch_thread

#读取上面建立的管道内容,如果读出数据,说明队列中有消息待处理。没有消息则退出此次循环。
_c = self._event_notify_recv.read(1)
#尝试读取事件队列
event = self._event_queue.get(block=False)
#如果是生命周期事件,则进入生命周期事件处理函数
self.emit_event(event)
#处理连接断开事件(告警日志打印,重置nova与libvirt的连接conn)
conn = last_close_event['conn']

生命周期事件处理函数emit_event(self, event)

#调用注册的事件处理函数
self._compute_event_callback(event)

注册事件处理函数init_virt_events

#此处注册了handle_events作为生命周期事件的处理函数
self.driver.register_event_listener(self.handle_events)

handle_events-->>handle_lifecycle_event

#按照如下的关系同步虚拟机在openstack层的电源状态
#EVENT_LIFECYCLE_STOPPED -> SHUTDOWN
#EVENT_LIFECYCLE_STARTED -> RUNNING
#EVENT_LIFECYCLE_PAUSED -> PAUSED
#EVENT_LIFECYCLE_RESUMED -> RUNNING self._sync_instance_power_state(context,
instance,
vm_power_state)

_sync_instance_power_state

#如果虚拟机的宿主机不是当前节点,说明虚拟机做了迁移,这种虚拟机直接跳过,不做同步。
if self.host != db_instance.host #虚拟机的任务状态不为空,说明当前事件只是一个任务的中间状态,也直接跳过不做处理
elif db_instance.task_state is not None #事件上报的虚拟机电源状态与数据库电源状态不一致的情况下,更新数据库中的虚拟机电源状态。
if vm_power_state != db_power_state:
db_instance.power_state = vm_power_state
db_instance.save()
#数据库中的虚拟机状态为ACTIVE
#接收到SHUTDOWN/CRASHED -> call stop api
#接收到SUSPENDED -> call stop api
#接收到PAUSED -> 虚拟机异常pause,ignore
#接收到NOSTATE -> 虚拟机丢失,忽略
#数据库中的虚拟机状态为STOPPED,而上报的生命周期事件不是NOSTATE/SHUTDOWN/CRASHED其中之一,则强制关闭虚拟机。
self.compute_api.force_stop(context, db_instance)
#数据库中的虚拟机状态为PAUSED,上报的生命周期事件为SHUTDOWN/CRASHED,则认为一个暂停状态的虚拟机被关机了,强制关闭虚拟机。
self.compute_api.force_stop(context, db_instance)
#数据库中虚拟机状态为SOFT_DELETED或者DELETED,而上报的事件不是NOSTATE或者SHUTDOWN,则发出日志告警。

nova-compute服务启动时,libvirt driver会同步加载,并与libvirt建立一个长连接。通过这个连接注册了libvirt的生命周期事件的回调函数

#注册生命周期事件,只有这些事件发生时,后面virEventRunDefaultImpl才会被触发。
wrapped_conn.domainEventRegisterAny(
None,
libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
self._event_lifecycle_callback,
self)

当libvirt监听到事件发生时,会调用注册的回调函数

#将事件添加到队列中
self._queue_event(virtevent.LifecycleEvent(uuid, transition))

_queue_event

#加入队列
self._event_queue.put(event)
#通过管道通知给dispatch绿色线程
c = ' '.encode()
self._event_notify_send.write(c)
self._event_notify_send.flush()

定时任务同步

nova-compute在服务启动的最后阶段启动了一个定时任务_sync_power_states。这个定时任务的主要功能是同步节点上的虚拟机电源状态与数据库记录保持一致。最终也是通过与事件同步一样的_sync_instance_power_state同步电源状态。

总结

nova中的状态同步有以下几种情况:

1.服务启动时

  • 节点上执行了evacuate操作的虚拟机直接destroy删除。
数据库状态 节点状态 任务状态 处理
SOFT_DELETED - 非RESIZE_MIGRATING -
ERROR - 非RESIZE_MIGRATING -
DELETED - - 清理资源
- - RESIZE_MIGRATING 回滚迁移操作
RUNNING 非RUNNING - 启动

2.事件通知及定时任务的状态同步

数据库状态 上报状态 处理
ACTIVE SHUTDOWN stop
ACTIVE CRASHED stop
ACTIVE SUSPENDED stop
ACTIVE PAUSED ignore
ACTIVE NOSTATE ignore
STOPPED 非NOSTATE/SHUTDOWN/CRASHED destroy
PAUSED SHUTDOWN/CRASHED destroy
SOFT_DELETED 非NOSTATE/SHUTDOWN 日志告警
DELETED 非NOSTATE/SHUTDOWN 日志告警

本文来自网易云社区,经作者岳文远授权发布。

原文地址:nova状态同步

更多网易研发、产品、运营经验分享请访问网易云社区

nova状态同步的更多相关文章

  1. Postman+Postman interceptor的安装和使用-解决把chrome浏览器登录状态同步到postman进行有依赖的接口测试 Postman 使用方法详解

    Postman+Postman interceptor的安装和使用-解决把chrome浏览器登录状态同步到postman进行有依赖的接口测试   问题引入:做接口测试时,有依赖关系的接口往往不好测试( ...

  2. 图解kubernetes容器状态同步机制核心实现

    在K8s中将Pod调度到某一台Node节点之后,后续的状态维护信息则是由对应机器上的kubelet进行维护,如何实时反馈本地运行状态,并通知apiserver则是设计的难点, 本节主要是通过感知Pod ...

  3. vue父子组件状态同步的最佳方式

    哈喽!大家好!我是木瓜太香,一位老牌儿前端工程师,平时我们在使用 vue 开发的时候,可能会遇到需要父组件与子组件某个状态需要同步的情况,通常这个是因为我们封装组件的时候有一个相同的状态外面要用,里面 ...

  4. vue父子组件状态同步的最佳方式续章(v-model篇)

    大家好!我是木瓜太香!一名前端工程师,之前写过一篇<vue父子组件状态同步的最佳方式>,这篇文章描述了大多数情况下的父子组件同步的最佳方式,也是被开源中国官方推荐了,在这里表示感谢! 这次 ...

  5. zookeeper有几种部署模式? zookeeper 怎么保证主从节点的状态同步?

    一.zookeeper的三种部署模式 Zookeeper 有三种部署模式分别是单机模式.伪集群模式.集群模式.这三种模式在不同的场景下使用: 单机部署:一般用来检验 Zookeeper 基础功能,熟悉 ...

  6. Android系统移植与调试之------->增加一个双击物理按键打开和关闭闪光灯并将闪光灯状态同步到下拉菜单中

    最近有一个客户有这样的需求: 1.在[设置]--->[无障碍]中添加一个开关按钮. 如果打开开关的话,双击某个物理按键的时候,打开闪光灯,再双击该物理按键的时候,关闭闪光灯. 如果关闭开关的话, ...

  7. java线程控制、状态同步、volatile、Thread.interupt以及ConcurrentLinkedQueue

    在有些严格的系统中,我们需要做到干净的停止线程并清理相关状态.涉及到这个主题会带出很多的相关点,简单的总结如下: 我们知道,在java中,有一个volatile关键字,其官方说明(https://do ...

  8. java多线程之hashmap concurrenthashmap的状态同步

    最近在高并发的系统中发现,concurrenthashmap除了大家熟知的避免循环期间发生ConcurrentModificationException异常外,还有重要的一点是Retrievals r ...

  9. 浅谈js拖拽

    本文来自网易云社区 作者:刘凌阳 前言 本文依据半年前本人的分享<浅谈js拖拽>撰写,算是一篇迟到的文章. 基本思路 虽然现在关于拖拽的组件库到处都是,HTML5也把拖放纳入了标准.但考虑 ...

随机推荐

  1. git版本超前了N个版本且落后了N个版本的解决办法

    当遇到该问题的时候,一般情况下我们会首先拉取,紧接着就出现了一系列问题………… 其实当输入命令,git pull的时候,会报错,这时瞬间就感到凌乱了&……*%%*%………… 莫慌…… 这时输入 ...

  2. 域名检索&路由算法

    域名查询顺序: a. 浏览器缓存(本机hosts文件),浏览器会缓存DNS记录一段时间. b. 系统缓存 c. 路由器缓存 d. 检查ISP e. 递归搜索域名服务器 路由算法: 一.静态路由算法 a ...

  3. c# 二分查找法

    1.仅 当 列表 是 有序 的 时候, 二分 查找 才 管用. 2.一般而言, 对于 包含 n 个 元素 的 列表, 用 二分 查找 最多 需要 log2n 步, 而 简单 查找 最多 需要 n 步. ...

  4. 如何在 Azure 中均衡 Windows 虚拟机负载以创建具有高可用性的应用程序

    负载均衡通过将传入请求分布到多个虚拟机来提供更高级别的可用性. 本教程介绍了 Azure 负载均衡器的不同组件,这些组件用于分发流量和提供高可用性. 你将学习如何执行以下操作: 创建 Azure 负载 ...

  5. MySQL客户端连接方式

    MySQL连接方式MySQL除了最常见的TCP连接方式外,还提供SOCKET(LINUX默认连接方式).PIPE和SHARED MEMORY连接方式.各连接方式的服务器.客户端启动选项,及连接默认值见 ...

  6. python-异常处理try_except

    异常处理try-except 在我们写程序的时候经常会遇到一些异常或错误,导致程序终止 当我们使用计算器时,用10除以0会提示 一个简单的错误代码(10/0) a = 10 / 0 print(&qu ...

  7. Access restriction: The type BASE64Encoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar

    解决方案:在configure build path 中去掉 jre system library,然后重新加载jre system library.....

  8. 【转】Java学习---volatile 关键字

    [原文]https://www.toutiao.com/i6591422029323305480/ 前言 不管是在面试还是实际开发中 volatile 都是一个应该掌握的技能. 首先来看看为什么会出现 ...

  9. T4学习- 3、创建运行时模板

    使用 Visual Studio 预处理过的文本模板,可以在运行时在应用程序中生成文本字符串. 执行应用程序的计算机不必具有 Visual Studio. 预处理过的模板有时称为"运行时文本 ...

  10. SDN2017 第二次实验作业

    安装floodlight 参考链接:http://www.sdnlab.com/19189.html 从github下载源码,并编译安装 $ sudo apt-get install build-es ...