服务初始化阶段

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. Prometheus Node_exporter 之 Network Netstat UDP

    Network Netstat UDP /proc/net/snmp 1. UDP In / Out type: GraphUnit: shortLabel: Datagrams out (-) / ...

  2. python给邮箱发送消息

    首先要用到两个模块  并且大同你的发送邮箱smtp 最开始测试没打通了好久 smtplib是提供邮箱smtp服务, email是提供你发送消息的格式之类服务 import smtplib from e ...

  3. git 命令行下浏览器tig使用记录

    git 命令行下浏览器tig使用记录 tig 是一款优化 git 命令行的工具,使 git 命令行更加的便捷人性化 .如果用习惯了,会上瘾. 以下是一些使用记录: 安装成功后,在 Repo 文件夹下, ...

  4. vuejs安装篇

    1.安装nodejs,自带npm环境. 地址:https://nodejs.org/en/download/,Node.js 历史版本下载地址:https://nodejs.org/dist/  可自 ...

  5. SQLSERVER无排序生成序号

    实现方式:ROW_NUMBER() SELECT RowID=(ROW_NUMBER() OVER(ORDER BY(SELECT ))) FROM dbo.tbl_name 实现方式:IDENTIT ...

  6. PyQt5 的几个核心模块作用

    QtCore  包含了核心的非GUI功能.此模块用于处理时间.文件和目录.各种数据类型.流.URL.MIME类型.线程或进程. QtGui  包含类窗口系统集成.事件处理.二维图形.基本成像.字体和文 ...

  7. Secure Shell相关设置

    1.清空known hosts记录 ctrl+shift+j调出js控制台后,输入: term_.command.removeAllKnownHosts()

  8. 13.5.SolrCloud集群使用手册之数据导入

    转载请出自出处:http://www.cnblogs.com/hd3013779515/ 1.使用curl命令方式 SolrCloud时会根据路由规则路由到各个shard. 删除所有数据 curl h ...

  9. 前端aes解密实战小结

    很多人对于AES加密并不是很了解,导致互相之间进行加密解密困难. 本文用简单的方式来介绍AES在使用上需要的知识,而不涉及内部算法.最后给出例子来帮助理解AES加密解密的使用方法. AES的麻烦 相比 ...

  10. mod_php和mod_fastcgi和php-fpm的介绍,对比和性能数据

    1.php中fastcgi和php-fpm是什么东西 最近在研究和学习php的性能方面的知识,看到了factcgi以及php-fpm,发现我对他们是少之又少的理解,可以说几乎是一无所知,想想还是蛮可怕 ...