nova文件注入流程分析




API支持说明

OpenStack Nova创建云主机API支持personality参数,用来把用户文件注入到新创建的云主机内部,该参数由两个部分组成,文件的目标路径和文件内容,比如用户想把

abc

efg

这个文件注入到云主机内部的 /etc/path/to/file 路径下,则参数可以设置为:

“personality”: [{“path”: “/etc/vm_monitor/info”, “contents”: “ZXhwb3J0IE9TX1VTRVJOQdJ…T05fTkFNRT1SZWdpb25Ud28K”}]

其中文件内容是需要经过base64转码的,可以支持同时注入多个不同路径的文件,在目标路径不存在的情况下会新创建整个路径和文件,在目标文件存在的情况下,会覆盖已有内容。 API说明页面

另外需要说明的是:key、密码、网络配置等文件的注入有专门的API参数支持,不虽然最终的注入逻辑是基本相同的,但是API参数却不是这个personality。

从本地镜像文件启动时文件注入功能实现介绍

nova文件注入序列图(简化版)如下:

nova文件注入流程分析 - aspirer - Aspirers blog

 

文字描述

  • 首先Nova api接受请求拿到base64编码后的文件内容,
  • 之后通过MQ转发到Nova scheduler进行计算节点的调度选择,
  • 选好节点后,把请求转发到相应计算节点,由Nova compute解码文件内容,
  • 之后根据本节点是否部署了libguestfs模块来决定文件注入的driver,
  • 如果安装了,使用libguestfs对应的guestfs driver来执行文件注入过程,
  • 否则选择localfs driver作为注入方法。

guestfs

关于guestfs介绍可以参考guestfs-python, 它是基于libguestfs开发的Python库,其最大好处就是可以不mount镜像就可以操作镜像内部文件系统里面的文件,支持read,write,writeappend,stat,mkdirp等基本文件读写操作。

libguestfs是一个C库,可以参考libguestfs,可以看出这是个功能非常强大的虚拟机镜像访问修改工具,其不足主要有两点,一是速度比较慢,二是有些系统上没有软件包,需要编译安装(redhat系列都支持,因为libguestfs是redhat主导开发的)

localfs

localfs driver则是完全依赖宿主机各种工具软件来实现的文件注入(loop或者nbd,如果是raw格式镜像使用loop设备,其它格式则使用nbd设备),这里以qcow2+nbd方式作为示例进行说明,这种情况下文件注入流程主要用到了如下几个软件:

  • nbd内核模块:用来加载nbd设备,供挂载镜像使用
  • qemu-nbd:一个qemu开源项目提供的nbd设备挂载小工具,可以把各种格式的镜像(比如qcow2等)文件挂载到nbd设备上,支持基于远程的nbd协议,可以把远程的镜像挂载到本地nbd设备上
  • kpartx:一个Linux下常用的分区工具
  • mount,mkdir,tee:这些软件不解释了

主要流程就是:

  1. 先把镜像准备好(从glance下载到计算节点,之后用qemu-img软件生成qcow2镜像及cow部分),
  2. 然后用qemu-nbd工具挂载镜像到找到的空闲nbd设备上,
  3. 之后用kpartx工具找到系统分区会把该分区映射到/dev/mapper下,名称为nbd0p1,0为刚刚挂载到的空闲nbd设备编号,
  4. 之后把/dev/mapper/nbd0p1 mount到宿主机的一个临时目录下,
  5. 最后就可以像操作本地文件一样操作虚拟机镜像里面的文件了。

需要注意的一点是,windows镜像的文件系统格式一般都是ntfs,如果要在宿主机上mount Windows分区,需要安装ntfs-3g软件,并且由于Windows vista及之后系统,都有一个保留分区,kpartx分区后,Nova默认选择注入到第一个分区,对于Windows系统镜像来说就是保留分区,而不是真正的系统分区,这一点需要注意,要么做镜像的时候,去掉保留分区,要么修改Nova代码,对Windows镜像特殊处理。

从云硬盘启动云主机时文件注入功能遇到的问题

上面的流程都是基于镜像文件已经保存到计算节点上这个前提下的,而从云硬盘启动的云主机,则没有镜像文件(是远程挂载过来的),虽说也是有一个mapper设备,但是在Nova代码流程里,挂载云硬盘是在文件注入流程之后才执行的,换句话说,注入文件的过程中,云硬盘还没有存在在计算节点上。Nova代码里也有明确说明,从云硬盘启动的云主机是不支持文件注入的,包括personality,key,password,net等。 简而言之,从云硬盘启动的云主机不支持文件注入!

解决方法

  1. 使用userdata配合cloud-init实现文件注入,用户现在是用的personality参数,需要修改为使用userdata参数, 好处:官方推荐,使用广泛,稳定可靠, 不足:需要cloud-init配合,增加一个维护项目。
  2. 使用user_data配合自研的脚本加上nas服务,实现类似cloud-init的功能, 好处:自己增加的脚本和自己开发的nas服务,可控程度高, 不足:所有功能都需要自己开发,比较耗时耗力,而且某些需求nas并不能满足,比如控制启动顺序等。
  3. 修改Nova代码,把personality在系统内部转换成user_data,之后再配合上面的两种方法来实现对用户透明的修改, 好处:对用户透明,不影响api, 不足:nova代码属于开源项目,如果有改动比较难融入上游代码,后期维护成本高。
  4. 使用qemu-guest-agent来实现文件注入,nova已经支持文件注入的api了,只不过libvirt driver没有实现具体的注入逻辑(Xen api driver支持了),如果我们通过qemu-guest-agent来完善libvirt driver的具体注入过程,是可以实现任意文件的注入的,但是一个问题是,要用户改变注入流程和接口,目前是创建云主机api参数传入,如果采用该方法,则需要云主机完全启动后,再调用nova文件注入api来实现文件注入,某些应用可能会遇到问题,比如配置文件还没有注入的时候就已经启动了,导致启动失败。

综合考虑推荐使用方法1,成熟方案且使用广泛。

需要注意的是,不论使用上面哪种方法,都不支持把已有的没有安装cloud-init或者没有打开user_data配置的云主机,转换成云硬盘系统盘,来恢复出一台支持文件注入的云主机。