一,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工具來創建數據打包的鏡像。
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/