Nova虚拟机启动

本来这一节打算写Neutron中各个OVS上的流表逻辑的,突然想起来之前学习Neutron时深入研究过的一个问题——虚拟机接入OVS是如何实现的?既然流表是为了虚拟机通信用的,那么没有虚拟机的接入,流表也就没有了用武之地。因此,本节就来讲一讲OpenStack中虚拟机启动过程中,是如何获取MAC、IP地址,如何在Neutron上绑定port,以及如何获取IP地址的。这一节就当做是对上一节“Neutron的软件实现”的一个补充吧。

从头开始讲。虚拟机的启动通常来自于控制节点命令行的nova boot,该命令被组装成REST API送到nova-api。Nova-api与neutron-server干的是一样的活:接收REST请求,然后跑一些调度机制,计算出虚拟机部署的位置,然后通过rpc与相应计算节点上的agent——nova-compute进行通信,而启动虚拟机的实际工作由nova-compute完成。

当然,以上过程与网络并没有发生什么关系,这里不去分析具体的代码实现,有兴趣的读者请移步http://www.linuxqq.net/archives/1277.html。

假定nova-compute已经通过rpc收到了开始干活的命令,我们就从这里开始漫长的代码分析。在此之前,先来看一看OpenStack组件层面的调用流程。这里借用OpenStack大神SammyLiu的一张图吧,图中1-6步骤依次做了这么几件事:

1)Nova-compute向neutron-server请求虚拟机对应的Port资源。

2)Neutron-server根据neutron-database生成Port资源。

3)Neutron-server通知Dhcp agent虚拟机信息。

4)Dhcp agent将虚拟机信息通知给dhcp server。

5)虚拟机接入并启动。

6)虚拟机从dhcp server处获得IP地址。

最后一步就是传统的dhcp的交互过程,这里就不讲了,下面来看1-5的实现。在开始具体的代码解读之前,我们首先给出码的主体思路。 1)Nova-compute向neutron-server发送create_port的REST API请求,生成新的Port资源。

2)Neutron-server收到该REST请求,通过APIRouter路由到ML2的create_port方法。该方法中,获得了neutron-database新生成的Port,并通知ML2 Mechanism Driver该Port的生成。

3)Nova-compute向neutron发送update_port的REST API请求,

4)Neutron-server收到该REST请求,通过APIRouter路由到ML2的update_port方法。该方法中,在neutron-database更新该Port的状态,并根据ML2 Mechanism Driver的不同,决定后续的处理:若Mechanism Driver为hyperv/linuxbridge/ofagent/openvswitch,则需要通过ML2的update_port方法中执行rpc远程调用update_port;对于其余的Mechanism Driver,ML2的update_port方法调用其的update_port_postcommit方法进行处理,这些Mechanism Driver可能使用非rpc方式与自身的agent通信(如REST API、Netconf等)。

5)ML2执行完update_port方法后,Port资源在wsgi中对应的Controller实例通过DhcpAgentNotifyAPI实例rpc通知给网络节点上的dhcp agent(也可能通过一些调度机制通知给分布在计算节点上的dhcp agent)。

6)Dhcp agent收到该rpc通知,通过call_driver方法将虚拟机MAC与IP的绑定关系传递给本地的DHCP守护进程Dnsmaq。

7)Nova-compute通过libvirt driver的spawn方法将虚拟机接入网络,然后启动虚拟机。

下面开始一步步地回溯相关代码。先提醒大家做好看到头晕的准备,不过只要狠下心来把这一节的代码搞懂了,OpenStack中基础网络功能的实现,可以说也就能搞懂一大半了。

====================== Codes Start ========================

(一)Nova-compute向neutron-server发出Port资源的REST请求

首先,nova-compute接收远端的rpc调用,入口方法为nova.compute.manager l 2173的run_instance方法,方法被多个修饰符包装,最终执行_run_instance方法(l 2183),其中参数node为当前物理宿主机,instance为待启动的虚拟机。

_run_instance方法(l 1237)中主要做了两件事情。第一件事是调用_prebuild_instance方法(l 1254)做一些预备工作,包括检查instance的唯一性(l 1287),更新nova资源追踪器中该虚拟机的状态(l 1290)等。第二件事是调用_build_instance方法(l 1265)开始构建虚拟机。

