一,cloud-init简介

    cloud-init专为云计算环境中虚拟机实例/裸金属实例的初始化而开发的一个开源工具,它安装在虚拟机镜像/裸金属镜像中,创建实例时,通过nova组件的configdrive把预注入的数据打包成镜像,并挂载在实例的cdrom中,实例启动时,通过读取cdrom中的相关数据,对虚拟机进行初始化配置。

1,cloudinit安装

    centos和ubuntu server最新官方源中已经包含cloudinit,也可以直接采用yum或者apt-get安装。

2,cloudinit使用场景

    cloudinit安装在openstack虚拟机、裸金属镜像中,只适用于linux操作系统镜像。

    Windows镜像对应需要安装cloudbase-init

    OpenStack中如果要使用Config Drive实现元数据的注入,在制作image时一定要安装cloud-init软件,否则无法实现元数据注入。

    Metadata只能注入主机的基本信息。

    Userdata使用场景比较广,只要有配置云服务器的需求,尤其是在批量化模版化的情况下,使用userdata定制云主机最好的选择

3,链接

    cloudinit官方文档

    http://cloudinit.readthedocs.io/en/latest/

    Cloud-init github仓库地址

    https://github.com/cloud-init/cloud-init 

4,cloudinit应用架构图



二,config drive

    config drive 是一个特殊的文件系统,OpenStack 会将 metadata/userdata写到config drive,在虚拟机启动时,自动挂载给 instance。

    如过instance镜像中安装了cloud-init,config drive会被自动 mount并从中读取 metadata/userdata,进而完成后续的初始化工作。


三,metadate

    metadata就是虚拟机的元数据

1,什么是元数据

    元数据是对数据资源的描述,英文名称是“metadata”, 通常被解释为data about data,即关于数据的数据。元数据是指从信息资源中抽取出来的用于说明其特征、内容的结构化的数据,用于组织、描述、检索、保存、管理信息和知识资源。

    一个基本的元数据由元数据项目和元数据内容的构成。

    比如,关于一本书的元数据如下,包括书名、作者、出版社等描述的是这本书本来的属性,和书中的内容没有直接关系。

    书名: OpenStack设计与实现
    作者: 英特尔开源技术中心 
    出版社: 电子工业出版社
    定价: ¥99
 


   虚拟机的元数据,主要包括虚拟机自身的属性,如 hostname、网络配置信息、SSH 登陆秘钥等,键值对的形式描述例如:

    Hostname: myhost1
    IP: 192.168.1.100
    密码: Passw0rd


2,metadata

    metadata是云平台提供的云主机属性信息,在创建云主机的时候做默认配置,目前包括hostname、镜像名称、网络类型、ip地址等属性值,以键值对的形式给出。

2.1,虚拟机获取metadata的方式

    在OpenStack中,虚拟机中的cloud-init获取metadata信息的方式有两种:

    Config drive 和 metadata RESTful服务

2.1.1 Config drive方式获取metadata

    Config drive 机制是指OpenStack将metadata信息写入虚拟机的一个特殊的配置设备中,然后在虚拟机启动时,自动挂载并读取 metadata信息,从而达到获取metadata的目的。

    在客户端操作系统中,存储 metadata 的设备需要是ISO9660或者VFAT文件系统。

    默认是 iso9660,但这会导致 instance 无法在线迁移,必须设置成config_drive_format=vfat 才能在线迁移。配置完成后,重启 nova-compute 服务。

    config-drive 其实就是 Metadata-Source 的 “本地” 版本,它不依赖虚拟机网络信息,客户机操作系统可以直接通过一个设备读取 Metadata 信息。

    config drive的格式:config drive 的默认格式是一个"ISO 9660"的文件系统,可以在nova配置文件中指出:

vi /etc/nova/nova.conf
config_drive_format=iso9660

具体实现参考代码分析

要实现上述功能,需要宿主机和虚拟机镜像两者协同完成,它们需要各自满足一些条件:

1宿主机(OpenStack计算节点)

支持 config drive 机制的 Hypervisors 有:libvirt、hyper-v 和 VMware。

当使用 libvirt 和 VMware 作为 Hypervisor 时,需要确保宿主机上即计算节点运行的系统安装有 genisoimage 程序,并且设置 mkisofs_cmd 标志为 genisoimage 的位置。

当使用 hyper-v 作为 Hypervisor 时,需要设置 mkisofs_cmd 标志为 mkisofs.exe 的全路径,此外还需要在 hyper-v 的配置文件中设置 qume_img_cmd 为 qemu-img 命令的路径。

