1. 程式人生 > >Nova-scheduler淺析

Nova-scheduler淺析

  1. 簡介

Nova-scheduler是openstack核心元件nova的核心元件之一,由此可見其在openstack中的地位。

    顧名思義,Nova-scheduer是nova的排程器,目前是負責為建立/啟動虛擬機器例項尋找合適的計算節點,遷移虛擬機器的時候也負責檢查目的端的物理資源是否足夠,其重要性隨著計算節點數量的增加而增加。

    下圖是openstack架構圖,從中我們可以看出nova-scheduer是通過與message queue和nova database互動來完成排程任務的。

    Nova-scheduer的簡化工作流程可以用下圖表示:

也即過濾不可用節點,並對可用節點進行權重排序,根據使用者配置的策略選出最優節點。

下面是nova-scheduler的類圖:

  1. 計算節點可用資源通知

要想選出策略最優的節點,就需要首先知道各個節點當前的可用資源狀況以及請求需要的資源資訊,而後者是使用者通過API請求主動傳遞過來的,所以不是nova-scheduer關注的重點。

    下圖描述了nova-compute服務如何更新其節點資源資訊併發布到message queue,以及nova-scheduer如何收集並儲存這些資訊以備後續使用:

    上述圖示可以分為三個相互關聯的部分:資訊收集、資訊釋出、資訊儲存。資訊收集(上圖1.X部分)由nova-compute服務完成,當然它要依賴hypervisor適配層提供的介面,比如libvirt介面或者XenAPI介面等(實際執行收集動作的函式為nova/virt/libvirt/connection.py:HostState.update_status()),收集到的資訊供資訊釋出函式使用;資訊釋出(上圖2.X部分)是nova-compute服務通過RPC機制完成的,因為釋出是跨服務的(nova-compute到nova-scheduer),所以使用message queue也是順理成章的事情,如果資訊收集函式沒能把最新的資訊傳遞過來,那麼釋出上次更新到的資訊;資訊儲存(上圖message queue下面的部分)是nova-scheduer服務完成的,它從message queue中獲取相關資訊並存儲起來,就可以用來作為相關決策的依據。

    這裡有個問題就是當上一個資源佔用請求還沒有處理的時候,又來了一個新的請求,並且被排程到的計算節點的資源只能滿足一個請求,那麼第二個請求就會失敗。

  1. 從API到manager

這裡的API指的是scheduler的API(當然除了nova-api之外的其他服務的API到manager的流程也是類似的,這裡僅討論nova-scheduer)。我們知道API到manager的訊息傳遞是通過message queue完成的,nova裡面的message queue採用的是AMQP,與AMQP建立連線的過程是在服務啟動的時候完成的,所以我們首先要看下nova-scheduer服務的啟動流程。

Nova-scheduler服務指令碼:

建立服務例項和最後的阻塞等待就不用多說了,我們直接看啟動服務流程,nova/service.py:serve->launch_server->eventlet用greenthread啟動主執行緒執行run_server->Service.start():

我們繼續看建立消費者的過程:

這樣就走到了ProxyCallback類裡面:

繼續

我們看到獲取method是用getattr()方法實現的,在Service類裡面定義了__getattr__方法,

另外在nova-scheduer的manager.py的SchedulerManager也定義了__getattr__方法,用來處理manager中未實現的被呼叫到的方法,這些方法都被定向到了_schedule方法。

至此API到manager的流程結束。

這部分要感謝田田高同學的鼎力基情支援!

  1. 從manager到driver

這部分的流程比較簡單,在SchedulerManager類初始化的時候可以看到driver的初始化過程:

比如我們在resize虛擬機器的時候,nova的compute-api的resize方法會通過message queue呼叫scheduler的prep_resize方法:self._cast_scheduler_message(context, {"method": "prep_resize", "args": args}),這樣就會呼叫到SchedulerManager裡面的prep_resize方法,進而呼叫到MultiScheduler裡面的schedule_prep_resize方法,

這裡我們又看到了一個dict型別的self.drivers,可以在MultiScheduler的__init__方法中找到它的初始化過程,

這裡我們看到了兩個子driver,compute_driver和volume_driver,也都是可以在配置檔案裡面配的,預設值如下:

volume_driver用到的地方不多,在nova/volume/api.py:API.create()方法裡面有用到,目的是為了選擇一個nova-volume節點建立卷。

compute_driver用到的地方就比較多了,幾乎所有涉及到啟動虛擬機器類的動作都跟它有關,比如建立虛擬機器、start虛擬機器、resize虛擬機器、遷移虛擬機器等等,因為啟動虛擬機器涉及到選擇在哪個節點上啟動的策略問題。我們繼續以resize為例進行說明,從MultiScheduler的schedule_prep_resize方法呼叫到了FilterScheduler裡的schedule_prep_resize方法:

這樣就把prep_resize動作定向到了特定的nova-compute節點,以便讓resize的目的端節點做好相關準備工作。

