天天看點

揭開OpenStack 統計資源和資源排程的面紗

揭開OpenStack 統計資源和資源排程的面紗

引言

運維的同僚常常遇到這麼四個問題:

nova 如何統計 openstack 計算資源?

為什麼 free_ram_mb, free_disk_gb 有時會是負數?

即使 free_ram_mb, free_disk_gb 為負,為什麼虛拟機依舊能建立成功?

資源不足會導緻虛拟機建立失敗,但指定了 host 有時卻能建立成功?

本文以以上四個問題為切入點,結合 kilo 版本 nova 源碼,在預設 hypervisor 為 qemu-kvm 的前提下(不同 hypervisor 的資源統計方式差别較大 ),揭開 openstack 統計資源和資源排程的面紗。

nova 需統計哪些資源

雲計算的本質在于将硬體資源軟體化,以達到快速按需傳遞的效果,最基本的計算、存儲和網絡基礎元素并沒有是以改變。就計算而言,cpu、ram 和 disk等依舊是必不可少的核心資源。

從源碼和資料庫相關表可以得出,nova 統計計算節點的四類計算資源:

1.cpu: 包括 vcpus(節點實體 cpu 總線程數), vcpus_used(該節點虛拟機的 vcpu 總和)

2.ram: 包括 memory_mb(該節點總 ram),memory_mb_used(該節點虛拟機的 ram 總和),free_ram_mb(可用 ram)

note: memory_mb = memory_mb_used + free_ram_mb

3.disk:local_gb(該節點虛拟機的總可用 disk),local_gb_used(該節點虛拟機 disk 總和),free_disk_gb(可用 disk)

note:local_gb = local_gb_used + free_disk_gb

4.其它:pci 裝置、cpu 拓撲、numa 拓撲和 hypervisor 等資訊

本文重點關注 cpu、ram 和 disk 三類資源。

nova 如何收集資源

從 源碼 可以看出,nova 每分鐘統計一次資源,方式如下:

cpu

vcpus: libvirt 中 get_info()

vcpu_used: 通過 libvirt 中 dom.vcpus() 進而統計該節點上所有虛拟機 vcpu 總和

ram

memory: libvirt 中 get_info()

memory_mb_used:先通過 /proc/meminfo 統計可用記憶體, 再用總記憶體減去可用記憶體得出(資源再統計時會重新計算該值)

disk

local_gb: os.statvfs(conf.instances_path)

local_gb_used: os.statvfs(conf.instances_path)(資源再統計時會重新計算該值)

其它

hypervisor 相關資訊:均通過 libvirt 擷取

pci: libvirt 中 listdevices(‘pci’, 0)

numa: livirt 中 getcapabilities()

那麼問題來了,按照上述收集資源的方式,free_ram_mb, free_disk_gb 不可能為負數啊!别急,nova-compute 在上報資源至資料庫前,還根據該節點上的虛拟機又做了一次資源統計。

nova 資源再統計

首先分析為什麼需要再次統計資源以及統計哪些資源。從 源碼 可以發現,nova 根據該節點上的虛拟機再次統計了 ram、disk 和 pci 資源。

為什麼需再次統計 ram 資源?以啟動一個 4g 記憶體的虛拟機為例,虛拟機啟動前後,對比主控端上可用記憶體,發現主控端上的 free memory 雖有所減少(本次測試減少 600 mb),卻沒有減少到 4g,如果虛拟機運作很吃記憶體的應用,可發現主控端上的可用記憶體迅速減少 3g多。試想,以 64g 的伺服器為例,假設每個 4g 記憶體的虛拟機啟動後,主控端僅減少 1g 記憶體,伺服器可以成功建立 64 個虛拟機,但是當這些虛拟機在跑大量業務時,伺服器的記憶體迅速不足,輕着影響虛拟機效率,重者導緻虛拟機 shutdown等。除此以外,主控端上的記憶體并不是完全分給虛拟機,系統和其它應用程式也需要記憶體資源。是以必須重新統計 ram 資源,統計的方式為:

free_memory = total_memory - conf.reserved_host_memory_mb - 虛拟機理論記憶體總和

conf.reserved_host_memory_mb:記憶體預留,比如預留給系統或其它應用

虛拟機理論記憶體總和:即所有虛拟機 flavor 中的記憶體總和

為什麼要重新統計 disk 資源?原因與 ram 大緻相同。為了節省空間, qemu-kvm 常用 qcow2 格式鏡像,以建立 disk 大小為 100g 的虛拟機為例,虛拟機建立後,其鏡像檔案往往隻有幾百 kb,當有大量資料寫入時磁盤時,主控端上對應的虛拟機鏡像檔案會迅速增大。而 os.statvfs 統計的是虛拟機磁盤目前使用量,并不能反映潛在使用量。是以必須重新統計 disk 資源,統計的方式為:

free_disk_gb = local_gb - conf.reserved_host_disk_mb / 1024 - 虛拟機理論磁盤總和

conf.reserved_host_disk_mb:磁盤預留

虛拟機理論磁盤總和:即所有虛拟機 flavor 中得磁盤總和

當允許資源超配(見下節)時,采用上述統計方式就有可能出現 free_ram_mb, free_disk_gb 為負。

資源超配與排程

即使 free_ram_mb 或 free_disk_gb 為負,虛拟機依舊有可能建立成功。事實上,當 nova-scheduler 在排程過程中,某些 filter 允許資源超配,比如 cpu、ram 和 disk 等 filter,它們預設的超配比為:

cpu: conf.cpu_allocation_ratio = 16

ram: conf.ram_allocation_ratio = 1.5

disk: conf.disk_allocation_ratio = 1.0

以 ram_filter 為例,在根據 ram 過濾主控端時,過濾的原則為:

memory_limit = total_memory * ram_allocation_ratio

used_memory = total_memory - free_memory

memory_limit - used_memory < flavor[‘ram’],表示記憶體不足,過濾該主控端;否則保留該主控端。

相關代碼如下(稍有精簡):

def host_passes(self, host_state, instance_type): 

"""only return hosts with sufficient available ram.""" 

requested_ram = instance_type['memory_mb'] 

free_ram_mb = host_state.free_ram_mb 

total_usable_ram_mb = host_state.total_usable_ram_mb 

memory_mb_limit = total_usable_ram_mb * conf.ram_allocation_ratio 

used_ram_mb = total_usable_ram_mb - free_ram_mb 

usable_ram = memory_mb_limit - used_ram_mb 

if not usable_ram >= requested_ram: 

log.debug("host does not have requested_ram") 

return false123456789101112 

主控端 ram 和 disk 的使用率往往要小于虛拟機理論使用的 ram 和 disk,在剩餘資源充足的條件下,libvirt 将成功建立虛拟機。

随想:記憶體和磁盤超配雖然能提供更多數量的虛拟機,當該主控端上大量虛拟機的負載都很高時,輕着影響虛拟機性能,重則引起 qemu-kvm 相關程序被殺,即虛拟機被關機。是以對于線上穩定性要求高的業務,建議不要超配 ram 和 disk,但可适當超配 cpu。建議這幾個參數設定為:

cpu: conf.cpu_allocation_ratio = 4

ram: conf.ram_allocation_ratio = 1.0

ram-reserve: conf.reserved_host_memory_mb = 2048

disk-reserve: conf.reserved_host_disk_mb = 20480

指定 host 建立虛拟機

本節用于回答問題四,當所有主控端的資源使用過多,即超出限定的超配值時(total_resource * allocation_ratio),nova-scheduler 将過濾這些主控端,若未找到符合要求的主控端,虛拟機建立失敗。

建立虛拟機的 api 支援指定 host 建立虛拟機,指定 host 時,nova-scheduler 采取特别的處理方式:不再判斷該 host 上的資源是否滿足需求,而是直接将請求發給該 host 上的 nova-compute。

def get_filtered_hosts(self, hosts, filter_properties, 

filter_class_names=none, index=0): 

"""filter hosts and return only ones passing all filters.""" 

... 

if ignore_hosts or force_hosts or force_nodes: 

if force_hosts or force_nodes: 

# note(deva): skip filters when forcing host or node 

if name_to_cls_map: 

return name_to_cls_map.values() 

return self.filter_handler.get_filtered_objects()123456789101112 

當該 host 上實際可用資源時滿足要求時,libvirt 依舊能成功建立虛拟機。

最後,以一圖總結本文内容

揭開OpenStack 統計資源和資源排程的面紗

本文作者:布加迪

來源:51cto