本文系微博運維數據平臺(DIP)在實時計算平臺的研發過程當中集羣資源管理方面的一些經驗總結和運用,主要關注如下幾個問題:
- 異構資源如何整合?
- 實時計算應用之間的物理資源如何隔離?
- 集羣資源利用率如何提升?
- 集羣運維成本如何下降?
1. 背景
這是咱們初期的一個實時計算架構,大體劃分爲三個部分:
(1)日誌收集;
使用Rsynlog、Flume、Scribe匯聚各個業務方發送過來的日誌數據;若是條件容許,業務方也能夠直接將數據寫入Kafka。
(2)日誌傳輸;
使用Kafka做爲日誌收集組件與實時應用之間的一個高速傳輸通道,實際也是一個日誌緩衝區。
(3)實時計算平臺;
實時計算平臺根據使用場景的不一樣包含有如下兩種類型的應用:
(1)自助實時應用:依託於Spark Streaming、Spark SQL構建的通用實時處理模塊,旨在簡化用戶開發、部署、運維實時應用的相關工做,大多數時候用戶經過咱們提供的Web頁面便可完成實時應用的建立;
(2)第三方應用託管:應用計算處理邏輯與自身業務結合比較緊密,沒法很好地抽象出可複用的模塊,一般由業務方使用Spark Streaming自行開發完成,而後經過DIP平臺統一部署;
這兩種類型的實時應用均以Spark Streaming的形式運行於使用Hadoop Yarn FairScheduler做爲資源管理器的集羣之上。
本文僅介紹實時計算平臺中的集羣資源管理方案,關於日誌收集、日誌傳輸的內容可分別參考如下兩篇文章:
2. Hadoop Yarn集羣資源管理
在咱們Hadoop Yarn集羣中,集羣資源管理器使用的是公平調度器(FairScheduler),以業務方爲單位進行資源劃分,爲每一個業務方分配一個單獨的隊列,這個隊列關聯着必定的資源(CPU、MEM)。爲了保障各個業務方的資源使用,咱們將各個隊列的資源下限值(minResources)和資源上限值(maxResources)設置爲相同,並禁用隊列(業務)之間的資源搶佔。上文中說起的自助實時應用和第三方應用提交時,均須要提交至業務方各自的隊列中去運行。
既然已經爲各個業務方分配隊列,且指定資源量,每一個業務方就至關於在咱們的Hadoop Yarn集羣中擁有必定數量的服務器,集羣運行時看起來是這個樣子的:
簡單起見,咱們以隊列名稱代替業務方名稱,每個Container的資源爲1 core、1g mem,由此能夠得出以下信息:
(1)Hadoop Yarn 集羣的總資源量:42cores,42g mem;
(2)三個業務方,即:queueA、queueB、queueC;
(3)queueA佔有6臺服務器資源:18 cores,18g mem,queueB、queueC各佔有4臺服務器資源:12 cores,12g mem;
這裏咱們須要注意,Hadoop Yarn FairScheduler只能控制各個隊列的資源使用量,這是一種邏輯資源上的控制,並不能實際控制這些資源(Container)被分配在哪些服務器上運行。以queueA爲例,queueA從資源使用分配的角度來講,它享有6臺服務器的資源,但並非說它會獨佔集羣中的某6臺服務器。
集羣運行時實際是這個樣子的:
queueA、queueB、queueC在各自資源使用量的範圍內「邏輯共享」集羣資源。這些資源的表現形式能夠理解爲就是Container(每一個Container運行時須要分配必定的資源量,如1 core,1g mem),各個業務方的全部Container是混合運行在集羣內的各個服務器上的。
Hadoop Yarn FairScheduelr的資源管理方式在咱們的離線計算場景裏運行良好,但在實時計算場景裏卻遇到了不少問題。
(1)異構資源如何整合?
異構資源包括兩個方面:服務器機型不一致、服務器角色不一致。
a. 服務器機型不一致
不少業務方在接入實時應用時沒法提供與咱們現有集羣同等配置的服務器,大多數性能偏低。這些業務方一般是出於開發、計算、運維效率方面的考慮,從過去的計算引擎(如:nodejs-statsd)遷移至Spark Streaming,機型的選取是根據當時的狀況來衡量的。
Hadoop已經爲咱們考慮到機型不一致的狀況,它的解決方式是每一臺服務器(計算節點,Hadoop NodeManager)的資源使用量能夠經過配置文件(yarn-site.xml,yarn.nodemanager.resource.cpu-vcores、yarn.nodemanager.resource.memory-mb)進行設置。咱們能夠根據業務方服務器的實際狀況合理地設置這臺服務器可以使用的資源量,再根據業務方服務器的資源量總和爲其劃分相應的隊列資源。
這個業務方的問題解決了,可是其它業務方會有這樣的疑問:「咱們的實時業務很重要,申請的所有是高性能的服務器,若是咱們的應用被調度到這些性能比較差的服務器中運行,計算性能會不會有損耗?」。
業務方的這種擔心確實值得咱們考慮,Spark Streaming Application是7 * 24小時不間斷運行的,若是這個應用的Containers被調度至性能迥異的Hadoop NodeManager服務器中去執行,確實沒法保證性能較差的服務器上的Container不會拖慢整個實時應用的執行進度。
b. 服務器角色不一致
新業務方的接入不只僅須要擴容集羣的計算節點(Hadoop NodeManager),同時也須要擴容Kafka Brokers節點。多數狀況下業務方沒法提供這樣的冗餘機器,這時咱們設想使用「混合部署」的技術方案,即計算節點與Kafka Brokers節點部署在同一臺服務器中,可是這會使得(1)中的「計算性能會不會有損耗」的問題愈演愈烈。
注:實時計算場景下,以Spark Streaming Application爲例,Hadoop NodeManager Container屬於CPU密集型應用,Kafka Brokers屬於IO密集型應用,若是是獨立業務,二者混合部署的效果仍是不錯的,DIP平臺在這方面已經有很好地實踐。
(2)實時計算應用之間的物理資源如何隔離?
物理資源包括四個方面:CPU、MEM、DISK、NETWORK。若是一臺Hadoop NodeManager服務器中運行着不一樣業務方的Spark Streaming Application Containers,那麼這些Containers之間就有可能存在互相影響的狀況。
目前Hadoop Yarn僅僅支持CPU、MEM的資源管理,同時提供基於線程監控的內存使用機制和基於Cgroup的資源隔離機制,但對於DISK、NETWORK則缺少相應的資源隔離機制,可能會引起Container之間的資源競爭致使Container執行效率低下,進而影響整個Spark Streaming Application的狀況。
(3)集羣資源利用率如何提升?
業務方提交Spark Streaming Application時能夠經過如下三個參數指定實時應用運行期間的資源配額,
num-executors:Spark Streaming Application運行時須要申請多少個Containers;
executor-cores:Spark Streaming Application的每個Container須要申請多少 cores;
executor-memory:Spark Streaming Application的每個Container須要申請多少mem;
這三個參數值的選取須要考慮兩個因素:
業務方隊列資源冗餘狀況:DIP平臺爲業務方劃分的隊列中是否有足夠的冗餘資源用於新的Spark Streaming Application接入;
Spark Streaming Application資源需求狀況:若是爲該Application分配較多的資源,會致使資源浪費;若是分配較少的資源,會致使計算資源不足,進而影響業務;
做爲集羣資源的管理者,咱們還須要考慮更多的問題:
a. 集羣CPU、MEM資源分配不均衡;
集羣CPU資源被耗盡,MEM資源有冗餘;或者集羣MEM資源被耗盡,CPU資源有冗餘;
b. 集羣資源碎片化的問題;
Hadoop NodeManager CPU/MEM資源均有冗餘,卻沒法分配給Spark Streaming Application使用,這是由於該Hadoop NodeManager CPU/MEM的資源剩餘量不足以知足任何一個業務方的Spark Streaming Application Container的資源需求量。若是出現這種現象的Hadoop NodeManager數目較多,會致使集羣大量資源處於沒法使用(浪費)的狀態,而Spark Streaming Application也會出現沒法申請Container或申請不到足夠數量的Container的狀況。
這兩種問題引起的現象:業務方隊列中的資源還有冗餘,可是提交的應用卻遲遲分配不到資源。
(4)集羣運維成本如何下降?
集羣的運維成本主要來自於第三方實時應用託管,不一樣的應用對系統環境有不一樣的依賴,如:Spark Streaming使用Python進行開發,它運行時須要不少依賴的庫,這些庫須要在整個集羣的全部Hadoop NodeManager機器上面進行安裝,各類版本衝突問題給咱們帶來了很大的運維壓力,同時也是對咱們集羣環境的一種「污染」。
3. 彈性集羣資源管理
異構資源、物理隔離、資源利用率、運維成本引起了咱們對Hadoop Yarn集羣資源管理的思考,直接想到的方案是爲各個業務方構建本身的集羣,以下圖所示:
根據咱們以往的經驗,這種方式運維監控的成本極高,咱們很快就否認了這種方案。
既然沒法構建多個集羣,那麼是否能夠在一個集羣內部劃分多個「資源池」?每一個資源池關聯着若干臺機器,爲每個業務方分配一個或多個資源池,業務方提交的應用僅在分配的資源池內運行,即僅在資源池關聯的那些服務器中運行。
咱們以「資源池」的方式來討論一下是否能夠解決上述提到的四個問題。
(1)異構資源;
咱們將機型相同或相近的服務器組成一個或多個資源池,資源池的大小根據具體的業務需求而定。通過這樣的處理以後,咱們能夠認爲任何一個資源池內的服務器的計算性能都是等同的。業務方提交的應用只會在某一個資源池內運行,這個資源池內的多個計算節點性能相同,服務器機型不一致可能帶來的性能損耗問題獲得解決。
若是業務方須要「混合部署」,能夠創建一個專用的資源池,將這些須要混合部署多個服務(這裏特指Kafka Broker、Hadoop NodeManager)的服務器關聯至該資源池。一般狀況下,這種「混合部署」模式的資源池是特定應用專用的,這個資源池內的服務器角色是一致的,服務器角色不一致可能帶來的性能損耗問題獲得解決。
(2)物理資源隔離;
每一個業務方僅能使用本身資源池內的物理資源,各個業務方之間的物理資源競爭問題獲得解決。
對於特定的業務方而言,本身的資源池內可能會運行着多個實時應用,這些實時應用之間依然可能存在着物理資源競爭的問題,這方面咱們是這麼認爲的:
a. 由於資源池是業務方獨享的,業務方在開發部署應用時應充分考慮應用之間的物理資源競爭問題,也就是說,將同一個業務方多個應用之間的物理資源資源問題交由業務方本身負責;
b. 若是有特殊狀況,咱們能夠爲同一個業務方劃分多個資源池,甚至一個應用一個資源池;
這樣物理資源隔離問題獲得解決。
(3)資源利用率;
「一個集羣、多個隊列」的模式下,業務方是不須要考慮資源利用率的問題的,只須要根據隊列分配的資源大小和應用須要的資源來部署應用就能夠了。從業務方的角度看,只要資源的使用處於隊列分配資源的區間範圍內就是沒有問題的,這種角度其實是比較「自私」的,沒有考慮本身的行爲對其它業務方的影響,資源分配不平衡、資源碎片化的問題本質即來源於此。
「一個集羣、多個資源池」的模式下,一個資源池以內的資源是徹底被獨享的,業務方必須在本身資源池的範圍內主動充分考慮資源分配不平衡、資源碎片化的問題。每一個業務方的這種主動參與會帶來集羣總體資源利用率的大幅提升。
假設咱們有一臺24 cores、128g mem的服務器,若是選用它做爲Hadoop NodeManager,通常狀況下咱們會這麼設置:22 cores、120g mem,以使得這臺服務器的物理資源可以獲得充分的利用,這也是業界比較常見的作法。集羣運維過程當中,咱們發現這樣的現象(以CPU爲例):「22 cores已經所有被分配,可是這臺服務器的CPU利用率卻只有60%,並且不是個別現象」,這是對集羣資源的巨大浪費。「一個集羣、多個隊列」的模式下,咱們的集羣配置須要充分考慮到各個業務方的需求,只能使用上述的通用作法;「一個集羣、多個資源池」的模式下,咱們則能夠根據應用的實際狀況爲某資源池內的Hadoop NodeManager做出「物別」地設置,如「44 cores、120g mem」,資源池可提供的資源「變多」,集羣的總體利用率也會有所提高。
(4)運維成本;
運維成本主要來自於業務方的特殊需求,這種特殊需求的實施須要涉及到整個集羣,包括後續加入的節點,人力成本和維護成本都很高。針對這種狀況,咱們對Hadoop NodeManager實現「容器化」(Docker)部署,若是業務方有特殊需求,則能夠在咱們提供的Docker基礎鏡像的基礎之上做出相應的變動,不會直接「污染」系統環境,容易回退,且變動後的鏡像只會部署在業務方特定資源池內的服務器上,不會影響到其它業務方,這種方式使得運維成本大幅降低。
綜上所述,「一個集羣、多個資源池」的模式可以解決異構資源、物理資源隔離、資源利用率、運維成本的問題。
4. 實現
Hadoop Yarn FairScheduler只支持應用之間的公平調度,咱們須要對其進行相應的擴展,使之支持「資源池」的模式:
(1)資源池可配置,經過資源池名稱可關聯若干Hadoop NodeManager節點(使用主機名稱(Hostname)表示),資源池配置在公平調度器的配置文件fair-scheduler.xml中便可,以下圖所示:
多個資源池之間的Hadoop NodeManager節點不能有重合,即每個Hadoop NodeManager節點僅能屬於一個資源池;
(2)資源池與隊列之間的對應關係依賴「名稱前綴」來識別;以「pool1」、「pool2」爲例,若是隊列名稱以「pool1」爲前綴,那麼該隊列中的全部應用將運行於資源池pool1中;若是隊列名稱以「pool2」爲前綴,該隊列中的全部應用將運行於資源池pool2中;
核心思想
Hadoop Yarn FairScheduler每次收到Hadoop NodeManager的「NODE_UPDATE」事件時,就會使用公平調度算法爲每一個處於運行狀態的應用分配Containers。咱們須要在公平調度的基礎之上添加資源池的處理邏輯,如爲某個應用分配Containers時:
(1)獲取「NODE_UPDATE」事件的Hadoop NodeManager的主機名稱;
(2)獲取應用的提交隊列名稱,並根據隊列名稱獲取對應的資源池;若是能夠找到對應的資源池,則繼續(3);若是未找到對應的資源池,則結束分配過程,繼續處理下一個應用;
(3)若是該資源池的節點列表中包含(1)中的主機名稱,則繼續使用公平調度算法完成分配;不然,結束分配過程,繼續處理下一個應用;
實現
Hadoop Yarn FairScheduler原有處理邏輯(Hadoop 2.5.0-cdh5.3.2 org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSLeafQueue.assignContainer):
在此基礎之上添加資源池的處理邏輯:
(1)獲取「NODE_UPDATE」的Hadoop NodeManager的主機名稱nodeHostName;
(2)獲取資源池信息groups,其中「key」表示資源池名稱,「value」表示資源池關聯着的Hadoop NodeManager HostNames;
(3)獲取待分配應用的隊列名稱queueName;
(4)尋找隊列名稱queueName對應資源池中的主機名稱列表nodes;
(5)若是nodes包含nodeHostName,則繼續分配過程;不然結束分配過程,繼續下一個應用;
也就是說,Hadoop Yarn FairScheduler的原有邏輯,只要收到集羣中任何一個Hadoop NodeManager的「NODE_UPDATE」事件,就會根據公平調度算法完成Containers的分配過程;添加資源池的處理邏輯以後,提交至隊列queueName中的全部應用,只有收到來自對應資源池中的Hadoop NodeManager的「NODE_UPDATE」事件時,纔會根據公平高度算法完成Containers的分配過程。
咱們還須要處理這兩種異常狀況:
(1)應用提交至某隊列,該隊列沒有配置對應的資源池;
(1)應用提交至某隊列,該隊列對應資源池的主機名稱列表爲空;
這兩種狀況咱們的處理方式相同,終止該應用的執行,以下:
org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler.addApplication
此外,還涉及到公平調度器fair-scheduler.xml的源碼擴展,以下:
org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.AllocationFileLoaderService.reloadAllocations
實際就是從公平調度器配置文件fair-scheduler.xml中解析出資源池的信息,存儲變量groups中,詳細過程再也不贅述。