2虚拟机镜像

虚拟机镜像需要确保安装了 cloud-init。

OpenStack 提供了命令行参数--config-drive 用于配置是否在创建虚拟机时使用 config drive 机制。比如:(具体实现可参考代码分析

# nova boot --config-drive=true --image image-name --key-name mykey --flavor 1 --user-data ./my-user-data.txt myinstance --file /etc/network/interfaces=/home/myuser/instance-interfaces

或者在/etc/nova/nova.conf中配置,直接使OpenStack计算服务在创建虚拟机时默认使用config drive 机制。

force_config_drive=true

2.1.2 Metadata RESTful 服务方式获取metadata

    OpenStack提供了RESTful 接口,虚拟机可以通过 REST API 来获取 metadata 信息。提供该服务的组件为:nova-api-metadata。当然,要完成从虚拟机至网络节点的请求发送和相应,只有 nova-api-metadata 服务是不够的,此外共同完成这项任务的服务还有:Neutron-metadata-agent 和 Neutron-ns-metadata-proxy。

    由于metadata service结构太复杂,建议使用config drive的方式获取metadata


四,userdata

1,Userdata的优势

    userdata即云主机启动时用户提供的自定义数据,用户可提供除metadata主机属性之外的自定义功能,例如,用自定义脚本的方式,实现云主机初始化时的自定义磁盘分区、网卡bonging、多网络的网关设置等功能,userdata是实现云主机个性化定制的基础。

    userdata非常灵活,当然使用userdata也可以实现metadata可以实现的功能。

    可以把metadata理解为标准模式,能配置的功能已经给定key值;而userdata可理解为定制模式,只要格式语法正确,完全可以自定义主机的初始化功能。metadata是报团旅游,userdata是定制旅游。

2,Userdata的注入方式

    userdata注入方式有多种,常用的格式有:userdata-scripts和cloud-config。

    userdata-scripts:适用于需要通过执行shell脚本初始化实例的用户,以“#!/bin/sh”开头,从用户数据来看目前大部分用户都是直接通过这种格式输入userdata的, 也适用于较复杂的部署场景。

    cloud-config: 是cloud-init支持的特有格式,它把常用的个性化配置包装成YAML文件格式提供出来,通过这种形式可以更方便的完成常用配置,以“#cloud-config”为首行区分,紧随其后的是一个关联数组,提供的键包括ssh_authorized_keys、hostname、write_files、manage_etc_hosts等。


五,Metadata和Userdata比较

    Metadata 主要包括虚拟机自身的一些常用属性,如 hostname、网络配置信息、SSH 登陆秘钥等,主要的形式为键值对。而 user data 主要包括一些命令、脚本等。User data 通过文件传递,并支持多种文件格式,包括 gzip 压缩文件、shell 脚本、cloud-init 配置文件等。虽然 metadata 和 user data 并不相同,但是 OpenStack 向虚拟机提供这两种信息的机制是一致的,只是虚拟机在获取到信息后,对两者的处理方式不同罢了。


六,主机挂载configdrive的方式

    在一个初始创建好的instance里面访问config drive,如果OS支持通过label访问磁盘,那么在instance里会有一个叫“config-2”的volume label,可以挂载它到instance本地:

mount /dev/disk/by-label/config-2 /mnt/config 

    如果OS没有使用udev将不会有/dev/disk/by-label目录,blkid可以发现它,并且同样可以被挂载:

# blkid -t LABEL="config-2" -odevice
/dev/vdb
# mount /dev/vdb /mnt/config


七,configdrive注入代码分析

1,nova/virt/vmwareapi/vmops.py

spawn方法是创建实例的入口,由configdrive.py文件中的的required_by方法可知,当创建实例时,若实例对象中指定config_drive、或者配置文件中的force_config_drive为真,都会创建configdrive。

调用_configure_config_drive() --> _create_config_drive()--> make_drive()方法创建镜像文件。

class VMwareVMOps(object):
    def spawn(self, context, instance, image_meta, injected_files,
              admin_password, network_info, block_device_info=None):

        metadata = self._get_instance_metadata(context, instance)

        if configdrive.required_by(instance):
            self._configure_config_drive(
                    instance, vm_ref, vi.dc_info, vi.datastore,
                    injected_files, admin_password, network_info)


    def _configure_config_drive(self, instance, vm_ref, dc_info, datastore,
                                injected_files, admin_password, network_info):
        session_vim = self._session.vim
        cookies = session_vim.client.options.transport.cookiejar
        dc_path = vutil.get_inventory_path(session_vim, dc_info.ref)
        uploaded_iso_path = self._create_config_drive(instance,
                                                      injected_files,
                                                      admin_password,
                                                      network_info,
                                                      datastore.name,
                                                      dc_path,
                                                      instance.uuid,
                                                      cookies)
        uploaded_iso_path = datastore.build_path(uploaded_iso_path)
        self._attach_cdrom_to_vm(
            vm_ref, instance,
            datastore.ref,
            str(uploaded_iso_path))

    def _create_config_drive(self, instance, injected_files, admin_password,
                             network_info, data_store_name, dc_name,
                             upload_folder, cookies):
        if CONF.config_drive_format != 'iso9660':
            reason = (_('Invalid config_drive_format "%s"') %
                      CONF.config_drive_format)
            raise exception.InstancePowerOnFailure(reason=reason)

        LOG.info(_LI('Using config drive for instance'), instance=instance)
        extra_md = {}
        if admin_password:
            extra_md['admin_pass'] = admin_password

        inst_md = instance_metadata.InstanceMetadata(instance,
                                                     content=injected_files,
                                                     extra_md=extra_md,
                                                     network_info=network_info)
        try:
            with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb:
                with utils.tempdir() as tmp_path:
                    tmp_file = os.path.join(tmp_path, 'configdrive.iso')
                    cdb.make_drive(tmp_file)
                    upload_iso_path = "%s/configdrive.iso" % (
                        upload_folder)
                    images.upload_iso_to_datastore(
                        tmp_file, instance,
                        host=self._session._host,
                        port=self._session._port,
                        data_center_name=dc_name,
                        datastore_name=data_store_name,
                        cookies=cookies,
                        file_path=upload_iso_path)
                    return upload_iso_path
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_LE('Creating config drive failed with error: %s'),
                          e, instance=instance)

2,nova/virt/configdrive.py

配置文件中的'mkisofs_cmd'默认值为'genisoimage',即默认用genisoimage工具来创建数据打包的镜像。

将用户数据打包成iso是在计算节点上进行的,需要mkisofs工具,因此,若实例要支持cloudinit,在每个计算节点上都要安装genisoimage(使用yum、apt-get连镜像源安装)。

def required_by (instance):
    image_prop = instance.image_meta.properties.get(
        "img_config_drive",
        fields.ConfigDrivePolicy.OPTIONAL)
    return (instance.config_drive or
            CONF.force_config_drive or
            image_prop == fields.ConfigDrivePolicy.MANDATORY
            )
configdrive_opts = [
    cfg.StrOpt('config_drive_format',
               default='iso9660',
               choices=('iso9660', 'vfat'),
               help='Config drive format.'),
    cfg.BoolOpt('force_config_drive',
                help='Force injection to take place on a config drive',
                default=False),
    cfg.StrOpt('mkisofs_cmd',
               default='genisoimage',
               help='Name and optionally path of the tool used for '
                    'ISO image creation')
    ]


class ConfigDriveBuilder(object):
    """Build config drives, optionally as a context manager."""
     
    def make_drive(self, path):
        """Make the config drive.


        :param path: the path to place the config drive image at


        :raises ProcessExecuteError if a helper process has failed.
        """
        with utils.tempdir() as tmpdir:
            self._write_md_files(tmpdir)


            if CONF.config_drive_format == 'iso9660':
                self._make_iso9660(path, tmpdir)
            elif CONF.config_drive_format == 'vfat':
                self._make_vfat(path, tmpdir)
            else:
                raise exception.ConfigDriveUnknownFormat(
                    format=CONF.config_drive_format)
    def _make_iso9660(self, path, tmpdir):
        publisher = "%(product)s %(version)s" % {
            'product': version.product_string(),
            'version': version.version_string_with_package()
            }


        utils.execute(CONF.mkisofs_cmd,
                      '-o', path,
                      '-ldots',
                      '-allow-lowercase',
                      '-allow-multidot',
                      '-l',
                      '-publisher',
                      publisher,
                      '-quiet',
                      '-J',
                      '-r',
                      '-V', 'config-2',
                      tmpdir,
                      attempts=1,
                      run_as_root=False)


参考:

https://www.ibm.com/developerworks/cn/cloud/library/1509_liukg_openstackmeta/


Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