OpenStack计算模块Nova架构及问题源码剖析

1. OpenStack简述

提起OpenStack,大家都不陌生。 OpenStack实质是亚马逊AWS的开源实现,在短短几年的发展历程中,已经成为了云计算开源领域的事实标准,得到了国内外知名企业的大力贡献和支持,OpenStack可以支持KVM、XEN、LXC等虚拟化技术。

OpenStack每半年发布一个版本,目前最新版本是Queens 版,主要包括虚拟化计算(Nova)、裸金属(Ironic )、容器管理(Zun)、块存储(Cinder)、对象存储(Swift)、共享文件存储(Manila )、数据库(Trove )、镜像(Glance)、网络(Neutron)、编排(Heat )、认证(Keystone)、计费(Cloudkitty)以及用户界面(Horizon)等核心模块。其中,Nova是管理虚拟机生命周期的核心模块,存储、镜像、网络等资源都是附属于Nova创建的虚拟机来对外提供服务。因此,本文将重点介绍Nova架构,并结合实践遇到的具体问题进行剖析。

2. nova架构

2.1 Nova基本概念

对OpenStack而言,Nova是一个虚拟机管理系统。Nova是OpenStack早期版本中最核心组件。掌握Nova的管理流程后,对OpenStack的整体会有更清晰的认识。虚拟机的创建和管理,流程复杂,会涉及存储、用户、安全及网络等相关资源。Nova计算模块和镜像模块、块存储模块、权限认证模块、网络模块以及用户界面模块的交互如图2.1所示。

OpenStack计算模块Nova架构及问题源码剖析

2.1 Nova与其他组件交互图

2.2 Nova核心组件

Nova主要由nova-api、nova-cell、nova-conductor、nova-scheduler和nova-compute等组件组成。Nova内部子服务是通过rabbitmq消息队列通信的,相互关系如图2.2所示。其中,nova-api主要对外提供restful API服务;nova-cell主要是用来解决横向扩展集群规模带来的RabbitMq和MariaDB瓶颈问题;nova-conductor是nova的中枢神经,直接操作数据库;nova-scheduler是nova创建虚拟机的调度器,;nova-compute是创建管理虚拟机生命周期的真正组件,直接调用hypervisor;DB模块一般采用MariaDB集群,存储nova生命周期的所有持久化信息;hypervisor支持KVM、Xen、Hyper-v、LXC等虚拟化技术,是管理虚拟机的最终组件。

OpenStack计算模块Nova架构及问题源码剖析

图2.2 Nova内部子服务交互图

在生产环境中,通常把Nova划分为控制节点和计算节点两种节点来部署。控制节点上主要运行nova-api、nova-conductor、nova-scheduler服务,计算节点上主要运行nova-compute和hypervisor服务。Nova各个组件之间通信通过rabbitmq消息队列通信,组件与组件保持松耦合性。

2.3 nova虚拟机管理流程

Nova对虚拟机的生命周期管理非常复杂,为了简化流程,我们暂时不考虑多Region和多Cell的场景,仅从涉及到的nova-api、nova-conductor、nova-scheduler和nova-compute等4个服务组成的主线上分析虚拟机的生命周期管理。

下面以创建虚拟机的组件调用流程来分析nova对虚拟机的生命周期管理流程。

(1)nova-api:nova-api接收来自前端dashboard或CLI命令行等客户端发出的创建虚拟机的请求命令,是一个restful API服务。当nova-api接收到请求命令后,会通过认证模块验证是否是合法的请求,如果通过验证,请求会被发送到对应的RabbitMq队列。

(2)nova-conductor: nova-conductor是在OpenStack G版本中新增加的一个组件。nova-conductor组件主要是用来避免nova-compute组件直接访问数据库造成的安全隐患。因为计算节点最容易被攻击,有了nova-conductor组件后,nova-compute组件的数据库操作都需要通过向nova-conductor组件发送RPC请求来实现。

(3)nova-scheduler: nova-scheduler是 nova创建虚拟机的调度器,它的主要作用就是选择合适的宿主机创建虚拟机。当从RabbitMq队列里接收到创建虚拟机的请求后,nova-scheduler会查看计算节点集群中所有服务正常的节点,根据filter过滤算法和weight权重后,选择一个最合适的宿主机节点作为目标节点,然后把结果发送到对应的RabbitMq队列。

nova-scheduler最核心的是虚拟机的调度算法,调度算法主要在类FilterScheduler的_schedule方法中定义。以下是_schedule方法的具体实现过程:

  1. 首先通过HostManager类的get_filtered_hosts函数得到可用宿主机的列表;
  2. 然后通过HostManager类的get_weighed_hosts函数计算出上一步过滤出来的宿主机的权值;
  3. 其次,再从过滤的scheduler_host_subset_size个权值最高的宿主机中随机选择一个宿主机作为创建虚拟机的目标节点;
  4. 最后,调用目标宿主机对应的HostState类的consume_from_instance函数,为虚拟机预留所需要的资源,并修改该宿主机的资源配置信息。

(4)nova-compute: nova-compute组件在计算节点上运行,主要用来管理虚拟机的生命周期。在nova-compute源码中,nova-manager接收从RabbitMq队列里的消息任务,而nova-driver是真正管理虚拟机的创建、删除、开机和关机等操作。nova-driver目前支持KVM、Xen、hyper-v、vmware等多种hypervisor,主要通过libvirt来管理KVM进行虚拟化的操作。nova-compute处理RPC请求是一个ComputeManager对象,ComputeManager对象通过run_instance方法处理虚拟机的创建命令。run_instance方法首先检查创建虚拟机的目标宿主机是否具有足够的硬件资源,然后向虚拟机分配网络资源,最后调用nova-driver的spawn方法孵化虚拟机。

nova-driver的spawn方法首先创建虚拟机磁盘镜像和虚拟机的XML定义文件,然后创建虚拟机和分配网络资源,最后启动一个_wait_for_boot定时线程,它会自动检查虚拟机的电源状态,当虚拟机的电源状态为running时,表示成功启动了虚拟机。

3. Nova应用问题剖析

3.1 Nova容量型和性能型存储

nova默认是不支持存储池选择的功能,但是在公有云中,有个常规需求,用户可以自由选择虚拟机的类型,主要包括容量型、性能型和超高性能型。容量型的虚拟机主要创建在普通的机械硬盘上,性能型的虚拟机创建在SSD硬盘上,超高性能型虚拟机创建在PCIE SSD硬盘上。为了支持这种用户需求,有几种方式:

  • 第一, 直接利用Availability Zone和Host Aggregate把不同存储类型的机器划分为不同的集合,虽然也可以实现对不同存储类型的支持,但是会有局限性。比如,本地存储场景下,需要保证单台计算节点的所有磁盘类型都要一致,有点不现实。
  • 第二, 利用cinder的volume type来创建不同类型的数据盘,然后挂载到虚拟机上。这种方式支持不同存储类型的存储,仅限于数据盘,本地系统盘会受限。
  • 第三, 需要对nova进行源码修改:

(1) 首先,需要在nova数据库的instances表中增加instance_disk_type字段。

源码如下:

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/nova/db/sqlalchemy/models.py

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack ~]# nova-manage db sync --version 379

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/nova/objects/instance.py

OpenStack计算模块Nova架构及问题源码剖析

(2)然后,需要在novaclient模块增加instance_disk_type请求参数字段,表示虚拟机的存储类型。增加虚拟机磁盘类型字段后,重启控制节点的nova-api服务,通过nova help boot 命令可以查询到增加的instance_disk_type请求参数。

源码如下:

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/novaclient/v2/shell.py

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack ~]# vim /usr/lib/python2.7/site-packages/novaclient/v2/servers.py

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

(3)接着在nova-api、nova-conductor、nova-compute等一系列子服务的创建虚拟机的接口方法中增加虚拟机存储类型参数。

源码如下:

[root@openstack~]# vim \

/usr/lib/python2.7/site-packages/nova/api/openstack/compute/servers.py

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/nova/compute/api.py

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

(4)最后在nova-driver模块, 根据前端传入的虚拟机存储类型参数,判断后台虚拟机的存储池。例如,当判断前端传入的instance_disk_type值为pcie_ssd时,虚拟机的目标存储池指向超高性能存储池volumes_pcie_ssd; 当判断前端传入的instance_disk_type值为ssd时,虚拟机的目标存储池指向性能存储池volumes_ssd;当判断前端传入的instance_disk_type值为hdd时,虚拟机的目标存储池指向容量存储池volumes_hdd。

源码如下:

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/nova/virt/libvirt/driver.py

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack~]#vim \

/usr/lib/python2.7/site-packages/nova/virt/libvirt/imagebackend.py

OpenStack计算模块Nova架构及问题源码剖析

OpenStack计算模块Nova架构及问题源码剖析

经过以上修改后,用户可以在界面上自由选择虚拟机的存储类型。当用户想要在虚拟机运行对磁盘IOPS和吞吐量要求不高的普通业务时,可以选择容量型虚拟机;当用户想要运行对读写要求较高的数据库业务时,就需要选择性能型或者超高性能型虚拟机。nova后台会根据用户的前端选择自动创建相对应的虚拟机。

3.2 Nova秒级创建虚拟机

我们的nova选择的是基于ceph的共享存储,如果采用默认的配置,创建虚拟机会非常缓慢,跟镜像大小成正比。在优化之前,如果批量创建几百台虚拟机,由于需要先从ceph上把镜像下载到本地,然后转换格式,再从本地上传到ceph,短则几分钟,长的有时需要10分钟以上,有些因为超时导致失败。优化后,同样条件下,可以实现秒级创建虚拟机的功能。优化步骤如下:

(1)在控制节点上修改/etc/glance/glance-api.conf镜像配置文件,把show_image_direct_url 参数设置为True。修改后Nova可以利用镜像文件在ceph中的rbd路径直接在ceph上启动虚拟机,避免镜像文件的拷贝。

(2)利用命令service openstack-glance-api restart重启镜像管理服务。

(3)在计算节点上进入 /var/lib/nova/instances/_base/ 目录,清空该目录下所有的缓存镜像。由于后台程序会先检测该目录下有没有缓存镜像,如果有,会把该缓存镜像上传到ceph中,如果没有,直接在ceph中clone镜像。

步骤如下:

[root@openstack _base]# cd \

/var/lib/nova/instances/_base/

[root@openstack _base]# ll

-rw-r--r--. 1 qemu qemu 41126400 Jul 24 01:01 d7fca384a7c355afa3b70667b60f04dd08cd6f35

[root@openstack _base]# rm -rf \

d7fca384a7c355afa3b70667b60f04dd08cd6f35

[root@openstack _base]# ll

total 0

(4) 最后,Nova在创建虚拟机时,会直接基于ceph上的基础镜像先做snapshot,然后基于该snapshot 进行clone(copy on write)。这样创建虚拟机,新增磁盘很小,所以速度非常快。

源码如下:

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/nova/virt/libvirt/imagebackend.py

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack ~]# vim /usr/lib/python2.7/site-packages/nova/virt/libvirt/driver.py

OpenStack计算模块Nova架构及问题源码剖析

[root@openstack ~]# vim \

/usr/lib/python2.7/site-packages/glance/api/v2/images.py

OpenStack计算模块Nova架构及问题源码剖析

3.3 Nova虚拟机快照

原生libvirt采用virDomainSnapshotCreateXML()函数来实现快照(CLI 为virsh snapshot-create-as/snapshot-create)。若为内置快照,快照信息和虚拟机存在同一个qcow2镜像中;若为外置快照,新建一个qcow2文件,原虚拟机的disk将变为一个read only的模板镜像,新qcow2镜像仅记录与模板镜像的差异数据。这种快照包含快照链信息,可保留disk和ram信息,可回滚至快照点。

Nova并未采用virDomainSnapshotCreateXML()来实现快照,而是单纯的对虚拟机镜像做转换和拷贝,生成一个与虚拟机无依赖关系的镜像,最后上传至glance中。这种快照不包含快照链信息,只保留disk信息,无法回滚至快照点,只能采用该快照镜像创建一个新的虚拟机。至于Nova为什么不实现增量快照,本人猜测可能是因为Nova默认创建的系统盘,从使用场景来说一般不会存储用户数据,有价值的数据都存放在数据盘里,系统盘即使出故障可以通过重装系统来恢复系统,社区可能觉得没有必要去实现系统盘的增量快照。

如果确实要实现虚拟机的增量快照,有两种方式:一种是可以通过卷启动的方式来创建虚拟机,然后通过cinder来对卷做增量快照。第二,可以通过nova-driver来对接ceph的快照接口来实现。

4 总结和学习建议

Nova的优化和修改对云平台的稳定性和速度都有极大的提升,但是随着集群规模不断的扩大,对整个OpenStack云平台的压力考验越来越大,需要从多个维度来合理优化OpenStack。可以优化的点很多,比如:多Region和多Cell规划、RabbitMQ消息队列超时问题、MariaDB死锁问题、Keystone用户鉴权瓶颈、Ceph OSD宕机快速自动恢复。并不是每个点都需要优化,不同的集群规模和应用场景,采取的优化点各不相同,需要因地制宜,用最小的代价来解决用户真实的痛点才是好的解决方案。

根据本人对OpenStack一路过来的学习经验,新手可以先从Nova模块入手,先从运维使用层面,从CLI命令行和Dashbord两种途径熟练掌握每个基本的操作。然后可以从源代码层级逐步分析创建一台虚拟机的完整流程,分析各个组件如何通过AMQP队列通信,分析MariaDB中instances表中记录和各种状态的实时变化,分析Nova如何通过Keystone认证并和Glance、Cinder、Neutron、Ceph等模块如何交互。这些流程熟悉后,相信你会触类旁通,对OpenStack其他模块甚至其他云平台架构的学习会更加容易,也会更加自信。

后续有机会可以和大家分享下OpenStack其他模块的内容。

十年云研发和架构经验,中国科学技术大学软件工程全日制硕士研究生学历。曾在IBM、土豆网、微烛云先后担任云计算研发工程师、架构师,技术总监等职务,参与并主导过产品设计、研发、测试和运维(DevOps)等完整产品生命周期流程,有上万台物理服务器规模的OpenStack集群架构实战经验,对云平台的瓶颈和性能优化有丰富经验。精通OpenStack、CloudForms、Kubernetes、Ceph、KVM、Docker、Solaris(SmartOS)等云平台及虚拟化技术。


分享到:


相關文章: