1、概述

启动一个新的实例,会涉及到OpenStack Nova中的多个组件:

API服务器,接收用户端的请求,并且将其传递给云控制器。

云控制器,处理计算节点、网络控制器、API服务器和调度器之前的通信。

调度器,选择一个执行命令的主机。

计算节点,管理计算实例:启动/终止实例,添加/删除卷…

网络控制器,管理网络资源:分配固定IP地址,配置VLAN

启动一个实例的流程如下:API服务器收到用户的run_instance命令,API服务器将消息传递给云控制器(1);对用户进行身份验证;云控制器将消息转发到调度器(2);调度器将消息扔给一个随机的一个主机,并通知它启动一个新实例(3);主机上的计算服务接收该消息;计算服务启动实例需要一个固定的IP,所以发送消息给网络控制器(5,6,7,8);之后,计算服务生产出一个新的实例。

2、API

可以使用OpenStack API或者EC2 API来启动新的实例。下面以EC2 API为例。添加一个新的key pair,并使用它启动一个新的m1.tiny类型的实例:

cd /tmp/
euca-add-keypair test > test.pem
euca-run-instances -k test -t m1.tiny ami-tiny

调用文件api/ec2/cloud.py中的run_instances()函数,在该函数中会调用文件compute/API.py中的compute API的create函数:

def run_instances(self, context, **kwargs):
...
instances = self.compute_api.create(context,
instance_type=instance_types.get_by_type(
kwargs.get('instance_type', None)),
image_id=kwargs['image_id'],
...

compute API  create函数流程如下:

检查此种类型的实例数是否已经达到最大值;

如果不存在安全组,则创建一个;

为新的实例创建MAC地址和主机名;

发送消息给调度器以运行实例。

3、消息投递

发送消息给调度器,在OpenStack中,这种类型的消息传递定义为RPC投递,使用RabbitMQ进行传递。消息生产者(API)发送消息到一个topic exchange(scheduler  topic)。消费者(调度器)从队列中接收消息。因为是消息投递,所以无需响应消息。

下面是消息投递的代码:

LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
" instance %(instance_id)s") % locals())
rpc.cast(context,
FLAGS.scheduler_topic,
{"method": "run_instance",
"args": {"topic": FLAGS.compute_topic,
"instance_id": instance_id,
"availability_zone": availability_zone}})

可见在消息投递时使用了scheduler topic,并且希望调度器在发送消息时,使用compute topic。

4、调度器

调度器接收消息,并且发”run_instance”消息给随机的主机。这里使用的是chance 调度器。还有其他类型的调度器,比如zone调度器(在一个特定的可用区域内随机选择主机)、简单调度器(选择最小负载的主机)。现在主机已经选择好了,接下来就是发送消息给该主机的计算服务了。

rpc.cast(context,
db.queue_get_for(context, topic, host),
{"method": method,
"args": kwargs})
LOG.debug(_("Casting to %(topic)s %(host)s for %(method)s") % locals())

5、计算节点

计算节点接收消息,然后调用compute/manager.py中的run_instance方法:

def run_instance(self, context, instance_id, **_kwargs):
"""Launch a new instance with specified options."""
...

run_instance()的流程如下:

检查实例是否已经运行;

分配固定的ip低值;

设置一个VLAN或者桥;

使用virtualization driver产生一个实例。

6、调用网络控制器

在分配固定IP时,会用到RPC调用。RPC调用不同于RPC投递,它使用topic.hostexchange,表明它的目的地为特定主机,而且RPC调用需要响应。

7、产生实例

下面就是由virtualization driver执行产生实例的过程。以libvirt为例,代码在virt/libvirt_conn.py中。

启动实例的第一件事就是创建libvirt xml。方法to_xml用来生成xml的内容。下面是我们实例的XML文件:

<domain type='qemu'>
<name>instance-00000001</name>
<memory>524288</memory>
<os>
<type>hvm</type>
<kernel>/opt/novascript/trunk/nova/..//instances/instance-00000001/kernel</kernel>
<cmdline>root=/dev/vda console=ttyS0</cmdline>
<initrd>/opt/novascript/trunk/nova/..//instances/instance-00000001/ramdisk</initrd>
</os>
<features>
<acpi/>
</features>
<vcpu>1</vcpu>
<devices>
<disk type='file'>
<driver type='qcow2'/>
<source file='/opt/novascript/trunk/nova/..//instances/instance-00000001/disk'/>
<target dev='vda' bus='virtio'/>
</disk>
<interface type='bridge'>
<source bridge='br100'/>
<mac address='02:16:3e:17:35:39'/>
<!-- <model type='virtio'/> CANT RUN virtio network right now -->
<filterref filter="nova-instance-instance-00000001">
<parameter name="IP" value="10.0.0.3" />
<parameter name="DHCPSERVER" value="10.0.0.1" />
<parameter name="RASERVER" value="fe80::1031:39ff:fe04:58f5/64" />
<parameter name="PROJNET" value="10.0.0.0" />
<parameter name="PROJMASK" value="255.255.255.224" />
<parameter name="PROJNETV6" value="fd00::" />
<parameter name="PROJMASKV6" value="64" />
</filterref>
</interface> <!-- The order is significant here. File must be defined first -->
<serial type="file">
<source path='/opt/novascript/trunk/nova/..//instances/instance-00000001/console.log'/>
<target port='1'/>
</serial> <console type='pty' tty='/dev/pts/2'>
<source path='/dev/pts/2'/>
<target port='0'/>
</console> <serial type='pty'>
<source path='/dev/pts/2'/>
<target port='0'/>
</serial> </devices>
</domain>

使用的虚机管理程序为qemu,客户机的内存为524字节,客户端OS从存储于主机OS中的内核和initrd启动。

分配各客户机的虚拟CPU个数为1. 电源管理中使能了ACPI。还定义了多个设备:

磁盘镜像为主机OS上的一个文件,使用qcow2驱动器,qcow2是一种qemu磁盘镜像的写时复制格式;

网络接口是客户机可见的桥,定义了一系列网络过滤器参数,比如IP地址10.0.0.3意味着始终使用该地址作为源IP地址;

日志文件,所有发送给字符设备的数据全部写入console.log中;

伪终端,virsh控制台可用于连接本地串口。

接下来是网络过滤器的配置。默认使用的防火墙driver是iptables。规则由IptablesFirewallDriver类的apply_ruleset方法定义。下面是本实例的防火墙规则:

*filter
...
:nova-ipv4-fallback - [0:0]
:nova-local - [0:0]
:nova-inst-1 - [0:0]
:nova-sg-1 - [0:0]
-A nova-ipv4-fallback -j DROP
-A FORWARD -j nova-local
-A nova-local -d 10.0.0.3 -j nova-inst-1
-A nova-inst-1 -m state --state INVALID -j DROP
-A nova-inst-1 -m state --state ESTABLISHED,RELATED -j ACCEPT
-A nova-inst-1 -j nova-sg-1
-A nova-inst-1 -s 10.1.3.254 -p udp --sport 67 --dport 68
-A nova-inst-1 -j nova-ipv4-fallback
-A nova-sg-1 -p tcp -s 10.0.0.0/27 -m multiport --dports 1:65535 -j ACCEPT
-A nova-sg-1 -p udp -s 10.0.0.0/27 -m multiport --dports 1:65535 -j ACCEPT
-A nova-sg-1 -p icmp -s 10.0.0.0/27 -m icmp --icmp-type 1/65535 -j ACCEPT
COMMIT

定义了防火墙规则之后,就是创建镜像,由方法_create_image()处理:

def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None):
...

在该方法中,会根据上面的XML创建libvirt.xml;复制虚拟机管理程序要使用的randisk、initrd和磁盘镜像;如果使用flat网络管理器,则会将一个网络配置植入到客户端的OS镜像中。本例中使用VLAN管理器。

实例的SSH key植入到镜像中,本过程是调用disk.inject_data方法:

disk.inject_data(basepath('disk'), key, net,
partition=target_partition,
nbd=FLAGS.use_cow_images)

basepath('disk')表示实例的磁盘镜像在主机OS中的位置,key是SSH key字符串,在我们的例子中不设置网络,因为不需要植入网络配置。因为使用的是内核镜像,所以没有分区,否则的话会使用分区的磁盘镜像。在inject_data内部:

第一件事就是链接镜像到一个设备,这发生在_link_device中:

device = _allocate_device()
utils.execute('sudo qemu-nbd -c %s %s' % (device, image))
# NOTE(vish): this forks into another process, so give it a chance
# to set up before continuuing
for i in xrange(10):
if os.path.exists("/sys/block/%s/pid" % os.path.basename(device)):
return device
time.sleep(1)
raise exception.Error(_('nbd device %s did not show up') % device)

_allocate_device()返回下一个可用的ndb设备:/dev/ndbx,其中x在0到15之间。qemu-nbd是一个QEMU磁盘网络块设备服务器。一旦该过程返回,则得到了设备,比如/dev/ndb0.

对该设备禁用文件系统检查,这里的mapped_device是”/dev/ndb0”。

out, err = utils.execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device)

将该文件系统挂载到临时目录,并将SSH KEY添加到authorized_keys文件中。

sshdir = os.path.join(fs, 'root', '.ssh')
utils.execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter
utils.execute('sudo chown root %s' % sshdir)
utils.execute('sudo chmod 700 %s' % sshdir)
keyfile = os.path.join(sshdir, 'authorized_keys')
utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n')

上面的代码中,fs就是临时目录。

最后,unmount文件系统,删除设备。这就结束了镜像的创建和安装。

在虚拟机化驱动器中的spawn方法中的下一步就是使用驱动器的createXML()加载实例,然后就是应用防火墙规则。

原文:http://www.laurentluce.com/posts/openstack-nova-internals-of-instance-launching/

OpenStack Nova启动实例流程的更多相关文章

  1. openstack之nova启动实例过程

    概述: 启动一个实例包含以下步骤: API server:处理用户请求并转发至cloud controller cloud controller:处理计算节点.网络控制.api server 以及sc ...

  2. Nova 启动虚拟机流程解析

    目录 文章目录 目录 前言 从请求说起 nova-api service 阶段 前言 Nova 启动虚拟机的东西太多,持续更新- 从请求说起 无论是通过 Dashboard 还是 CLI 启动一个虚拟 ...

  3. 如何删除 OpenStack Nova 僵尸实例

    转自:http://www.vpsee.com/2011/11/how-to-delete-a-openstack-nova-zombie-instance/ 前天强制重启一台 OpenStack N ...

  4. OpenStack Nova虚拟机创建流程解析

    https://yikun.github.io/2017/09/27/OpenStack-Nova%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%88%9B%E5%BB%BA%E6%B5 ...

  5. openstack nova修改实例路径,虚拟磁盘路径

    #实例路径 --instances_path=$state_path/instances #日志的目录 --logdir=/var/log/nova #nova的目录 --state_path=/va ...

  6. OpenStack Train版-12.创建虚拟网络并启动实例(控制节点)

    使用VMware虚拟机创建网络可能会有不可预测到的故障,可以通过dashboard界面,管理员创建admin用户的网络环境 1.第一种: 建立公共提供商网络在admin管理员用户下创建 source ...

  7. openstack之nova-api服务流程分析

    nova-api公布api服务没实用到一个些框架,基本都是从头写的.在不了解它时,以为它很复杂,难以掌握.花了两三天的时间把它分析一遍后,发现它本身的结构比較简单,主要难点在于对它所使用的一些类库不了 ...

  8. OpenStack Nova

    OpenStack Nova 简介 OpenStack 中的 Nova 负责维护和管理云环境的计算资源 Nova 在现有 Linux 服务器上作为一组守护线程来提供服务 Nova 由多个服务器进程组成 ...

  9. openstack nova 源码解析 — Nova API 执行过程从(novaclient到Action)

    目录 目录 Nova API Nova API 的执行过程 novaclient 将 Commands 转换为标准的HTTP请求 PasteDeploy 将 HTTP 请求路由到具体的 WSGI Ap ...

随机推荐

  1. angular4 自定义表单验证Validator

    表单的验证条件有时候满足不了需求就可以自定义验证 唯一要求返回是ValidatorFn export interface ValidatorFn{ (c:AbstractControl):Valida ...

  2. Oracle锁表查杀会话进程

    一.逐条--锁表 (1)查表名 和 sessionidselect b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_obj ...

  3. day36 03-Hibernate检索方式:排序、参数绑定、投影查询

    排序之后是分页查询. 检索单个对象 还可以进行参数的绑定. HQL的参数绑定,按参数名称绑定或者是按参数位置绑定. 还可以用投影的操作,投影的操作是只查询这里面的某几个属性.只查询某一个属性,查询多个 ...

  4. 如何查看MySQL执行计划呢?

    覆盖索引: MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件 包含所有满足查询需要的数据的索引称为 覆盖索引(Covering Index) 如果要使用覆盖索引,一定 ...

  5. farv

    http://weishu.me/ https://github.com/jimupon/VirtualXposed O:  ?  api 26 - vdex N: speed-profile M: ...

  6. web前端学习(三)css学习笔记部分(1)-- css入门基础知识+基本样式

    1.介绍及语法 1.1CSS概述: CSS指层叠样式表 CSS样式表极大地提高了工作效率 如果值大于一个单词,需要加上引号(意思是值只有一个的时候可以不加引号) 1.2CSS高级语法 1.选择器分组 ...

  7. php 单向散列加密

    1.加密文件 <?php //sha1_en.php header("content-type:text/html;charset=utf-8"); $str = " ...

  8. HTML-DOM实例——实现带样式的表单验证

        HTML样式 基于table标签来实现页面结构 <form id="form1"> <h2>增加管理员</h2> <table&g ...

  9. 修正Thinkphp 3.2 分页Page类以支持URL路由

    http://www.thinkphp.cn/topic/22114.html 最终目的实现以http://www.fl900.com/product/lists/1-0-0-1.html这样的URL ...

  10. WCF ChannelFactory

    public static class WcfExtensions{    public static void Using<T>(this T client, Action<T&g ...