天天看點

nova虛拟機鏡像從建立到檔案系統resize完整流程

1. 虛拟機鏡像的建立和resize流程

nova建立虛拟機涉及的元件比較多,調用比較複雜,這裡隻列出跟虛拟機鏡像建立相關的流程,友善理清虛拟機狀态變化的整個流程。

nova-api

nova.api.openstack.compute.servers.ServersController.create() # 接受建立請求,解析出image_uuid
  nova.compute.api.API.create ()
    nova.compute.api.API._create_instance() # 調用glance api擷取image對象
      nova.conductor.api.LocalComputeTaskAPI.build_instances()
        nova.conductor.manager.ConductorManager.build_instances() # 此處雖然接收block_device_mapping參數,但是是為了相容舊版,沒有使用。實際通過nova.objects.BlockDeviceMappingList.get_by_instance_uuid()擷取
          nova.compute.rpcapi.ComputeAPI.build_and_run_instance() # 使用cast方法調用nova-compute的build_and_run_instance方法。      

nova-compute

nova.compute.manager.ComputeManager.build_and_run_instance()
  nova.compute.manager.ComputeManager._do_build_and_run_instance()
    nova.compute.manager.ComputeManager._build_and_run_instance()

      nova.compute.manager.ComputeManager._build_resources()
        nova.compute.manager.ComputeManager._prep_block_device()
          nova.virt.block_device.attach_block_devices()
            nova.virt.block_device.DriverImageBlockDevice.attach()
              nova.volume.cinder.API.create()

      nova.virt.libvirt.driver.LibvirtDriver.spwan()
        nova.virt.libvirt.driver.LibvirtDriver._create_image() # 此處會判斷如果不是從volume啟動,則調用imagebackend去建立虛拟機鏡像
          nova.virt.libvirt.driver.LibvirtDriver._try_fetch_image_cache()
            nova.virt.libvirt.imagebackend.Image.cache()

              nova.virt.libvirt.imagebackend.Rbd.create_image()
                nova.virt.libvirt.imagebackend.Rbd.clone()
                  nova.virt.libvirt.storage.rbd_utils.RBDDriver.clone() # 建立虛拟機鏡像,此處如果所使用的image後端不支援clone,或者鏡像不可clone(比如rbd中不是raw格式的鏡像),會觸發異常,create_image調用下面的fetch_image函數
                
                nova.virt.libvirt.utils.fetch_image()
                  nova.virt.images.fetch_to_raw()
                    nova.virt.images.fetch()
                      nova.image.API.download()
                    nova.virt.images.convert_image()
                      nova.virt.images._convert_image() # 将鏡像拷貝到本地的/var/lib/instances/_base/目錄下,檔案名為md5(image).part,然後用qemu-img convert轉換為raw格式,名為md5(image).converted,最後重命名為md5(image)
                nova.virt.libvirt.storage.rbd_utils.RBDDriver.import_image() # 這一步是在clone失敗,執行fetch_image的情況下,判斷虛拟機鏡像不存在,執行import_image将fetch的鏡像導入到RBD後端作為虛拟機鏡像。

                nova.virt.libvirt.storage.rbd_utils.RBDDriver.resize() # 調整虛拟機鏡像大小

              nova.virt.libvirt.imagebackend.Rbd.resize_image() # 調整虛拟機鏡像大小,RBD後端實際上在create_image時已經resize了,不會執行這一步,這裡應該是為了確定其他後端能夠正确設定虛拟機鏡像的大小      

為了便于分析,用graphviz畫了在nova-compute的調用關系圖:

nova虛拟機鏡像從建立到檔案系統resize完整流程

注:存儲後端用的是Ceph,是以調用的後端代碼是nova.virt.libvirt.imagebackend.Rbd,如果nova使用了不同的後端,比如本地的qcow2鏡像、raw鏡像、lvm等,隻需要對照nova.virt.libvirt.imagebackend中提供的對應實作,出入不會太大,因為它們都繼承nova.virt.libvirt.imagebackend.Image,有相同的接口。

至此,虛拟機的鏡像已經建立完畢,并且resize為flavor所設定的大小。後面是虛拟機啟動後,resize分區和檔案系統的過程。

一般虛拟機鏡像中會安裝cloud-init或者配置啟動腳本來對虛拟機做初始化配置。在cloud-init或啟動腳本中調用growpart和resizefs來完成分區和檔案系統的擴容。

2. 分區的resize

cloud-init支援使用growpart和gpart對分區進行擴容,時配置的mode而定,預設會按順序檢測系統中是否安裝了這兩個工具,使用第一個找到的。

growpart是AWS的擴充分區工具,它分别使用sfdisk和sgdisk對MBR和GPT分區表操作,先将分區表導出,然後改寫分區的其實扇區位置,最後将改寫後的分區表導入,完成分區的擴容。

# growpart [diskdev] [partnum]      

gpart是FreeBSD推出的磁盤管理工具,GPT分區表将metadata的主本儲存在硬碟的開始,将副本儲存在硬碟的末尾,是以當虛拟機鏡像被擴容,相當于硬碟的容量變大,在GPT看來末尾的metadata副本丢失了,需要先執行recover指令恢複,然後再進行擴容。

# gpart recover [diskdev]
# gpart resize -i [partnum] [diskdev]      

3. 檔案系統的resize

cloud-init通過依次嘗試解析/proc/$$/mountinfo、/etc/mtab和mount指令的輸出,來擷取根目錄所挂載的分區和檔案系統格式。

針對不通的檔案系統,使用不同的指令擴容:

# resize2fs [devpth]    # ext檔案系統
# xfs_growfs [devpth]    # xfs檔案系統
# growfs [devpth]        # ufs檔案系統
# btrfs filesystem resize max [mount_point]    # btrfs檔案系統      

繼續閱讀