YARN是雙層調度範式,YARN Scheduler是YARN的主調度器,YARN Scheduler有多種實現,每一種對應了不同的調度策略,如常見的FIFO Scheduler,Fair Scheduler、Capacity Scheduler等,它們都是可插拔的。資源調度器是YARN中最核心的組件之一,並且是可插拔的,用戶可以根據它的一整套接口,編寫自己的Scheduler,實現自己所需的調度邏輯。這裏的調度邏輯指的是第一次調度邏輯,而不關注第二層調度策略,它由計算框架自己控制。
一、YARN的資源管理機制
YARN的資源是以資源池的形式組織的。每個資源池對應一個隊列(queue),用戶在提交作業的時候以
-queue
參數指定要提交的隊列,就可以使用隊列背後對應的資源池。在第二代Hadoop中,隊列是以樹形的方式組織起來的,如圖下圖所示,YARN中最大的資源池就是整個集羣的資源,對應隊列爲根隊列root,如果用戶不指定隊列,那麼默認提交的就是該隊列。
Yarn中的隊列(queue)機制。在yarn中,有層級隊列組織方法,它們構成一個樹結構,且根隊列叫做root。所有的應用都運行在葉子隊列中(即樹結構中的非葉子節點只是邏輯概念,本身並不能運行應用)。對於任何一個應用,我們都可以顯式地指定它屬於的隊列,也可以不指定從而使用username或者default隊列。用戶只能將自己的作業提交給子隊列,每個隊列都可繼續向其中添加子隊列。子隊列的資源使用的都是父隊列的資源。
Yarn中有三種調度器可用:FIFO Scheduler(FIFO 調度器) ,Capacity Scheduler(容量調度) 和 Fair Scheduler(公平調度)。
二、FIFO Scheduler
FIFO Scheduler
把應用按提交的順序排成一個隊列,這是一個先進先出隊列,在進行資源分配的時候,先給隊列中最頭上的應用進行分配資源,待最頭上的應用需求滿足後再給下一個分配,以此類推。
FIFO Scheduler
是最簡單也是最容易理解的調度器,也不需要任何配置,但它並不適用於共享集羣。大的應用可能會佔用所有集羣資源,這就導致其它應用被阻塞。在共享集羣中,更適合採用Capacity Scheduler
或Fair Scheduler
,這兩個調度器都允許大任務和小任務在提交的同時獲得一定的系統資源。從下圖可以看出在 FIFO 調度器中,小任務會被大任務阻塞:
三、Capacity Scheduler
Capacity Scheduler是 Yahoo! 開發的調度器,對多用戶支持較好。
啓用 Capacity Scheduler:
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler
</value>
<description>The class to use as the resource scheduler.</description>
</property>
Capacity 調度器允許多個組織共享整個集羣,每個組織可以獲得集羣的一部分計算能力。通過爲每個組織分配專門的隊列,然後再爲每個隊列分配一定的集羣資源,這樣整個集羣就可以通過設置多個隊列的方式給多個組織提供服務了。除此之外,隊列內部又可以垂直劃分,這樣一個組織內部的多個成員就可以共享這個隊列資源了,在一個隊列內部,資源的調度是採用的是先進先出(FIFO)策略。
如上圖所示,對於Capacity調度器,有一個專門的隊列用來運行小任務,但是爲小任務專門設置一個隊列會預先佔用一定的集羣資源,這就導致大任務的執行時間會落後於使用FIFO調度器時的時間。
上圖所以,單個作業使用的資源不會超過其隊列容量。然而如果這個隊列中運行多個job,如果這個隊列的資源夠用,那麼就分配給這些job,如果這個隊列的資源不夠用了呢?其實 Capacity 調度器仍可能分配額外的資源給這個隊列,這就是彈性隊列”(queue elasticity)
的概念。
在正常的操作中,Capacity調度器不會通過強制終止來搶佔
Container,因此如果一個隊列一開始資源夠用,然後隨着需求增長,資源開始不夠用時,那麼這個隊列就只能等着其它隊列釋放容器資源。緩解這種情況的方法是,爲隊列設置一個最大容量限制,以免這個隊列過多的佔用空閒資源,導致其它隊列無法使用這些空閒資源,當然這樣做是犧牲隊列彈性爲代價的,因此需要在不斷嘗試和失敗中找到一個合理的值。
如果屬性
yarn.scheduler.capacity.<queue-path>.user-limit-factor
設置爲大於 1(默認值),那麼一個作業可以使用超過其隊列容量的資源。
假設一個隊列的層次結構如下:
容量調度器配置文件爲 capacity-scheduler xml
。在root隊列下定義兩個隊列:prod和dev, 分別佔40%和60%的 容量。需要注意的是,對特定隊列進行配置時,是通過以下形式的配置屬性 yarn.scheduler.capacity.<queue-path>.<sub-property>
進行設置的, 其 中, <queue-path>
表示隊列的層次路徑(用圓點隔開) , 例如 root.prod。
<?xml version="1.0"?>
<configuration>
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>prod,dev</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.queues</name>
<value>eng,science</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.capacity</name>
<value>40</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.capacity</name>
<value>60</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.maximum-capacity</name>
<value>75</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.eng.capacity</name>
<value>50</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.science.capacity</name>
<value>50</value>
</property>
</configuration>
正如你所見的,prod 和 dev 分別默認分配 root (也就是總資源)中 40% 和 60% 的資源,而 dev 的兩個子隊列則分配當前 dev 分配到的資源的 50% 和 50% 的資源。
上面說默認分配,是因爲 YARN 爲了合理利用資源,有一個叫做 queue elasticity 的功能:即在大多時候,一個隊列中的 application 是不能使用超過隊列容量的資源的。但是如果隊列中應用個數超過一個並且其他隊列擁有空閒資源時,Capacity Scheduler 會分配給它這些空閒的資源,哪怕使得總資源超過該隊列的容量。
同時在 Capacity Scheduler 中,沒有搶佔資源
這一說。因此當某個隊列需要更多的資源時(比如本身屬於它的資源被其他隊列暫時借走),則必須等到其他隊列自己還回資源。爲了避免某個隊列佔用了全部資源從而導致其他隊列完全無法運行應用的情況出現,我們可以在配置文件中設置 maximum-capacity
的值,例如上面的列子,dev 對應的值是 75,這就意味着 dev 隊列最多使用 75% 的 root 資源,從而最少給 prod 留下 25% 的資源,從而避免剛剛所說的情況出現。
隊列放置
將應用放置在哪個隊列中, 取決於應用本身。例如, 在Map Reduce中,可以通過設置屬性map reduce.job.queue name
來指定要用的隊列。如果隊列不存在, 則在提交時會發送錯誤。如果不指定隊列,那麼應用將被放在一個名爲 “default”的默認隊列中。
對於容量調度器,隊列名應該是隊列層次名的最後一部分(即葉子結點),完整的隊列層次名是不會被識別的。例如, 對於上述配置範例, prod 和 eng 是合法的隊列名,但 root.dev.eng 和 dev.eng 作爲隊列名是無效的。
sudo -u hdfs hadoop jar hadoop-examples.jar pi -Dmapreduce.job.queuename=eng 10 1
四、Fair Scheduler
公平調度器旨在爲所有運行的應用公平分配資源
。下圖展示了同一個隊列中的應用是如何實現資源公平共享的。然而公平共享實際也可以在多個隊列間工作,後續會對此進行分析。
接下來解釋資源是如何在隊列之間公平共享的。想象兩個用戶 A 和 B,分別擁有自己的隊列(參見下圖)。啓動一個作業,在B沒有需求時 A 會分配到全部可用 資源;當 A 的作業仍在運行時 B 啓動一個作業,一段時間後,按照我們先前看到的方式,每個作業都用到了一半的集羣資源。這時,如果 B 啓動第二個作業且其他作業仍在運行,那麼第二個作業將和B的其他作業(這裏是第一個)共享資源,因此 B 的每個作業將佔用四分之一的集羣資源,而 A 仍繼續佔用一半的集羣資源。最終的結果就是資源在用戶之間實現了公平共享。
1、啓用 Fair Scheduler
公平調度器的使用由屬性 yarn.resourcemanager.scheduler.class
的設置所決定。默認是使用 Capacity Scheduler (儘管在一些Hadoop分佈式項目, 如CDH中是默認使用 Fair Scheduler),如果要使用 Fari Scheduler,需要修改 yarn-site.xml 文件。
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler
</value>
</property>
CM中啓用
2、隊列配置
通過一個名爲 fair-scheduler.xml
的分配文件對 Fari Scheduler 進行配置, 該文件位於類路徑下。(可以通過設置屬性yarn.scheduler.fair.allocation.file
來修改文件名) 。當沒有該分配文件時,Fair Scheduler 的工作策略同先前所描述的一樣:每個應用放置在一個以用戶名命名的隊列中,隊列是在用戶提交第一個應用時動態創建的。
CM 配置 fair-scheduler.xml 文件:
通過分配文件可以爲每個隊列進行配置。這樣可以對 Fair Scheduler 支持的層次隊列進行配置。例如,可以像爲Capacity Scheduler 所做的那樣,使用分配文件定義 prod 和 dev。
<?xml version="1.0"?>
<allocations>
<defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy>
<queue name="prod">
<weight>40</weight>
<schedulingPolicy>fifo</schedulingPolicy>
</queue>
<queue name="dev">
<weight>60</weight>
<queue name="eng" />
<queue name="science" />
</queue>
<queuePlacementPolicy>
<rule name="specified" create="false" />
<rule name="primaryGroup" create="false" />
<rule name="default" queue="dev.eng" />
</queuePlacementPolicy>
</allocations>
隊列的層次使用嵌套 queue 元素來定義。所有的隊列都是 root 隊列的孩子, 即使實際上並沒有嵌套進 root queue 元素裏。這裏把 dev 隊列又劃分成 eng 和 science 兩個隊列。
隊列中有權重元素,用於公平共享計算。在這個例子中,當集羣資源按照 40:60 的比例分配給 prod 和 dev 時, 集羣分配被認爲是公平的。eng 和 science 隊列沒有指定權重,因此它們會被平均分配。權重並不是百分百,例子中是爲了簡單起見使用了相加之和爲100的兩個數。也可以爲 prod 和 dev 隊列分別指定 2 和 3 的權重,在效果上是一樣的。
儘管上述的分配文件中沒有展示,每個隊列仍可配置最大和最小資源數量,及最大可運行的應用的數量(更多細節可以訪問http://bit_l/fair_scheduar)。最小資源數量不是一個硬性的限制,但是調度器常用它對資源分配進行優先排序。如果兩個隊列的資源都低於它們的公平共享額度,那麼遠低於最小資源數量的那個隊列優先被分配 資源。最小資源數量也會用於接下來將介紹的搶佔行爲。
最小資源配置:
在每個資源池中,允許配置該資源池的最小資源,這是爲了防止把空閒資源共享出去還未回收的時候,該資源池有任務需要運行時候的資源保證。
比如,資源池businessA中配置了最小資源爲(5vCPU,5GB),那麼即使沒有任務運行,Yarn也會爲資源池businessA預留出最小資源,一旦有任務需要運行,而集羣中已經沒有其他空閒資源的時候,這個最小資源也可以保證資源池businessA中的任務可以先運行起來,隨後再從集羣中獲取資源。
CM中配置
當設置權重時,記住要考慮默認隊列和動態創建的隊列(例如以用戶名命名的隊列)。雖然沒有在分配文件中爲它們指定權重,但它們仍有值爲1的權重。
每個隊列可以有不同的調度策略。隊列的默認調度策略可以通過頂層元素 `default Queue Scheduling Policy 進行設置, 如果省略, 默認使用公平調度。儘管名稱是“公平”, 公平調度器也支持隊列級別的FIFO(fifo) 策略, 以及 Dominant Resource Fairness(drf) 策略, 本章稍後會對此策略進行解釋。
隊列的調度策略可以被該隊列的 scheduling Policy
元素指定的策略覆蓋。在上述例子中,由於我們希望每個生產性作業能夠順序運行且在最短可能的時間內結束, 所以 prod 隊列使用了 FIFO 調度策略。值得注意的是, 在prod 和 dev 隊列之間、eng 和 science 隊列之間及內部劃分資源仍然使用了公平調度。
CM中配置:
3、隊列放置
公平調度器使用一個基於規則的系統來確定應用應該放到哪個隊列。queuePlacementPolicy元素包含了一個規則列表, 每條規則會被依次嘗試直到匹配成功。第一條規則, specified, 表示把應用放進所指明的隊列中, 如果沒有指明,或如果所指明的隊列不存在,則規則不匹配,繼續嘗試下一條規則。primary Group規則會試着把應用放在以用戶的主Unix組名命名的隊列中,如果沒有這樣的隊列, 則繼續嘗試下一條規則而不是創建隊列。Default規則是 一條兜底規則, 當前述規則都不匹配時, 將啓用該條規則, 把應用放進 dev.eng 隊列中。
當然, 可以完全省略queue Placement Policy元素, 此時隊列放置默認遵從如下規則:
<queue Placement Policy>
<rule name="specified"/>
<rule name="user"/>
</queue Placement Policy>
換而言之,除非明確定義隊列,否則必要時會以用戶名爲隊列名創建隊列。
另一個簡單的隊列放置策略是, 將所有的應用放進同一個隊列(default) 中。這樣可以在應用之間公平共享資源,而不是在用戶之間共享。策略定義等價於以下規則:
<queue Placement Policy>
<rule name="default"/>
</queue Placement Policy>
不使用分配文件也可以設置以上策略,通過將屬性 yarn.scheduler.fair.user-as-default-queue設爲false, 應用就會被放入default隊列, 而不是各個用戶的隊列。另外,將屬性 yarn.scheduler.fair allow-undeclared-pools 設置爲false, 用戶便不能隨意創建隊列了。
4、搶佔
在一個繁忙的集羣中,當作業被提交給一個空隊列時,作業不會立刻啓動,直到集羣上已經運行的作業釋放了資源。爲了使作業從提交到執行所需的時間可預測,公平調度器支持“搶佔” (preemption)
功能。
所謂搶佔
,就是允許調度器終止那些佔用資源超過了其公平共享份額的隊列的容器,這些容器資源釋放後可以分配給資源數量低於應得份額的隊列。注意,搶佔會降低整個集羣的效率, 因爲被終止的 containers 需要重新執行。
通過將 yarn.scheduler.fair.preemption
設置爲true, 可以全面啓用搶佔功能。有兩個相關的搶佔超時設置:一個用於最小共享(minimum share preemption timeout) , 另一個用於公平共享(fair share preemption timeout) , 兩者設定時間均爲秒級。默認情況下,兩個超時參數均不設置。所以爲了允許搶佔容器,需要至少設置其中一個超時參數。
如果隊列在 minium share preemption timeout
指定的時間內未獲得被承諾的最小共享資源,調度器就會搶佔其他容器。 可以通過分配文件中的頂層元素 default Min Share Preemption Timeout
爲所有隊列設置默認的超時時間, 還可以通過設置每個隊列的 min Share Preemption Timeout
元素來爲單個隊列指定超時時間。
類似, 如果隊列在fair share preemption timeout
指定的時間內獲得的資源仍然低於其公平共享份額的一半,那麼調度器就會搶佔其他容器。可以通過分配文件中的頂層元素default Fair Share Preemption Timeout
爲所有隊列設置默認的超時時間, 還可以通過設置每個隊列的fair Share Preemption Timeout
元素來爲單個隊列指定超時時間。通過設置 default Fair Share Preemption Threshold
和 fair Share Preemption Threshold
(針對每個隊列) 可以修改超時閾值, 默認值是 0.5。
CM配置:
5、延遲調度
所有的YARN調度器都試圖以本地請求
爲重。在一個繁忙的集羣上, 如果一個應用請求某個節點,那麼極有可能此時有其他容器正在該節點上運行。顯而易見的處理是,立刻放寬本地性需求,在同一機架中分配一個容器。然而,通過實踐發現,此時如果等待一小段時間(不超過幾秒),能夠戲劇性的增加在所請求的節點上分配到一個容器的機會,從而可以提高集羣的效率。這個特性稱之爲延遲調度 (delay scheduling)
。容量調度器和公平調度器都支持延遲調度。
YARN 中的每個節點管理器週期性的(默認每秒一次)向資源管理器發送心跳請求。心跳中攜帶了節點管理器中正運行的容器、新容器可用的資源等信息,這樣對於一個計劃運行一個容器的應用而言,每個心跳就是一個潛在的調度機會 (scheduling opportunity) 。
當使用延遲調度時,調度器不會簡單的使用它收到的第一個調度機會,而是等待設定的最大數目的調度機會發生,然後才放鬆本地性限制並接收下一個調度機會。
對於容量調度器,可以通過設置 yarn.scheduler.capacity.node-locality-delay
來配置延遲調度。設置爲正整數,表示調度器在放鬆節點限制、改爲匹配同一機架上的其他節點前,準備錯過的調度機會的數量。
公平調度器也使用調度機會的數量來決定延遲時間,儘管是使用集羣規模的比例來表示這個值。例如將yarn.scheduler.fair.locality.threshold.node
設置爲0.5,表示調度器在接受同一機架中的其他節點之間,將一直等待直到集羣中的一半節點都已經給過調度機會。還有個相關的屬性 yarn.scheduler.fair.locality.threshold.rack
, 表示在接受另一個機架替代所申請的機架之前需要等待的時長閾值。
6、主導資源公平性
對於單一類型資源,如內存的調度,容量或公平性的概念很容易確定。例如兩個用戶正在運行應用,可以通過度量每個應用使用的內存來比較兩個應用。然而,當有多種資源類型需要調度時,事情就會變得複雜。例如,如果一個用戶的應用對CPU的需求量很大, 但對內存的需求量很少;而另一個用戶需要很少的CPU, 但對內存需求量大,那麼如何比較這兩個應用呢?
YARN中調度器解決這個問題的思路是, 觀察每個用戶的主導資源, 並將其作爲對集羣資源使用的一個度量。這個方法稱爲主導資源公平性”(Dominant Resource Fairness, DRF)
, 這個思想用一個簡單的例子就可以很好的給予解釋。
想象一個總共有100個CPU和10TB的集羣。應用A請求的每份容器資源爲2個CPU和300GB內存, 應用B請求的每份容器資源爲6個CPU和100GB內存。A請求的資源在集羣資源中佔比分別爲2%和3%, 由於內存佔比(3%) 大於CPU佔比(2%),所以內存是A的主導資源。B請求的資源在集羣資源中佔比分別爲6%和1%, 所以CPU是B的主導資源。由於B申請的資源是A的兩倍(6%vs 3%) , 所以在公平調度下,B將分到一半的容器數。
默認情況下不用DRF, 因此在資源計算期間, 只需要考慮內存, 不必考慮CPU。對容量調度器進行配置後, 可以使用DRF, 將capacity-scheduler.xml
文件中的 org.apache.had oop.yarn.util.resource.Dominant Resource Calculator
設爲 yarn.scheduler.capacity.resource-calculator
即可。
公平調度器若要使用DRF, 通過將分配文件中的頂層元素 defaultQueueSchedulingPolicy 設爲drf即可。
7、動態更新資源配置
通過編輯分配文件,可以在運行時修改最小份額,限制,權重,搶佔超時和隊列調度策略。調度程序將在看到它被修改後10-15秒重新加載此文件。
Fair Scheduler除了需要在yarn-site.xml文件中啓用和配置之外,還需要一個XML文件來配置資源池以及配額,而該XML中每個資源池的配額可以動態更新,之後使用命令:yarn rmadmin –refreshQueues
來使得其生效即可,不用重啓Yarn集羣。
每次在ResourceManager上新增用戶或者調整資源池配額後,需要執行下面的命令刷新使其生效:
yarn rmadmin -refreshQueues
yarn rmadmin -refreshUserToGroupsMappings
需要注意的是:動態更新只支持修改資源池配額,如果是新增或減少資源池,則需要重啓Yarn集羣。
CM中操作:
8、在隊列之間移動應用程序
Fair Scheduler支持將正在運行的應用程序移動到不同的隊列。這對於將重要應用程序移動到更高優先級隊列或將不重要的應用程序移動到較低優先級隊列非常有用。可以通過運行紗線應用程序移動應用程序
movetoqueue appID -queue targetQueueName
當應用程序移動到隊列時,其現有分配將使用新隊列的分配而不是舊分配來計算,以確定公平性。如果將應用程序的資源添加到該隊列將違反其maxRunningApps或maxResources約束,則嘗試將應用程序移動到隊列將失敗。
9、傾倒公平調度程序狀態
Fair Scheduler能夠定期轉儲其狀態。默認情況下禁用它。管理員可以通過將org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler.statedump
日誌記錄級別設置爲DEBUG 來啓用它。
默認情況下,Fair Scheduler日誌會轉到Resource Manager日誌文件。Fair Scheduler狀態轉儲可能會生成大量日誌數據。取消註釋log4j.properties中的“公平調度程序狀態轉儲”部分,以將狀態轉儲到單獨的文件中。
五、Capacity 與 Fair Scheduler比較
fair Scheduler 跟 Capacity Scheduler 在以隊列爲單位劃分資源,每個隊列可設定一定比例的資源最大最小值及用戶資源限定方面是相同的,fair主要在以下方面比Capacity 具有優勢:
-
資源公平共享。在每個隊列中,fair可選擇按照FIFO、Fair或 DRF策略爲應用程序分配資源。
-
支持資源搶佔。當某個隊列有剩餘資源時,調度器會將這些資源共享給其他隊列,而當該隊列中有新的應用程序提交時,調度器要爲它回收資源。爲了儘可能降低不必要的計算浪費,調度器採用了
先等待再強制回收
的策略,即如果等待一段時間後尚有未歸還的資源,則會進行資源搶佔。 -
負載均衡。fair 提供了一個基於任務數目的負載均衡機制,該機制儘可能將系統中的任務均勻分配到各個節點上。此外,用戶也可以根據自己需求設計負載均衡機制。
-
調度策略配置靈活。fair允許管理員爲每個隊列單獨設置調度策略(當前支持fifo,fair,DRF三種)。
-
提高小應用程序響應時間。由於採用了最大最小公平算法,小作業可以快速獲取資源並運行完成。
Capacity Scheduler | Fair Scheduler | |
---|---|---|
動態加載配置文件 | 是 | 是 |
負載均衡 | 否 | 是 |
彈性隊列 | 是 | 是 |
搶佔 | 否 | 是 |
隊列間資源分配方式 | 資源使用率低者優先 | Fair、FIFO 或 DRF |
隊列內部資源分配方式 | FIFO 或 DRF | Fair、FIFO 或 DRF |
Container請求資源粒度 | 最小資源量的整數倍, 比如Container請求量是1.5GB,最小資源量是1GB,則Container請求自動被歸一化爲2GB | 有專門的內存規整化參數控制,粒度更小, Container請求量是1.5GB,規整化值爲128MB, 則Container請求不變 |
本文分享自微信公衆號 - 大數據AI(songxt1990)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。