OpenStack cloud-init架構和應用

一,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/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章