除了FilterScheduler排程器之外還有其他的排程器,比如ChanceScheduler、SimpleScheduler,ChanceScheduler是隨機選擇一個執行中的host,SimpleScheduler則是根據已經佔用的核數來選擇host,當前佔用核數最少的host將被選中。這裡我們重點討論預設的FilterScheduler,這個排程器負責host的選擇以及選中後host的資源預佔,選擇host的依據是使用過濾器過濾host和對host計算權重費用,成功通過所有的過濾器並且權重費用最低的host將被選中。

  1. 從Driver到filter

FilterScheduler(目前只支援compute服務排程)有很多的filter用來選擇host,可以用的filter通過配置項'scheduler_available_filters'來確定,預設值為'scheduler_available_filters',根據註釋可以知道預設是遍歷所有可用的filter,也就是隻要在nova/scheduler/filters目錄下的*_filter.py都可以使用。

另外還有一個配置項是'scheduler_default_filters',這個引數可以配置預設使用的filter,也就是說雖然可用的filter很多,但並未全部使用,其預設配置有三個filter:'AvailabilityZoneFilter', 'RamFilter', 'ComputeFilter'。

我們先看下_scheduler這個方法,它負責篩選可用的Host列表,我們知道如果要做出決策,一定要有資料支援,否則就是瞎蒙了,所以下面我們先看下篩選Host所需要的資料:

從程式碼裡面可以看出需要兩個引數作為決策依據:Host和filter_properties,既然是篩選Hosts,那以Host作為輸入也是理所當然的,Host資訊的獲取方式是:首先查詢資料庫獲取到所有的compute 服務節點,然後查詢自己之前儲存的service_states屬性獲取相關資訊,最後封裝成HostState物件返回。

剩下的就是filter_properties了,這個引數儲存了虛擬機器的規格、以及特定的Host選擇要求等資訊,這些資訊都是由使用者在啟動虛擬機器的時候指定的,比如我們可以在建立虛擬機器的API的data裡面設定如下引數:

我們還可以指定特定的Host來啟動虛擬機器或者指定不在特定的Host上啟動:

'force_hosts': ['testhost'],ignore_hosts: ['testhost']

這兩個引數都是在執行過濾動作之前生效的,看下程式碼實現:

我們甚至還可以傳入任意key-value對到scheduler裡以備特定用途使用。

    filter_properties裡面整合了request_spec這個引數,這也是filter過濾Host的主要依據,通常request_spec包含的內容如下,其中黃色高亮部分是對filter或者costfunction比較重要的項:

    上面提到的passes_filters方法,是_schedule方法中執行過濾過程filter_hosts用到的,它屬於HostState類,也就是說每個需要過濾的Host都要通過這個方法進行過濾,成功通過的返回True,否則返回False。

我們再看_choose_host_filters是如何選擇過濾器的,以及返回的是什麼東東:

預設的過濾器肯定是可以用的,因為他們都是從可用過濾器裡挑選出來的,_choose_host_filters選出的好用的過濾器的host_passes方法最後都作為引數傳給了passes_filters,之後被呼叫用來過濾Host,我們再來看下這三個過濾器的host_passes方法:

  1. 從filter到cost function

用預定義的三個過濾器過濾完之後,會得到一個Hosts列表,儲存了所有通過三個過濾器的Host,列表中可能不止一個Host,所以還要在這些Host當中選擇一個最優的節點,以起到均衡資源分配的目的,選擇最優節點的方式就是利用costfunction計算出各個Host啟動虛擬機器所需的"費用",消耗"費用"最低的節點將被選中用來執行啟動或者遷入虛擬機器之類的操作。

上一節中提到過costfunction是在FilterScheduler類的_scheduler方法中通過呼叫get_cost_functions獲取的,程式碼如下:

我們再看下nova.scheduler.least_cost.compute_fill_first_cost_fn的定義:

現在我們來看最後的"費用"計算過程(也是在FilterScheduler類的_scheduler方法中呼叫的):

  1. 資源預佔

還有一個問題就是,如果一次啟動多個instance的話,nova-scheduer是一個一個的選擇Host的,而且中間不會更新節點的可用資源,這裡就有一個可用資源數量不準確的問題,比如Host1被選中啟動了一個instance,對nova-scheduer來說Host1的資源並沒有變化,第二個instance還是可能被啟動到Host1上,這是個非常明顯的問題,所以社群肯定是考慮到了的,社群的解決方法就是資源預佔,簡單來說就是先在nova-scheduer的程序內部把被選中的Host的可用資源中把已經啟動到這個Host上的instance的相關資源資訊給減掉,這樣就可以避免資源數量不準確的問題了。

  1. 其他問題

Nova-scheduer多節點佈置??--essex版本不支援,folsom正在開發。

問題參考:

社群藍圖:

Work items:

Added scheduling retries when build errors occur: DONE

Added resource tracking in the compute host to more gracefully control resource usage and provide up-to-date information to the scheduler: INPROGRESS

已經在F-3版本中實現的失敗後重新排程程式碼: