2.7 Nova关键组件
2.7.1 API服务(nova-api)
nova-api也称为API服务器,是用Python实现的一个Web服务器,实现Restful API到内部请求消息的转换。nova-api支持两类API,一类是Nova自定义的API,这个API又区分为不同的版本,其中V2是主流支持的接口版本。另一类是兼容EC1-API接口,这个API接口在未来可能被移除。
nova-api本身并不复杂,先看一下启动Nova的代码:
[root@nova-controller ~]# cat /usr/bin/nova-api #!/usr/bin/python # PBR Generated from u'console_scripts' import sys from nova.cmd.api import main if __name__ == "__main__": sys.exit(main())
找到nova.cmd.api的main()函数。
def main(): config.parse_args(sys.argv) logging.setup("nova") utils.monkey_patch() gmr.TextGuruMeditation.setup_autorun(version) launcher = service.process_launcher() for api in CONF.enabled_apis: should_use_ssl = api in CONF.enabled_ssl_apis if api == 'ec2': server = service.WSGIService(api, use_ssl=should_use_ssl, max_url_len=16384) else: server = service.WSGIService(api, use_ssl=should_use_ssl) launcher.launch_service(server, workers=server.workers or 1) launcher.wait()
从代码大概能够看出,首先是读取配置文件,读取配置参数,并且根据配置完成初始化消息队列,用以后续与别的组件进行内部消息交互。同时根据配置文件中的配置项启动WSGI服务器,配置文件中的每一个API对应一个服务器。另外根据系统的CPU核心数n,每个WSGI服务器都会有n个worker协程去处理请求。
通常,EC2的API对应TCP端口8773,Nova自定义的API对应端口8774。WSGI服务器运行后,开始循环侦听TCP端口,接收客户端发起的HTTP请求,并将请求参数验证解释后转换为系统内部消息,然后通过RPC将内部消息传递到后端组件执行。在技术实现上,nova-api利用WSGI框架,实现URL到action的映射。action是对应模块里面的具体实现函数。
2.7.2 消息队列(AMQP)
AMQP即Advanced Message Queuing Protocol,是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件而设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品、不同开发语言等条件的限制。典型的AMQP的产品实现有RabbitMQ和Qpid。OpenStack广泛使用消息队列作为组件间通信的中间件。
消息队列协议规范AMQP定义了一种消息队列传输模型。AMQP的消息模型概念主要是两个组件:Exchange和Queue,如图2-6所示。X就是Exchange,条状的条是Queue,这两者都在Server端,又称作Broker,这部分由服务器实现。客户端通常有Producer和Consumer两种类型,Producer与Consumer通过TCP连接RabbitMQ,按照规范定义的信令交互协议,通过服务器发送或接收消息。
Client分为两种角色,一种是消息发送方,如图2-6中P所示(Provider,消息发送者);一种是消息接收方,如图2-6中C所示(Consumer,消息接收者)。Client所传递的消息需通过服务器进行转发。
图2-6 AMQP的消息模型示意
交换器(Exchange)是交换消息的实体,起到消息路由、过滤的作用。队列(Queue)是接收消息的实体,本质上是一个缓存消息的队列。绑定器(Bind)将交换器和队列连接起来,并且封装消息的路由信息。
交换器指向队列的黑色线——RoutingKey,可以将它简单地理解为一条连接交换器和队列的路线,交换器和队列都需要通过Channel来进行定义,而RoutingKey则只需要在绑定时取个名称就行了。
图2-6中左边的客户向右边的客户发送消息,流程如下:
1)获取Connection(客户端到MQ服务器的TCP链路)。
2)获取Channel(逻辑层的链路,基于Connection)。
3)定义交换器、队列。
4)使用一个RoutingKey将队列绑定到一个交换器上。
5)通过指定一个交换器和一个RoutingKey来将消息发送到对应的队列上。
6)接收方在接收时也是获取Connection,接着获取Channel,然后指定一个队列直接到它关心的队列上取消息,它对交换器、RoutingKey及如何绑定都不关心,到对应的队列上去取消息就行了。
一个客户发送消息,哪些Client可以收到消息,其核心就在于交换器、RoutingKey、队列的关系上。
交换器在定义的时候是有类型的,以决定到底是哪些队列符合条件,可以接收消息,OpenStack中使用的交换器类型主要有以下3种。
1)Fanout:所有绑定到此交换器的队列都可以接收消息。此为(publisher/subscriber)模式。
2)Direct:通过RoutingKey和交换器决定的那个唯一的队列可以接收消息,此为1对1模式。
3)Topic:所有符合RoutingKey(可以是一个表达式)的RoutingKey所绑定的队列可以接收消息,和扇出相比,这个相当于通过RoutingKey进行了过滤。
使用RoutingKey为#、交换器类型为topic的时候,相当于使用扇出。
以nova-api收到外部请求创建虚拟机为例。nova-api通过rpc.cast函数封装,通过消息队列发送消息给nova-scheduler,触发nova-scheduler来调度nova-compute创建虚拟机。
rpc.cast(context, FLAGS.scheduler_topic, {"method": "run_instance", "args": {"topic": FLAGS.compute_topic, "instance_id": instance_id, "availability_zone": availability_zone, "injected_files": injected_files}})
第二个参数即为调度器关心的topic,系统中设定的值为scheduler,表明该消息需要nova-scheduler来接收并处理;第三个参数是消息体,使用的是key/value值对,method对应的run_instance表明这是一条创建虚拟机的消息。第一个参数context是上下文通用环境,其他参数编入到args参数里传递给nova-scheduler。
2.7.3 nova-compute
nova-compute负责管理虚拟机,单独运行于承载分配虚拟机的主机之上。nova-compute通过消息队列获取任务然后执行。
典型的业务流程是:nova-compute从消息队列获取分发给自己的消息,消息队列的消息体里定义了一系列的key/value值对,nova-compute需要对消息体进行解析。消息体中有个很重要的key是method,该key对应的value指明nova-compute需要完成的任务。以创建虚拟机为例,method对应的value为run_instance。run_instance指令要求nova-compute创建虚拟机。
首先nova-compute从Glance将虚拟机模板镜像下载到/var/lib/nova/instances/_base/文件夹下,文件名即为image,在对象存储上所对应的image_id,形如:
这里存储的还是原始模板镜像文件。随后,nova-compute会以这个镜像为模板,创建一个虚拟机真正使用的磁盘映像文件。以KVM作为Hypervisor举例,映像文件使用qcow2文件格式。
qemu-img create -f qcow1--o cluster_size=2M, backing_file=/var/lib/nova/ instances/_base/644208f3 /var/lib/nova/instances/instance-00000033/disk
如例所示,以644208f3这个镜像文件为模板,通过qemu-img程序创建虚拟机磁盘文件/var/lib/nova/instances/instance-00000033/disk,其中instance-00000033是nova-compute动态创建的目录,存放虚拟机实例相关信息,instance-00000033本身也是该虚拟机实例的instance-id。
随后nova-compute会向/var/lib/nova/instances/instance-00000033/disk这个虚拟机文件注入SSH登录的公钥,以支持虚拟机启动后通过私钥方式登录虚拟机。注入SSH公钥的步骤如下:
1)连接到虚拟机文件/var/lib/nova/instances/instance-00000033/disk,将之虚拟成字符设备。
sudo qemu-nbd -c /dev/nbd15 /var/lib/nova/instances/ instance-00000033/disk
2)挂载文件系统。
sudo tune2fs -c 0 -i 0 /dev/nbd15 sudo mount /dev/nbd15 /tmp/tmpLoFGru
3)建立SSH登录环境目录。
sudo mkdir -p /tmp/tmpLoFGru/root/.ssh sudo chown root /tmp/tmpLoFGru/root/.ssh sudo chmod 700 /tmp/tmpLoFGru/root/.ssh
4)将公钥写入SSH的配置文件/tmp/tmpLoFGru/root/.ssh/authorized_keys。
sudo tee -a /tmp/tmp4kabqd/root/.ssh/authorized_keys
5)释放资源。
sudo umount /dev/nbd15 rmdir /tmp/tmpLoFGru sudo qemu-nbd -d /dev/nbd15
通过该样例过程,可了解nova-compute是如何向虚拟机注入数据的。如果是向虚拟机注入固定IP,也是相同思路,无非就是挂接虚拟机的磁盘文件到文件系统上,修改其内部的网络配置文件。从H版本开始,这样的直接注入方式不是默认方式,Nova又引入了ConfigDrive机制和metadata服务,来实现针对虚拟机内部数据的修改。
对于AMI格式的虚拟机镜像,AMI格式的镜像只包含一个根文件系统。Kernel和ramdisk是单独另外存放的,不存放在镜像里面。为此,nova-compute也需要将Kernel和ramdisk从Glance上下载下来,复制到/var/lib/nova/instances/instance-00000033目录下,nova-compute会自动创建一个libvirt.xml文件,该文件即是该虚拟机实例的配置文件,nova-compute通过libvirt接口调用KVM,KVM会使用libvirt.xml虚拟机配置文件来启动虚拟机实例。最终,虚拟机运行实例动态目录下的文件如下:
root@nova-compute-1:/var/lib/nova/instances/instance-00000033# ls console.log disk disk.local kernel libvirt.xml ramdisk root@nova-compute-1:/var/lib/nova/instances/instance-00000033#
虚拟机启动参数文件libvirt.xml样例如下:
<domain type='kvm'> <name>instance-00000033</name> <memory>2097152</memory> <os> <type>hvm</type> <kernel>/var/lib/nova/instances/instance-00000033/kernel</kernel> <cmdline>root=/dev/vda console=ttyS0</cmdline> <initrd>/var/lib/nova/instances/instance-00000033/ramdisk</initrd> </os> <features> <acpi/> </features> <vcpu>1</vcpu> <devices> <disk type='file'> <driver type='qcow2'/> <source file='/var/lib/nova/instances/instance-00000033/disk'/> <target dev='vda' bus='virtio'/> </disk> <disk type='file'> <driver type='qcow2'/> <source file='/var/lib/nova/instances/instance-00000033/disk.local'/> <target dev='vdb' bus='virtio'/> </disk> <interface type='bridge'> <source bridge='br100'/> <mac address='02:16:3e:2b:29:cf'/> <!-- <model type='virtio'/> CANT RUN virtio network right now --> <filterref filter="nova-instance-instance-00000033-02163e2b29cf"> <parameter name="IP" value="10.0.0.4" /> <parameter name="DHCPSERVER" value="10.0.0.1" /> </filterref> </interface> <!-- The order is significant here. File must be defined first --> <serial type="file"> <source path='/var/lib/nova/instances/instance-00000033/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> <graphics type='vnc' port='-1' autoport='yes' keymap='en-us' listen='0.0.0.0'/> </devices> </domain>
以上,以典型的虚拟机实例创建过程,说明了nova-compute基本工作原理。nova-compute从消息队列获取的任务主要有以下几个:
□ 运行实例
□ 终止实例
□ 重启实例
□ 添加卷
□ 去除卷
□ 获得控制输出
□ 迁移实例
□ 诊断实例
□ 搁置
2.7.4 nova-cell
从G版本开始,Nova为了增加横向扩展以及分布式、大规模(地理位置级别)部署能力,同时又不增加数据库和消息中间件的复杂度,引入了Cell概念。并引入了新的nova-cell服务。Cell有自己的DB和AMQP,并且是树状结构,Cell之间使用AMQP通信。顶层的Cell称为API Cell,有自己的nova-api。子Cell无nova-api服务,子Cell定时上报资源给父Cell,接受父Cell的调度。
在API Cell节点要部署nova-api对外提供统一服务,nova-cell负责与子cell进行通信;子Cell节点要统一部署nova-cell,如果子Cell直接接入虚拟化层,则还要部署nova-scheduler、nova-compute。nova-cell架构如图2-7所示。
图2-7是3个Cell级联的情况,其中API Cell收到请求后,通过nova-cell提供的调度算法,通过消息队列将消息转发到子Cell节点,在子Cell节点处做与API Cell同样的工作,选择一个孙子Cell并继续转发,在孙子Cell节点上做真正的主机调度工作,选择主机创建虚拟机。
上面的情况下,孙子Cell需要将自己连接的资源信息定时上报给子Cell,以提供调度功能使用,同样子Cell也要将自己知道的资源信息上报给API Cell使用,这样,每层调度时只需获取自己掌握的资源信息即可,这就实现了每层解耦。
图2-7 nova-cell架构
2.7.5 nova-conductor
从G版本开始,取消了nova-compute的直接数据库访问,而增加了一个服务nova-conductor,所有nova-compute原先需要和数据库交互的部分,都不得直接访问数据库,而是通过访问nova-conductor获得。nova-conductor相当于Nova组件操作数据库的一个中间件。这个主要是从两方面考虑,一个是数据库安全。传统情况下每个nova-compute所运行的主机都能访问数据库,一旦某个主机被攻破,则数据库也就直接暴露了,对于数据库来说很不安全。另一个是,nova-compute与数据库解耦更有利于nova-compute的后续升级。
不仅是nova-compute在访问数据库时需要和nova-conductor交互,其他Nova组件访问数据库也通过nova-conductor中转。所以在启动时,nova-conductor需要首先启动起来。另外,随着nova-conductor的逐步完善,nova-conductor也逐渐承接了一部分TaskAPI任务。TaskAPI任务主要包含一些比较耗时的任务,如创建虚拟机等。比如传统模式下,nova-api收到请求消息后会发送给nova-scheduler处理,现在改为首先发给nova-conductor,再由nova-conductor发送给nova-scheduler进行调度处理。
2.7.6 nova-scheduler
nova-scheduler是Nova中实现任务分派的模块,决定如何派遣compute和volume的请求。Scheduler映射nova-api调用到合适的OpenStack Component,根据指定的算法从可用资源中获取Compute服务器。以下因素决定调度器的具体算法:负载、内存、可用zone的物理距离、CPU体系结构等。目前nova-schedule的可用调度算法如下。
□ Chance:在可用zone中随机选择计算主机。
□ AZ:与Chance相似,在执行的AZ中随机选择计算主机。
□ Simple:选择负载最轻的主机来运行实例,可从负载均衡器中获取负载信息。
□ 滤波器调度器:滤波器调度器利用滤波和权重两个概念来决定应该在何处创建新的实例。该调度算法只支持对计算节点的调度,具体调度原理如图2-8所示。
Nova的滤波策略是非常灵活的,目前支持多种滤波调度策略。用户也可实现自己的滤波算法。当前版本内置的滤波器如下。
□ AllHostsFilter:实质上没有任何操作,返回的是所有可用主机。
□ AvailabilityZoneFilter:通过AZ过滤主机,返回与请求实例具有相同AZ的主机。
□ ComputeFilter:返回由满足附加规格的Compute Service提供的能力、与实例类型相关的能力。返回一个列表指明主机上可创建的实例类型。
□ CoreFilter:基于CPU核利用率的滤波器,提供具有足够的CPU核数量的主机。
□ IsolatedHostsFilter:基于image_isolated和host_isolated flags的滤波器。
□ Json Filter:选择主机时考虑是否是基于JSON语法。
□ RamFilter:指标是主机的RAM,只返回具有够用的RAM的主机。
□ SimpleCIDRAffinityFilter:放置一个新实例到与其具有相同IP block的主机上。
□ DifferentHostFilter:放置一个实例到与某个指定的实例系列不同的主机上。
□ SameHostFilter:放置一个实例到与某个指定的实例系列相同的主机上。
图2-8 滤波器调度器的调度原理
用户自己创建自己的滤波器,只需集成BaseHostFilter类,并实现该类的一个方法:Host_passes。满足用户设置的条件是,该方法返回为true。该方法有两个参数:host_state、filter_properities。
滤波器调度器在工作过程中会使用所谓的Weight和Cost。Cost是作为请求结果来表达所选择主机的一个度量。通过将请求中的特性与主机特性进行比较得出不同指标的Cost值,最后放置实例到一个低Cost的主机上。计算Cost的函数从高速缓存中获取(如果高速缓存中没有,则从nova.conf中获取),Weight值也是在nova.conf中获取的。使用weight计算函数对主机进行称重,并计算weighted_sum。默认使用的是compute_fill_first_cost_fn函数,返回的是空闲RAM的Weight值。而用户具体想要使用哪种参数的Weight和Cost决定调度结果是用户自己实现的。最后将该函数写入nova.conf文件中的least_cost_functions选项。默认的权重计算函数和权重值设置如下:
--least_cost_functions=nova.scheduler.least_cost.compute_fill_first_cost_fn --compute_fill_first_cost_fn_weight=-1.0
2.7.7 nova-volume
nova-volume管理块存储设备,类似于Amazon的EBS,提供附加的块存储挂接给虚拟机。目前已经完全被Cinder所替代,但是在提供弹性能力的本质上,nova-volume和Cinder是一样的。Cinder只是对nova-volume的进一步封装,包括异构设备的支持、水平扩展能力的支持等。nova-volume管理的块设备基于Linux的LVM(Logical Volume Manager),使用iSCSI进行挂接。因此,nova-volume节点上必须要安装LVM包,也要安装iSCSI的包。
在使用nova-volume之前,必须配置一个nova-volumes的卷组。事实上nova-volume是在名为nova-volumes的卷组上创建逻辑卷,来分配块设备的。
配置LVM卷组可按照如下步骤:
1)创建分区。使用分区工具(如fdisk)创建LVM分区,方法和创建其他一般分区的方式是一样的,区别仅仅是LVM的分区类型为8e。
2)创建物理卷。创建物理卷的命令为pvcreate,利用该命令,将希望添加到卷组的所有分区或者磁盘创建为物理卷。将整个磁盘创建为物理卷的命令如下:
# pvcreate /dev/hdb
将单个分区创建为物理卷的命令如下:
# pvcreate /dev/hda5
3)创建卷组。创建卷组的命令为vgcreate,将使用pvcreate建立的物理卷创建为一个完整的卷组:
# vgcreate nova-volumes /dev/hda5 /dev/hdb
vgcreate命令的第一个参数是指定该卷组的逻辑名:nova-volume。后面的参数是指定希望添加到该卷组的所有分区和磁盘。vgcreate除了创建卷组nova-volume以外,还设置使用大小为4MB的PE(默认为4MB),这表示卷组上创建的所有逻辑卷都以4MB为增量单位来进行扩充或缩减。由于内核原因,PE大小决定了逻辑卷的最大值,4MB的PE决定了单个逻辑卷最大容量为256GB。若希望使用大于256G的逻辑卷,则在创建卷组时指定更大的PE。PE大小为8KB~512MB,并且必须是2的倍数(使用-s指定,具体参考man vgcreate)。
创建完nova-volume卷组后,nova-volume可以使用lvcreate在卷组上创建任意大小的逻辑卷。这是nova-volume可随意分配块设备的核心。
nova-volume卷组分配好后,还需要配置iscsi,
sed -i's/false/true/g'/etc/default/iscsitarget service iscsitarget start
在nova-controller.conf里面配置好参数:
–iscsi_ip_prefix=10.46.169.157
重启nova-volume服务即可。在外部可使用euca2tools工具进行验证。euca2tools有以下4个命令与卷相关:
euca-create-volume euca-attach-volume euca-detach-volume euca-delete-volume
nova-volume的主要工作如下:
□ Create volumes
□ Delete volumes
□ Establish Compute volumes
2.7.8 nova-network
nova-network是Nova里面负责虚拟机网络的组件,后来OpenStack发展了一个功能更强大的项目Neutron,用来替代nova-network。但是Neutron过于复杂,还不是很成熟,在生产环境中容易出问题,所以,目前还是有许多生产环境选择部署nova-network。另外,nova-network也依然保留在Nova代码内,还没有被移除,只是这部分代码现在比较稳定,很少改动。本节从基本概念开始,重点介绍这部分内容。
1.关键概念
Nova中有fixed_ip和floating_ip概念。
(1)fixed_ip
这是内网中虚拟机所使用的内部IP地址,从虚拟机创建起到虚拟机终止为止,都需要一个固定的fixed_ip来对应这个虚拟机,一个虚拟机必须要有fixed_ip。集群的网络类型决定虚拟机获取fixed_ip的方式,具体使用何种方式是在系统中预先配置好的,nova-controller.conf有一个选项network_manager来指定具体的方式。
(2)floating_ip
这是外网的IP地址,可动态指派给虚拟机,也可随时收回。floating_ip是可选的(用户需要这台虚拟机可供外网访问时,才需要为其指派floating_ip)。flaoting_ip不是在虚拟机的虚拟网卡上,而是在nova-network的外围网络接口上分配。nova-network会根据用户配置指令做NAT,将用户内网的fixed_ip映射到外网的floating_ip,这样,用户访问floating_ip时,会通过nova-network重定向到具体的flixed_ip上。nova-network通过这种NAT机制控制虚拟机的内外网连通性。
在linux_net模块实现的floating_ip的绑定和解绑方法如下:
def bind_floating_ip(floating_ip, device): """Bind ip to public interface.""" _execute('ip', 'addr', 'add', str(floating_ip) + '/32', 'dev', device, run_as_root=True, check_exit_code=[0, 2, 254]) if FLAGS.send_arp_for_ha: _execute('arping', '-U', floating_ip, '-A', '-I', device, '-c', 1, run_as_root=True, check_exit_code=False) def unbind_floating_ip(floating_ip, device): """Unbind a public ip from public interface.""" _execute('ip', 'addr', 'del', str(floating_ip) + '/32', 'dev', device, run_as_root=True, check_exit_code=[0, 2, 254])
fixed_ip、floating_ip资源池都是由管理员事先配置好的,存放于Nova数据库的fixed_ip表和floating_ips表中。Daiblo版本中整个OpenStack集群只有一个floating_ips资源池。为便于系统管理员管理不同租户的floating_ips,从Essex版本开始引入多个floating_ips_pools的概念,其实就是对floating_ips地址段进行划分,从概念和数据库表属性上进行表征,不同的池属于不同的租户。
2.网络类型
目前,Nova支持3种类型的网络:Flat、Flat DHCP和VLAN。在一个云系统中,这3种网络可以同时存在。但用户不能为给定的项目选择网络类型,因此在一个给定的compute部署中,只能配置一种网络类型。
(1)Flat模式
Flat模式中,网络管理员需要设定一个子网。VM的IP地址需要从该子网中抓取,同时在发布实例时注入镜像中。每个实例从可用的地址资源池中获取一个fixed_ip。网络管理员必须在托管网络的网络控制器和托管实例的云控制器间配置Linux网桥(通常命名为br100)。系统的所有实例都要由管理员手动配置连接到该网桥上。compute在创建虚拟机过程中,会向nova-network发指令,索要IP地址,nova-compute收到nova-network返回的IP地址后,在虚拟机创建时注入该IP地址。
注意:配置注入目前仅支持Linux-style的系统,网络配置信息记录在/etc/network/interfaces中。
FlatManager是Flat网络的管理类,它不负责网桥和VLAN的创建工作。用户在创建网络时负责通过nova-manage来创建指定的网桥。需要在所有的compute host节点上创建网桥。为主机创建单个网络的命令如下:
nova-manage network create 192.168.0.0/24 1 256
目前Nova不支持由一个管理器创建多个网络,但可通过修改allocate_fixed_ip和get_network来获得新的网络逻辑。管理员可手动修改fixed_ip资源池包含的IP地址列表。
当flat_injected=True时,compute host将注入网络配置给VM。它通过修改/etc/network/interfaces实现,目前只支持基于Debian的系统。
该模式下,metadata转发由网关处理,发送给169.254.169.254:80的请求会被转发给API服务器。
FlatManager实现的函数有def_setup_network_on_host(self,context,network):
"""Sets up network on this host,设置内网的虚拟网络.""" network['dhcp_server'] = self._get_dhcp_ip(context, network) self.l3driver.initialize_gateway(network) if not FLAGS.fake_network: dev = self.driver.get_dev(network) self.driver.update_dhcp(context, dev, network) if(FLAGS.use_ipv6): self.driver.update_ra(context, dev, network) gateway = utils.get_my_linklocal(dev) self.db.network_update(context, network['id'], {'gateway_v6': gateway})
Flat Networking使用一台网络适配器作为网桥,传输多节点间的网络通信。该设置可在主机的单个适配器或多个适配器上实现。该模式不需要具有打VLAN标签功能的交换机,通常用于概念证明。如果选择使用Flat networking模式,Nova其实是不管理网络的。相反,IP地址通过文件系统(或客户端代理)注入实例中。如果需要Metadata转发机制,则必须在网关上手动配置。nova.conf中的配置选项如下:
network_manager=nova.network.manager.FlatManager
注意:在Flat networking模式下,失败的flat_injection将使VM在启动时不能收到其IP信息。
Compute默认定义了一个名为br100的网桥设备(存储在Nova数据库中),网桥的名称是可以更改的。
在任意Flat Networking(Flat|FlatDHCP)模式下,nova-network主机负责转发私有网络中的通信。网络主机需要配置br100来与其他托管VM的节点进行会话。两种Flat模式中,VM的默认网关都是运行Nova-network的主机。
设置compute节点的外部IP地址到网桥上,并添加eth1到网桥的网络接口配置文件如下:
sudo brctl addbr br100 #创建网桥br100 sudo brctl addif br100 eth1 #添加设备到网桥br100 sudo ip addr add 10.0.0.1/24 brd 10.0.0.255 dev br100 #设置br100管理的子网
下一步是重启网络,使用命令sudo/etc/init.d/networking重启。图2-9给出all-in-one的网络环境。
图2-9 Flat网络,all-in-one服务器配置
对存在多个compute节点,而仅有一个网络适配器的部署环境,可在做“冒烟测试”或概念证明时使用,图2-10表示此种环境的网络设置情况。
图2-10 Flat网络,一个接口,多个服务器
对存在多个compute节点,而有多个网络适配器的部署环境,能隔离admin和数据通信,图2-11表示此种环境的网络设置情况。
图2-11 Flat网络,多个接口,多个服务器
(2)Flat DHCP模式
虚拟机通过DHCP方式获取fixed_ip地址,虚拟机在启动时通过dhcpdiscover向DHCP服务器发起获取IP地址的请求。一个DHCP服务器侦听网桥br100(由管理员手动配置),收到DHCP请求后,分配指定子网中的一个fixed_ip响应给虚拟机。这种模式最为简单。FlatDHCPManager不会注入网络设置给guest,但管理网桥。
类似于Flat模式,所有instance挂接到一个单独的compute节点上。该模式下compute需要桥接到一个以太网设备上(默认是eth0),同时需要运行dnsmasq服务作为DHCP服务器来侦听网桥。实例通过dhcpdiscover获取其fixed_ip。
在两种Flat模式下,compute为每个项目&实例创建iptables/ebtables目录来隔离IP、MAC地址欺诈或ARP中毒。
实现的函数如下:
1)def init_host(self)
"""Do any initialization that needs to be run if this is a standalone service. """ #将linux_net作为后端的3层驱动器 self.l3driver.initialize() super(FlatDHCPManager, self).init_host() self.init_host_floating_ips()
2)def_setup_network_on_host(self,context,network)
"""Sets up network on this host.""" network['dhcp_server'] = self._get_dhcp_ip(context, network) self.l3driver.initialize_gateway(network) if not FLAGS.fake_network: dev = self.driver.get_dev(network) self.driver.update_dhcp(context, dev, network) if(FLAGS.use_ipv6): self.driver.update_ra(context, dev, network) gateway = utils.get_my_linklocal(dev) self.db.network_update(context, network['id'], {'gateway_v6': gateway})
3)def_teardown_network_on_host(self,context,network)
if not FLAGS.fake_network: network['dhcp_server'] = self._get_dhcp_ip(context, network) dev = self.driver.get_dev(network) self.driver.update_dhcp(context, dev, network)
4)def_get_network_by_id(self,context,network_id)
return NetworkManager._get_network_by_id(self, context.elevated(), network_id)
5)def_get_network_dict(self,network)
"""Returns the dict representing necessary and meta network fields""" # get generic network fields network_dict = super(FlatDHCPManager, self)._get_network_dict(network) # get flat dhcp specific fields if self.SHOULD_CREATE_BRIDGE: network_dict['should_create_bridge'] = self.SHOULD_CREATE_BRIDGE if network.get('bridge_interface'): network_dict['bridge_interface'] = network['bridge_interface'] if network.get('multi_host'): network_dict['multi_host'] = network['multi_host'] return network_dict
在Flat DHCP模式下,运行nova-network的host作为虚拟节点的网关。每个集群需要运行一个nova-network。在nova-compute节点的conf文件中设置network-host,告诉nova-compute host nova-network在哪运行,以完成nova-compute与nova-network间的通信。nova-network将对数据库进行跟踪,通过DHCP正确配置的VM实例。通过设置iptables规则允许VM与外网进行通信,同时连接一个指定的metadata服务器来检索云中的信息。
Flat DHCP模式下,nova-compute主机不需要得到VM网络的IP地址便能启动VM,因为网桥将VM和network host放置在同一个逻辑网络中。在启动一个VM时,VM发送一个DHCP包,在network host的DHCP服务器上响应并为其分配一个IP地址。
使用libvirt驱动器时,环境设置如图2-12所示。
图2-12 使用libvirt驱动器的Flat DHCP网络
在设置flat_interface时需特别小心。如果指定一个有IP地址的接口,将出错;如果该接口是SSH连接使用的接口,将不能解决该问题,除非有ipmi/console权限。在Flat DHCP模式下,--network_size等于整个固定IP的数量。如果网络过大,在初始化时将花费较长时间(需在数据库中创建所有IP记录)。
如果在你的主机上有一个没有IP的未使用的接口,则可告诉Flat DHCP桥接到该接口(在配置文件中设定falt_interface=?)。nova-network主机会自动为网桥添加网关IP。也可手动添加interface到br100上,配置如下:
dhcpbridge_flagfile=/etc/nova/nova.conf dhcpbridge=/usr/bin/nova-dhcpbridge network_manager=nova.network.manager.FlatDHCPManager fixed_range=10.0.0.0/8 flat_network_dhcp_start=10.0.0.2 flat_network_bridge=br100 flat_interface=eth2 flat_injected=False public_interface=eth0
对于任意Flat网络(Flat、Flat DHCP),运行nova-network的主机负责转发私有网络的通信,作为VM的默认网关。该主机需要有一个桥接口,用于与其他托管VM的主机进行会话。
当VM发送通信给互联网时,首先发送到默认网关(运行nova-network的主机),如图2-13所示。
图2-13 单适配器主机,第一次路由
下一步,运行nova-network的主机转发通信到互联网,如图2-14所示。
注意:如果使用单个接口,则接口需要设置为混杂模式才能正确转发通信。该情况在具有两个接口的物理主机上是不会发生的。
图2-14 单适配器主机,第二次路由
(3)VLAN模式
VLAN是nova-network默认的网络模式,此模式中,Nova为每个项目创建一个VLAN和网桥。对于多节点部署,VLAN网络模式要求支持打VLAN标签(IEEE802.1Q)的交换机。项目有一个私有IP的范围,这些私有IP仅可从VLAN内部访问。在该模式下,每个项目有独立的VLAN、Linux网桥和子网。Vlan中子网的网络创建命令如下:
nova-manage network create 10.0.0.0/8 3 16 #创建3个网络,范围从10.0.0.0开始
VlanManager试图解决Flat管理器存在的两个缺陷:
1)缺乏可扩展性(在整个OpenStack集群中,Flat管理器依赖于单一的L2广播域)。
2)缺少正确的租户隔离机制(所有租户共享单一的IP池)。
该方式比较复杂,但是虚拟机之间的隔离也最好。使用方式简述如下。一个项目配置一个VLAN,启动一个DHCP服务器,属于该项目的虚拟机启动时在DHCP服务器处分配一个带有VLAN标签的fixed_ip。这样,这些具有相同VLAN标签的fixed_ip之间可以互相连通,不同VLAN标签的fixed_ip之间相互隔离。为了能够在外围访问VLAN虚拟机,在Nova环境中需要单独设置一个VPN虚拟机(cloudpipe),并为用户生成一个访问该VPN的证书和密钥。该虚拟机提供VPN服务,外网用户先用VPN登录,才能访问VLAN中其他虚拟机。
子网由网络管理员指定,并在项目请求时分配。DHCP服务器为每个VLAN中的VM实例分发指定子网中的IP地址。属于同一项目的所有实例被桥接到相同的VLAN上。Nova通过用户请求创建Linux网桥和VLAN。
注意:在默认的compute环境中,一旦销毁一个VM,需要花费一段时间将IP地址从被销毁的VM上撤离下来,以便之后分配给其他新instance。
如果设置force_dhcp_release=True,则在销毁一个VM时,Compute服务会发送一个DHCP release包,则分配给VM的IP地址立即被释放。该配置项用于Flat DHCP和VLAN模式。运用此选项需要运行dhcp_release服务,这需确保在所有nova-compute的主机上都运行了该服务。服务路径是/usr/bin/dhcp_release。
vlanManager中比较重要的函数如下:
def create_networks(self,context,**kwargs):
"""基于参数创建网络""" #校验num_networks + vlan_start is not > 4094, fixes lp708025 if kwargs['num_networks'] + kwargs['vlan_start'] > 4094: raise ValueError(_('The sum between the number of networks and' ' the vlan start cannot be greater' ' than 4094')) #校验num networks and network size fits in fixed_net fixed_net = netaddr.IPNetwork(kwargs['cidr']) if len(fixed_net) < kwargs['num_networks'] * kwargs['network_size']: raise ValueError(_('The network range is not big enough to fit ' '%(num_networks)s. Network size is %(network_size)s') % kwargs) #调用NetworkManager中的create_networks实现网络的创建工作 return NetworkManager.create_networks(self, context, vpn=True, **kwargs)
Nova可将不同项目的VM设置在不同的子网中,每个子网具有不同的VLAN标签,当用户具有很大的IP空间时是非常有用的(大空间被划分为很多小的子网)。划分子网的目的是控制广播的范围。对多租户环境下划分附加的隔离层也是非常有用的。
注意:在VLAN模式中,网络和子网常常交换使用。一般情况下,提及的IP地址范围指的都是子网。
运行VLAN模式比其他两种模式都要复杂,特别之处如下:
□ 必须启用IP转发。
□ 运行nova-network和nova-compute的主机必须加载8021q内核模块。
□ 网络交换机必须支持打VLAN标签功能。
□ 在compute环境中必须指明特定的VLAN标签用于打VLAN标签。
□ 需要从网络管理员处获得一些必要的信息,用于正确配置Nova(如:netmask、broadcast、gateway、ethernet设备、VLAN ID)。
□ 该模式是Nova默认的网络模式,也可使用network_manager=nova.network.manager.VlanManager指明。
由网络管理员创建的网桥必须挂接vlan_interface指明的接口,默认使用eth0。fixed_range是用于描述整个IP空间的CIDR(无类型域间选路,Classless Inter-Domain Routing)块,该IP地址空间会被划分为多个子网。
network_size指明每个网络中默认的IP地址数量,该选项在网络创建时能被重写。使用nova-manage network create命令创建网络。下面是一个创建网络的示例:
nova-manage network create --label=example-net --fixed_range_v4=172.16.169.0/24 --vlan=169 --bridge=br169 --project_id=a421ae28356b4cc3a25e1429a0b02e98
创建的网络example-net被分配给租户a421ae28356b4cc3a25e1429a0b02e98。子网172.16.169.0/24的VLAN tag是169(可以任意指定,最好与地址的第三位相同)。该命令还会在运行nova-network服务的主机上创建一个网桥接口设备,名为br169,该设备会出现在ifconfig命令的输出内容中。
每个网络都与一个租户相关联。可在网络创建时指定该关联关系(使用-project_id)。使用Keystone tenant-list命令获取已创建的租户列表及相应的ID号。
代替手动指定VLAN、桥和项目ID,用户可一次创建多个网络,同时将Compute服务自动与租户的网络相关联,同时自动生成VLAN ID和桥接口名。下面的命令即为创建100个网络(从172.16.100.0/24到172.16.199.0/24):
nova-manage network create --num_networks=100 --fixed_range_v4=172.16.100.0/24
nova-manage network create命令支持很多配置选项,下面是该命令的-help信息:
Usage: nova-manage network create <args> [options] Options: -h, --help #显示帮助信息 --label=<label> Label for network (ex: public) --fixed_range_v4=<x.x.x.x/yy> IPv4 subnet (ex: 10.0.0.0/8) --num_networks=<number> Number of networks to create --network_size=<number> Number of IPs per network --vlan=<vlan id> vlan id --vpn=VPN_START vpn start --fixed_range_v6=FIXED_RANGE_V6 IPv6 subnet (ex: fe80::/64 --gateway_v6=GATEWAY_V6 ipv6 gateway --bridge=<bridge> VIFs on this network are connected to this bridge --bridge_interface=<bridge interface> the bridge is connected to this interface --multi_host=<'T'|'F'> Multi host --dns1=<DNS Address> First DNS --dns2=<DNS Address> Second DNS --uuid=<network uuid> Network UUID --project_id=<project id> Project id --priority=<number> Network interface priority
nova-mange network create命令中的flag能用来重写nova.conf中的环境设置:
--network_size #重写网络大小 --bridge_interface #vlan_interface 配置项
Root用户可查看已创建的网络的列表:
nova-manage network list
其他网络命令如下:
□ nova-manage network modify
□ nova-manage network delete
在网络被删除之前必须将其从项目上撤离下来。创建网络时,会自动触发数据库,更新可用的固定IP地址表。可通过如下命令查看固定IP地址与实际VM的关联情况:
nova-manage fix list
如果用户想通过VPN访问实例,则需要创建一个特定的VPN(命名为cloudpipe)实例,该实例后面会详细阐述。
为使节点支持打VLAN标签功能,root用户需安装VLAN包,并装载8021q内核模块。
# apt-get install vlan # modprobe 8021q #该模块位于/lib/modules/2.6.31-28-server/kernel/net目录下 #为使该模块在启动时被加载,需在/etc/modules下添加一行:8021q
下面给出一个在VLAN模式运行nova-network的nova.conf的例子:
network_manager=nova.network.manager.VlanManager vlan_interface=eth0 fixed_range=172.16.0.0/12 network_size=256
一些情况下,Network Manager在停止时并不能正确地拆卸bridges和VLAN。此时如果重启Network Manager将会出错,检查错误日志文件会发现bridge设备已近存在。如果出现这种情况,用户需要手动拆卸bridge和VLAN设备,也可手动终止所有dnsmasq进程,命令如下:
# stop nova-network # vconfig rem vlan169 #移出Vlan169 # ip link set br169 down #关闭网桥br169 # brctl delbr br169 #删除网桥br100 # killall dnsmasq # start nova-network
3.各种网络类型间的区别
在Flat Managers模式下,管理员操作network的工作流程如下:
1)创建一个具有很多固定IP的网络(典型的是16-bitnetmask或更少),由所有租户共享。
Nova-manager network create –fixed_range_v4=10.0.0.0/16–label=public
2)创建租户。
3)一旦租户启动实例,就会从共享的IP资源池主攻获取一个空闲的IP地址。
因此,典型的IP分配给实例的模式如下。
租户1:
租户2:
从图中可以看出,租户1和租户2位于相同的网络(10.0.0.0)中。
在VLAN Manager模式下,管理员的工作流程如下:
1)创建一个新的租户,并注意其ID号。
2)为新租户创建一个专用的固定IP网络。
nova-manager network create–fixed_range_v4=10.0.0.0/24–vlan=101-–project_ id=tenantID
3)在实例产生之前,会自动从租户的私有IP池中分配一个IP地址。
对比FlatDHCPManager,VLAN为网络定义了两点:
□ 将一个网络与指定的租户关联(--project_id=<tenanted>)。这使得没有其他租户可以访问到该IP池。
□ 为该网络设置一个独立的VLAN(--vlan=102)。
一旦一个租户SPAWN一个新的VM,它会自动从该专用池中获取一个地址。也会被放置到一个专用的VLAN中,该VLAN由OpenStack自动创建和维护。因此VLAN模式下,是为不同的租户创建不同的VLAN。
租户1:
租户2:
由上可以看出不同租户的实例位于不同的IP池中。
4.使用nova-network管理网络
(1)nova-network工作实现
首先是nova-network的服务启动流程,如图2-15所示。
在Flat DHCP网络模式下,启动虚拟机后的网络请求流程如下:
1)请求MAC地址和fixed_ip地址,fixed_ip是直接读取db获得的。
nova.compute.manager.run_instancenova.compute.manager._run_instance(...)nova.compute.manager._allocate_networknetwork_info=nova.compute.manager.network_api.allocate_for_instance:注:#network_api=network.API()=nova.network.api.API()nw_info = rpc.call(..allocate_for_instance…)#处理为实例分配网络资源的方法nova.network.manager.allocate_for_instance:networks = self._get_networks_for_instanceself._allocate_mac_addresses self.add_virtual_interface self._add_virtual_interface utils.generate_mac_address() self._allocate_fixed_ips self.allocate_fixed_ip address = self.db.fixed_ip_associate 或address = self.db.fixed_ip_associate_pool
图2-15 nova-network的服务启动流程
2)nova-network中DHCP服务器维护IP地址与MAC地址的映射,并返回网络信息。
□ lease_fixed_ip(self,context,address)当IP被租用时,由dhcp-bridge调用,该函数主要是更新数据库的操作。
□ add_lease(mac,ip_address)使用DHCP服务器实现IP地址与MAC地址的关联。
□ DHCP服务器发送一个cast消息。
□ DHCP服务器创建一个新的连接,连接到VM。
□ nova-network打包网络信息,并发送rpc消息返回给nova-compute。
3)网络连接好以后,DHCP服务器会周期性地调用odd_lease(mac,ip_address)方法维护映射关系。
4)创建floating_ip,网络首先解析收到的rpc消息,然后依次调用以下函数来分配一个动态IP。
nova.network.api.allocate_floating_ip nova.network.manager.allocate_floating_ip floating_ip = self.db.floating_ip_allocate_address nova.db.api.floating_ip_allocate_address IMPL.floating_ip_allocate_address=nova.db.sqlalchemy.api.floating_ip_allocate_address
5)为实例关联一个动态IP。
Nova-network收到一个get_instance_nw_info的rpc消息,对其进行解析,从解析出来的信息中为_get_dhcp_ip抓取信息量,nova-network解析associate_floating_ip的rpc消息。
在nova-network上执行以下命令:
sudo ip addr add 10.46.169.213 dev eth0 #为nova.network.linux_net.apply()方法抓取信息量iptables #为nova.network.linux_net.apply()方法抓取文件lock "iptables" sudo iptables-save -t filter sudo iptables-restore sudo iptables-save -t nat sudo iptables-restore
(2)使能ping和SSH
用户使用euca-authorize或nova-secgroup-add-rule来使能对VM的访问。在此之前需要开通VM的ping和SSH能力。
注意:如果与nova-api交互的凭据被放置在/root/.bashrc,则只有Root用户可以执行该操作。相应地,如果凭据被放置在某个用户的.bashrc,则必须是该用户运行此命令。
使用Nova命令行工具:
$ nova secgroup-add-rule default icmp -1 -1 -s 0.0.0.0/0 $ nova secgroup-add-rule default tcp 21-21--s 0.0.0.0/0
使用euca2ools:
$ euca-authorize -P icmp -t -1:-1 -s 0.0.0.0/0 default $ euca-authorize -P tcp -p 21--s 0.0.0.0/0 default
如果在使用nova-secgroup-add-rule命令后,仍不能ping活SSH到VM,则查看nova-network节点启动的dnsmasq的数量,运行一个实例对应两个dnsmasg进程。如果不是该情况,则指定以下命令:
# killall dnsmasq # service nova-network restart
(3)配置公有(动态)IP地址
1)私有和公有IP地址。
每个虚拟实例会被自动分配一个私有IP地址,而公有IP对实例是可选的。OpenStack使用动态IP来表示可动态分配给虚拟实例的公有IP。Nova使用NAT(Network Address Translation)来为虚拟实例指派动态IP。
如果计划使用此功能,则需在nova.conf中增加以下数据,用来指定nova-network服务绑定公有IP到那个接口。
public_interface=vlan100
如果改变了nova.conf文件,需要重启nova-network服务。
2)创建一个可用的动态IP地址列表。
Nova维护一个动态IP地址列表,用来为实例分配动态IP。Root用户使用nova-manage floating create命令添加地址给该列表。例如:
# nova-manage floating create 10.46.169.224/29
下面是应用与动态IP的nova-manage命令:
nova-manage floating list #列出池中的所有floating IP addresses nova-manage floating create [cidr] #创建一个特定的floating IPs(一个单独的地址或一个子网) nova-manage floating delete [cidr] #使用与创建命令相同的参数删除floating IP地址
3)为实例关联动态IP。
为实例关联一个动态IP需要以下两步:
□ nova floating-ip-create:在产品那个可用的地址列表中分配一个动态IP地址。
□ nova add-floating-ip:给一个运行中实例添加一个被分配的动态IP地址。
下面给出一个动态IP(68.99.26.170)的操作示例。
如果实例不再需要某个公有IP地址,则从实例上移出该动态IP地址,并释放该地址。
$ nova remove-floating-ip 11-68.99.26.170 $ nova floating-ip-delete 68.99.26.170
4)自动添加动态IP。
nova-Network可以在实例启动时自动为其分配和关联动态IP地址,管理员仅需增加下面一行到nova.conf中,并重启nova-network即可。
auto_assign_floating_ip=True
注意:如果设置了该选项,则在所有动态IP被使用完后,再执行Nova boot命令会失败。
(4)移出项目的网络
通过简单的删除网络操作,是不能移出一个已分配给项目的网络的。管理员需要使用一个scrub命令将项目从网络中释放。
$ nova-manage project scrub projectname
(5)实例使用multinic
Multi-nic特征允许用户为实例增加多个接口,使以下应用场景可用成为可能:
□ SSL配置
□ 服务容错/HA
□ 带宽分配
□ 实例接入
3种网络模式中multinic的用法如图2-16~图2-18所示。
图2-16 multinic扁平网络管理器
图2-17 multinic扁平网络DHCP管理器
每个VIF数据有单独的网络,每个网络模型针对multinic用法都有自己的改变。
首先创建一个新网络,并将其与项目关联。
$ nova-manage network create --fixed_range_v4=20.20.0.0/24 --num_networks=1 --network_size=256 --label=test --project=$your-project
图2-18 multinic VLAN管理器
用户每次产生一个新实例时,将会从各自的DHCP服务器获得两个IP地址。
注意:需要保证实例上的第二个接口上电,否则实例的第二个IP地址将不可用。设置实例的接口的示例如下:
/etc/network/interfaces # The loopback network interface auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet dhcp
5.网络的故障诊断
(1)不能连接到动态IP
如果使用动态IP地址不能连接到实例,首先要确认默认安全组允许ICMP(ping)和SSH(端口22),这样才能连接到虚拟机。
确保NAT规则已经被添加到了nova-network主机的iptables中,Root用户运行以下命令:
# iptables -L -nv -A nova-network-OUTPUT -d 68.99.26.170/31--j DNAT --to-destination 10.0.0.3 # iptables -L -nv -t nat -A nova-network-PREROUTING -d 68.99.26.170/31--j DNAT --to-destination10.0.0.3 -A nova-network-floating-snat -s 10.0.0.3/31--j SNAT --to-source 68.99.26.170
核对公有地址(例子中是68.99.26.170)是否已经被成功地添加到了公有接口,使用如下命令查看:
$ ip addr 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000 link/ether xx:xx:xx:17:4b:c1-brd ff:ff:ff:ff:ff:ff inet 13.22.194.80/24 brd 13.22.194.255 scope global eth0 inet 68.99.26.170/31-scope global eth0 inet6 fe80::82b:2bf:fe1:4b2/64 scope link valid_lft forever preferred_lft forever
值得注意的是:VM使用SSH通过公有IP连接自己时,是连不通的,因为路由配置不允许这样操作。
用户可使用tcpdump来确认包是否被路由到compute主机上的入站接口上。如果报被传送到了compute主机但是连接失败的话,问题可能是包被反向路径过滤器丢弃。需要关闭入站接口的反向路径过滤器,如果入站接口是eth2,Root用户可进行如下操作:
# sysctl -w net.ipv4.conf.eth2.rp_filter=0
如果该方法解决了你的问题,这在/etc/sysctl.conf中增加下面一行,这样反向路径过滤器在下次重启compute主机时就不会被启用。
net.ipv4.conf.rp_filter=0
(2)防火墙不工作
为了帮助调试VM的网络问题,在/etc/nova/nova.conf中进行如下设置,不启用防火墙。
firewall_driver=nova.virt.firewall.NoopFirewallDriver
一旦网络问题被解决,就应该重新启用防火墙。