Nova 与虚拟机管理
戢友
2012/11/10
Self-introduce
• Eucalyptus 云计算    2010.5 ~2010.10
• OpenStack 企业私有云     2010.10~ 今
• 快速部署 + 模块添加 +Nova 定制
概要
Nova 与虚拟机管理
Nova Traps and Pitfalls
Nova 开发 VS Eucalyptus 开发
Nova 在 OpenStack 中的位置
• Nova-Compute 的结构
Nova 的核心部件
Nova 源码分析
• Hacking on OpenStack’s Nova Source code

• http://e.gensee.com/v_154692_2



• 重点 :

• 阅读源码 + 按需定制与修改

• Make hands dirty.

• 虚拟机建立流程
Traps & Pitfalls
Compute 服务的稳定性
• Compute 服务不稳定因素

• libvirt >= 0.9.13

经测试稳定。低版本的 libvirt 有异步操作 bug 。容易导致 lib
virtd 服务死掉。升级吧,不用犹豫了。
• 锁机制

compute 模块在与 libvirt 交互时,没有采用锁机制。
• 信息更新

compute 模块有不少定时服务去刷 libvirt 关于虚拟机的信息
。
锁机制
•   为了保证libvirtd服务的正常运行。在compute通过libvirt_connection访问libvirt时,加了锁机制。
class Connection(object):
    def __init__(self):
      Connection.getInst()
    __libvirt_conn = None
    __inst = None
    __lock = threading.Lock()


    @property
    def uri(self): ….
    @staticmethod
    def getInst():
      auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT],
            'root',
            None]
      Connection.__lock.acquire()
      if not Connection.__libvirt_conn:
         Connection.__inst = object.__new__(Connection)
         object.__init__(Connection.__inst)
         Connection.__libvirt_conn = libvirt.openAuth(Connection.__inst.uri, auth, 0)
      Connection.__lock.release()
      return Connection.__inst


    @utils.synchronized('virt_libvirt_Connection', external=True)
    def __getattr__(self,attr):
      return getattr(Connection.__libvirt_conn, attr)
信息的更新
• 改变传统的自顶向下的询问方式,采用自底向上的信息更
 新。




db.instance_get_all_by_host(host)


改为利用 libvirt 收集到的本机的虚拟机,然后直接针对 insta
nce 进行更新:
db._instance_update()
虚拟机的快速启动
• 常规启动多台虚拟机:
利用 qcow2 快速启动
• 快速启动原理:
                     增量文件




      backing_file




• 增量文件只保存不一致的部分
Traps & Pitfalls
• 通过 FLAGS.use_cow_images=True 则使用快速启动。

• 增量文件的格式: qcow2



• Xen 快速启动的陷阱

• Windows 快速启动的缺陷

• 何时不应该使快速启动?
Xen 快速启动
Xen 快速启动的条件 :
• Backing file 格式需要使用 raw 格式。

• 增量文件使用 qcow2 格式。

• 需要用 Xen 自带的 qemu-img 工具生成 qcow2 文件。不建
 议用标准的 qemu 。因为 Xen 的 stubdom 就是一个修改过
 的 qemu 。
• 底层 libvirt XML 文件的配置 :
  <emulator>/usr/lib/xen/bin/qemu-dm</emulator>
  <disk type='file' device='disk'>
    <driver name='tap' type='qcow2'/>
    <source file='${basepath}/disk'/>
    <target dev='hda' bus='ide'/>
  </disk>
Windows 的快速启动
• Windows Image 在移植至 OpenStack 时,利用快速启动容
 易遇到蓝屏问题。
• 1 、 virtio driver.

   virtio-win-0.1-22.iso
   virtio-win-1.1.16.vfd
• 2 、 qcow2 增量文件大小指定需要比原有

       windows image 大。一般 2~3 倍没有问题。
何时不要使用快速启动
• Qcow2 文件缺点:

删除文件时,并没真正将文件从磁盘中删除。只是设置此文
件失效。
如果需要在 Openstack 的虚拟机中建立存储服务,不建议使
用快速启虚拟机模式。
设置 :
/etc/nova/nova.conf
FLAGS.use_cow_images=False
Snapshot
• Nova 的 snapshot 是针对磁盘而言,并没有做内存的 snapsh
ot 。
• Nova 做 Snapshot 的大致流程:
Snapshot 流程
1   定位至相应的虚拟机。
2   将虚拟机挂起:即内存保存至 checkpoint 文件中。
3   利用 qemu-img 对磁盘建立 snapshot 。
4   整合磁盘文件与 snapshot 生成一个新的独立的磁盘。
5   将磁盘上传至 Glance 服务。
利用 snapshot 进行恢复
1 从 glance 下载 image 。
2 利用这个新的 image 启动一个虚拟机。



问题:
Snapshot: 相当于把原有虚拟机的磁盘上传至 glance 。
恢复        : 从 glance 上下载此磁盘,新开一个虚拟机。和原
来的虚拟机关系不大。

时间漫长………………………… .
快速 Snapshot
需求: 1 、快速地做 Snapshot 与恢复
   2 、必要时,才把 image 上传至 Glance 。
        不用每次都上传。

步骤:
1 做 snapshot :
$qemu-img snapshot –c snapshot_name disk_path 创建 snapshot.
此时不用将 image 上传至 glance
2 恢复时:
$ qemu-img snapshot –a snapshot_name disk_path
亦不用从 glance 下载磁盘进行恢复
3 必要时,将虚拟机磁盘上传至 glance
image_service.update(context, image_href, metadata, image_file)
快速 snapshot
优点
1 并不是每次 snapshot 都涉及到 glance 。
  都是在本地操作。因此可以很快地完成。
2 可以把多次 snapshot 放在一个磁盘里面,
  直接上传至 Glance 。不用把多个 snapshot 单独上传。
快速 Snapshot
Note:

磁盘 snapshot 针对 qcow2 格式磁盘较易操作。对于 raw 格式
磁盘不易进行。

亦可以在针对磁盘做 snapshot 时,同时对内存做 snapshot 。
这样可以做到对虚拟机的点恢复。

在做 snapshot 时,建议用 qemu-img 针对磁盘做 snapshot 。
利用 libvirt 对内存做快照。不建议利用 libvirt 提供的 snapshot
功能。因为 libvirt 提供的 snapshot 在操作时,会把部分消息保
存至 libivrt 的数据库中。增删易造成不一致。
Terminate 虚拟机
如果创建虚拟机失败。导致虚拟机并没有正常启动。那么 Terminate 操作亦
会失效。看一下流程:
def do_terminate_instance():
     elevated = context.elevated()
     instance = self.db.instance_get_by_uuid(elevated, instance_uuid)
     compute_utils.notify_usage_exists(instance, current_period=True)
     try:
        self._delete_instance(context, instance)
     except exception.InstanceTerminationFailure as error:
        msg = _('%s. Setting instance vm_state to ERROR')
        LOG.error(msg % error, instance_uuid=instance_uuid)
        self._set_instance_error_state(context, instance_uuid)
     except exception.InstanceNotFound as e:
        LOG.warn(e, instance_uuid=instance_uuid)
Live Migration
Live Migration 支持两种
shared storage.
Non-shared storage.

为什么迁移经常出错?哪里出了问题?
检测目标机器

                                 根据
             是否需要从
                            block_migration
 检测目标机器      glance 下载
                            决定是否需要迁
             backing file
                                 移磁盘
Live Migration 逻辑陷阱
                              传入
                       block_migration 参数



     Admin 有 1000 台机
                                        block_migration 决定
     器,他并不清楚这两
                                          是否迁移 VM 的
     台机器是否是共享存
                                              image
            储




          最终是否迁移 image            是否迁移 image 是由
          由参数与底层存储决               底层存储是否共享决
              定                       定
Live Migration
不需参数指定。直接使用
block_migration = self. mounted_on_same_shared_storage()
来进行决定。



 CPU 的比较
 底层基于 libvirt 的 CPU 比较过于严格,可以放松条件。厂商
 一致,型号差别不是太远,基本可以完成迁移。
Live Migration
迁移时,去掉下面这个参数,则否会导致重新从 glance 下载
backing file.

image.cache(fetch_func=libvirt_utils.fetch_image,
       context=ctxt,
       filename=cache_name,
       image_id=instance['image_ref'],
       user_id=instance['user_id'],
       project_id=instance['project_id'],
       size=info['virt_disk_size'])
Live Migration 什么时候可以使用
总的前提条件:网络数据传输允许!数据传输有独立的通道!
比如:可以的情况
计算密集型 + 小磁盘: 迁移量 = 内存 + 增量磁盘
计算节点之间采用共享存储
允许小概率的迁移失败

不建议的情况:
非共享存储 + 大磁盘
关键服务 + 不允许迁移失败
Xen 在 OpenStack 状况
python-libvirt 对 Xen 的支持并不好。一些功能都不能实现。底
层虚拟机启动配置需要修改。
live_migration
pause/unpause
suspend/resume
save/restore

底层实现最好是利用 xen-tools 来进行操作或者
XenAPI 。 Virsh 在一定程度上也可以帮忙。
OpenStack VS Eucalyptus
OpenStack         cc

松耦合 / 异步消息传递与函数调用
python rest API 易修改与定制         cc
Eucalptus
上层发布 ec2 接口的 java 代码混乱
                         clc
模块连续紧密
层层调用的消息传递与函数调用                 nc
c/axis2c/WSDL 开发调试难度加大

Nova与虚拟机管理

  • 1.
  • 2.
    Self-introduce • Eucalyptus 云计算 2010.5 ~2010.10 • OpenStack 企业私有云 2010.10~ 今 • 快速部署 + 模块添加 +Nova 定制
  • 3.
    概要 Nova 与虚拟机管理 Nova Trapsand Pitfalls Nova 开发 VS Eucalyptus 开发
  • 4.
    Nova 在 OpenStack中的位置 • Nova-Compute 的结构
  • 5.
  • 6.
    Nova 源码分析 • Hackingon OpenStack’s Nova Source code • http://e.gensee.com/v_154692_2 • 重点 : • 阅读源码 + 按需定制与修改 • Make hands dirty. • 虚拟机建立流程
  • 7.
  • 8.
    Compute 服务的稳定性 • Compute服务不稳定因素 • libvirt >= 0.9.13 经测试稳定。低版本的 libvirt 有异步操作 bug 。容易导致 lib virtd 服务死掉。升级吧,不用犹豫了。 • 锁机制 compute 模块在与 libvirt 交互时,没有采用锁机制。 • 信息更新 compute 模块有不少定时服务去刷 libvirt 关于虚拟机的信息 。
  • 9.
    锁机制 • 为了保证libvirtd服务的正常运行。在compute通过libvirt_connection访问libvirt时,加了锁机制。 class Connection(object): def __init__(self): Connection.getInst() __libvirt_conn = None __inst = None __lock = threading.Lock() @property def uri(self): …. @staticmethod def getInst(): auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT], 'root', None] Connection.__lock.acquire() if not Connection.__libvirt_conn: Connection.__inst = object.__new__(Connection) object.__init__(Connection.__inst) Connection.__libvirt_conn = libvirt.openAuth(Connection.__inst.uri, auth, 0) Connection.__lock.release() return Connection.__inst @utils.synchronized('virt_libvirt_Connection', external=True) def __getattr__(self,attr): return getattr(Connection.__libvirt_conn, attr)
  • 10.
    信息的更新 • 改变传统的自顶向下的询问方式,采用自底向上的信息更 新。 db.instance_get_all_by_host(host) 改为利用libvirt 收集到的本机的虚拟机,然后直接针对 insta nce 进行更新: db._instance_update()
  • 11.
  • 12.
    利用 qcow2 快速启动 •快速启动原理: 增量文件 backing_file • 增量文件只保存不一致的部分
  • 13.
    Traps & Pitfalls •通过 FLAGS.use_cow_images=True 则使用快速启动。 • 增量文件的格式: qcow2 • Xen 快速启动的陷阱 • Windows 快速启动的缺陷 • 何时不应该使快速启动?
  • 14.
    Xen 快速启动 Xen 快速启动的条件: • Backing file 格式需要使用 raw 格式。 • 增量文件使用 qcow2 格式。 • 需要用 Xen 自带的 qemu-img 工具生成 qcow2 文件。不建 议用标准的 qemu 。因为 Xen 的 stubdom 就是一个修改过 的 qemu 。 • 底层 libvirt XML 文件的配置 : <emulator>/usr/lib/xen/bin/qemu-dm</emulator> <disk type='file' device='disk'> <driver name='tap' type='qcow2'/> <source file='${basepath}/disk'/> <target dev='hda' bus='ide'/> </disk>
  • 15.
    Windows 的快速启动 • WindowsImage 在移植至 OpenStack 时,利用快速启动容 易遇到蓝屏问题。 • 1 、 virtio driver. virtio-win-0.1-22.iso virtio-win-1.1.16.vfd • 2 、 qcow2 增量文件大小指定需要比原有 windows image 大。一般 2~3 倍没有问题。
  • 16.
    何时不要使用快速启动 • Qcow2 文件缺点: 删除文件时,并没真正将文件从磁盘中删除。只是设置此文 件失效。 如果需要在Openstack 的虚拟机中建立存储服务,不建议使 用快速启虚拟机模式。 设置 : /etc/nova/nova.conf FLAGS.use_cow_images=False
  • 17.
    Snapshot • Nova 的snapshot 是针对磁盘而言,并没有做内存的 snapsh ot 。 • Nova 做 Snapshot 的大致流程:
  • 18.
    Snapshot 流程 1 定位至相应的虚拟机。 2 将虚拟机挂起:即内存保存至 checkpoint 文件中。 3 利用 qemu-img 对磁盘建立 snapshot 。 4 整合磁盘文件与 snapshot 生成一个新的独立的磁盘。 5 将磁盘上传至 Glance 服务。
  • 19.
    利用 snapshot 进行恢复 1从 glance 下载 image 。 2 利用这个新的 image 启动一个虚拟机。 问题: Snapshot: 相当于把原有虚拟机的磁盘上传至 glance 。 恢复 : 从 glance 上下载此磁盘,新开一个虚拟机。和原 来的虚拟机关系不大。 时间漫长………………………… .
  • 20.
    快速 Snapshot 需求: 1、快速地做 Snapshot 与恢复 2 、必要时,才把 image 上传至 Glance 。 不用每次都上传。 步骤: 1 做 snapshot : $qemu-img snapshot –c snapshot_name disk_path 创建 snapshot. 此时不用将 image 上传至 glance 2 恢复时: $ qemu-img snapshot –a snapshot_name disk_path 亦不用从 glance 下载磁盘进行恢复 3 必要时,将虚拟机磁盘上传至 glance image_service.update(context, image_href, metadata, image_file)
  • 21.
    快速 snapshot 优点 1 并不是每次snapshot 都涉及到 glance 。 都是在本地操作。因此可以很快地完成。 2 可以把多次 snapshot 放在一个磁盘里面, 直接上传至 Glance 。不用把多个 snapshot 单独上传。
  • 22.
    快速 Snapshot Note: 磁盘 snapshot针对 qcow2 格式磁盘较易操作。对于 raw 格式 磁盘不易进行。 亦可以在针对磁盘做 snapshot 时,同时对内存做 snapshot 。 这样可以做到对虚拟机的点恢复。 在做 snapshot 时,建议用 qemu-img 针对磁盘做 snapshot 。 利用 libvirt 对内存做快照。不建议利用 libvirt 提供的 snapshot 功能。因为 libvirt 提供的 snapshot 在操作时,会把部分消息保 存至 libivrt 的数据库中。增删易造成不一致。
  • 23.
    Terminate 虚拟机 如果创建虚拟机失败。导致虚拟机并没有正常启动。那么 Terminate操作亦 会失效。看一下流程: def do_terminate_instance(): elevated = context.elevated() instance = self.db.instance_get_by_uuid(elevated, instance_uuid) compute_utils.notify_usage_exists(instance, current_period=True) try: self._delete_instance(context, instance) except exception.InstanceTerminationFailure as error: msg = _('%s. Setting instance vm_state to ERROR') LOG.error(msg % error, instance_uuid=instance_uuid) self._set_instance_error_state(context, instance_uuid) except exception.InstanceNotFound as e: LOG.warn(e, instance_uuid=instance_uuid)
  • 24.
    Live Migration Live Migration支持两种 shared storage. Non-shared storage. 为什么迁移经常出错?哪里出了问题? 检测目标机器 根据 是否需要从 block_migration 检测目标机器 glance 下载 决定是否需要迁 backing file 移磁盘
  • 25.
    Live Migration 逻辑陷阱 传入 block_migration 参数 Admin 有 1000 台机 block_migration 决定 器,他并不清楚这两 是否迁移 VM 的 台机器是否是共享存 image 储 最终是否迁移 image 是否迁移 image 是由 由参数与底层存储决 底层存储是否共享决 定 定
  • 26.
    Live Migration 不需参数指定。直接使用 block_migration =self. mounted_on_same_shared_storage() 来进行决定。 CPU 的比较 底层基于 libvirt 的 CPU 比较过于严格,可以放松条件。厂商 一致,型号差别不是太远,基本可以完成迁移。
  • 27.
    Live Migration 迁移时,去掉下面这个参数,则否会导致重新从 glance下载 backing file. image.cache(fetch_func=libvirt_utils.fetch_image, context=ctxt, filename=cache_name, image_id=instance['image_ref'], user_id=instance['user_id'], project_id=instance['project_id'], size=info['virt_disk_size'])
  • 28.
    Live Migration 什么时候可以使用 总的前提条件:网络数据传输允许!数据传输有独立的通道! 比如:可以的情况 计算密集型+ 小磁盘: 迁移量 = 内存 + 增量磁盘 计算节点之间采用共享存储 允许小概率的迁移失败 不建议的情况: 非共享存储 + 大磁盘 关键服务 + 不允许迁移失败
  • 29.
    Xen 在 OpenStack状况 python-libvirt 对 Xen 的支持并不好。一些功能都不能实现。底 层虚拟机启动配置需要修改。 live_migration pause/unpause suspend/resume save/restore 底层实现最好是利用 xen-tools 来进行操作或者 XenAPI 。 Virsh 在一定程度上也可以帮忙。
  • 30.
    OpenStack VS Eucalyptus OpenStack cc 松耦合 / 异步消息传递与函数调用 python rest API 易修改与定制 cc Eucalptus 上层发布 ec2 接口的 java 代码混乱 clc 模块连续紧密 层层调用的消息传递与函数调用 nc c/axis2c/WSDL 开发调试难度加大