_build_instance方法(l 1328)中做了三件涉及网络的事情,最后将虚拟机接入网络并启动虚拟机(l 1385)。最后的处理这里暂且不提,涉及网络的前两件事件是由libvirt生成一个物理宿主机可用的MAC地址集合(l 1361),以及生成虚拟机的dhcp选项(l 1362),dhcp选项数据结构如下图所示(nova.virt.baremetal.pxe)。

_build_instance方法干的第三件事调用_allocate_network方法获取虚拟机网络参数(l 1364)。从_allocate_network方法跳入_allocate_network_async方法(l 1565),开始循环尝试(l 1580)获取虚拟机网络参数(l 1582)。

进入nova.network.neutronv2.api的allocate_for_instance方法(l 197),该方法负责与neutron-server进行REST API的通信,以获取虚拟机相应的网络资源。该方法比较长,这里把代码粘在下面,并用红框勾出两个主要的功能区块。

第一个区块中,分析业务请求的MAC是否在宿主机可用的MAC地址池内。第二个区块中,判断业务请求的Port是否已经存在,如果存在则向neutron-server发出update_port的REST API,否则需要先create_port再update_port。

到这里为止,nova-compute就完成了Port资源的申请,总结起来就是接收业务请求的虚拟机参数,然后将涉及网络的请求通过REST API扔给neutron-server。

(二)Neutron-server的工作

Port是3种核心资源里面的一种,neutron-server通过APIRouter将这个create_port或者update_port请求路由到ML2 plugin中(具体请参考上一节“Neutron的软件实现”中wsgi部分的介绍),假设Port是第一次申请,要先create再update。

2.1)Ml2对create_port的处理

收到create_port的REST API后,由ML2(neutron.plugins.ml2.plugin)执行create_port方法。该方法主要做了3个工作:第一是在neutron-database中新建该Port的信息(l 634),第二是进行port_binding(l 639),第三是将请求交给Mechanism Driver去完成Port在网络中的部署(l 649)。

首先,来看l 634中ML2调用的数据库方法。Ml2Plugin继承了neutron.db.db_base_plugin_v2.NeutronDbPluginV2,l 634中即执行了NeutronDbPluginV2类中的create_port方法,该方法中主要做了两个事情:第一是分配可用的MAC地址(l 1364-1373),如何业务请求的MAC地址可用,即为其分配该MAC地址,如果不可用则DB生成一个可用的MAC地址;第二是在虚拟机所在租户子网的地址池中获取IP地址(l 1376)。最后该方法返回了该Port(l 1413)。

接下来看l 639中port_binding(l 210)的处理。前面进行一堆数据库中Port属性的赋值(包括host,vnic_type和profile),l 259调用各个neutron.plugins.ml2.manager中Mechanism Manager的bind_port方法(l 440),该方法再调用(l 458)各个Mechanism Driver的bind_port方法。该方法有的driver实现了(hyperv/linuxbridge/ofagent/openvswitch),记录了segment_id,vif_type等信息,有的则直接pass掉了。

ML2的create_port方法执行最后一个工作(l 649),将请求交给Mechanism Driver去完成Port在网络中的创建。这个工作各家的实现又是天差地别,有的不处理(hyperv/linuxbridge/ofagent/openvswitch),有的通过Netconf(Cisco Nexus,Brocade),有的走rpc(Arista),还有的走REST API(ODL、ONOS),这里就不具体讲了。

2.2)Ml2对update_port的处理

收到update_port的REST API后,由ML2(neutron.plugins.ml2.plugin)执行update_port方法。该方法主要做了两件事:第一是向neutron-database更新Port状态(l 672);第二是将请求交给Mechanism Driver去完成Port在网络中的更新(l 698),更新后需要进行以下判断,由于hyperv/linuxbridge/ofagent/openvswitch这类driver自身并没有实现向下的通信机制,因此需要ml2在update_port方法中调用rpc的notify方法将update_port事件传给相应的agent(l 707)。Agent执行update_port事件的代码分析请参考上一节“Neutron的软件实现”中agent部分的分析。

以上步骤的代码处理与2.1中类似,这里就不再详细说了。

(三)Neutron与dhcp-agent间的rpc

Port资源生成好了,还需要向Dhcp server更新MAC地址与IP地址的绑定关系,这依赖于rpc通信两端的工作。

3.1)Neutron发出dhcp通知

这部分代码难找得要命,其机制在于wsgi的Port Controller(neutron.api.v2.base)在update方法(l 492)中不仅完成了到Ml2Plugin update_port方法的路由(l 528),还在其后实现了向dhcp-agent的rpc通知port_ update_end(l 549)。

3.2)Dhcp-agent接收rpc通知

Dhcp-agent的入口文件为neutron.agent.dhcp_agent,在初始化的过程中实例化了rpc的Publiser(l 77),可以主动地向neutron同步dhcp的数据库状态,当然dhcp-agent也可以被动地监听neutron发出的rpc通知。当收到port_update_end通知后,执行l 308的同名方法。

(四)Dhcp-agent向dhcp-server更新虚拟机地址

在port_update方法中,调用call_driver方法(neutron.agent.dhcp_agent,l 314)找到dhcp-server(dnsmaq),执行dnsmaq的reload_allocations方法(neutron.agent.linux.dhcp,l 394),之后开始加载虚拟机的地址信息。

(五)Nova-compute将虚拟机接入,并启动虚拟机

转了一大圈,回到了起点nova-compute(nova.compute.manager),前面(一)中提到了在_build_instance方法(l 1328)的最后(l 1385),nova-compute完成了启动虚拟机的收尾工作_spwan方法。_spwan方法(l 1791)调用nova.virt.baremetal中BareMetalDriver的_spwan方法(l 111),该方法主要包括两个工作,首先是调用_plug_vifs方法(l 250)将虚拟机接入网络,然后启动虚拟机(l 272-277)。

======================= Codes End ========================

到这里,虚拟机启动过程中的网络处理就都结束了,虚拟机间就可以开始通信了。下一节分析Neutron中OVS的流表逻辑,看看OVS是怎么支持虚拟机间的通信的。

原文发布于微信公众号 - SDNLAB(SDNLAB)

原文发表时间:2016-04-14

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第28章 RL-TCPnet之DNS应用

本章节为大家讲解RL-TCPnet的DNS应用,学习本章节前,务必要优先学习第27章的DNS基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

1135
来自专栏有趣的django

RESTful API 设计指南

简介 REST(英文:Representational State Transfer,简称REST)描述了一个架构样式的网络系统,比如 web 应用程序。它首次...

3295
来自专栏Coding01

学习 Lumen 用户认证 (一)

好久没写 PHP 代码了,尤其是 Lumen,我是 Lumen 的忠实用户,自从面世开始,我就将 Lumen 作为我 API 的主要框架使用。

1751
来自专栏Jackson0714

不惧面试:HTTP协议(1) - 基础扫盲

3037
来自专栏腾讯IVWEB团队的专栏

使用 Node.js 实现一个简单的 ZooKeeper 客户端

Zookeeper 是一个分布式的、开源的协调服务,用在分布式应用程序中。它提出了一组简单的原语,分布式应用程序可以基于这些原语之上构建更高层的分布式服务用于实...

1.6K0
来自专栏JetpropelledSnake

Python入门之logging日志模块以及多进程日志

本篇文章主要对 python logging 的介绍加深理解。更主要是 讨论在多进程环境下如何使用logging 来输出日志, 如何安全地切分日志文件。 1. ...

1.1K7
来自专栏Elson's web

webpack4实用配置指南-上手篇

算起来已经有3到4个项目的webpack构建打包经历。然而每次搭建起来还是有新手既视感,比较捉急。工具用法的东西,好记性不如烂笔头,有必要系统梳理下webpac...

2.1K14
来自专栏马洪彪

C#爬虫系列(一)——国家标准全文公开系统

网上有很多Python爬虫的帖子,不排除很多培训班借着AI的概念教Python,然后爬网页自然是其中的一个大章节,毕竟做算法分析没有大量的数据怎么成。 C#相比...

60910
来自专栏SDNLAB

OpenDaylight ping模块开发及分析

编者按:OpenDaylight ping模块开发及当ping操作触发数据流,对其进行分析及流程原理的疏通讲解,并在开发过程中遇到的问题进行总结,希望给大家能够...

3486
来自专栏破晓之歌

Eclipse上通过Pydev使用python

转载自:http://www.cnblogs.com/linzhenjie/articles/2639113.html

1231

扫码关注云+社区

领取腾讯云代金券