大資料系列4:Yarn以及MapReduce 2

lillcol發表於2021-01-29

系列文章:
大資料系列:一文初識Hdfs
大資料系列2:Hdfs的讀寫操作
大資料謝列3:Hdfs的HA實現

通過前文,我們對Hdfs的已經有了一定的瞭解,本文將繼續之前的內容,介紹YarnYarnMapReduce 2的應用


MapReduce1 作業流程

在介紹Yarn之前,我們先介紹一下Mapreduce1作業流程。

有了這個基礎,再去看看採用Yarn以後的MapReduce2到底有啥優勢也許會有更好的理解。

首先先介紹一下相關的幾個實體:

  1. Client:負責提交 MapReduce 作業
  2. jobtracker:協調作業執行,是一個Jave程式,主類為JobTracker
  3. tasktracker:執行作業劃分後的任務,是一個Jave程式,主類為TaskTracker
  4. Hdfs:分散式檔案系統,用於在其他實體之間共享作業檔案

作業流程圖如下:

  1. MapReduce Program 呼叫runJob()建立JobClient並告知其提交作業。
    在提交作業後runJob()會每秒輪詢作業進度,如果發生改變就把進度輸出控制檯。
    作業成後輸出作業計數器,如果失敗,則輸出失敗資訊。

  2. JobClient通過呼叫JobTracker.getNewJobId()請求一個新的JoobId

  3. 將執行作業需要的資源(作業Jar檔案,配置檔案,計算所得的輸入分片)複製到以JobId命名的目錄下jobtrackerHDFS中。
    作業Jar的會有多個副本(mapred.submit.replication預設10),在執行作業的時候,tasktracker可以訪問多個副本。

  4. 呼叫JobTracker.submitJob()方法告知jobtracker作業準備執行。

  5. JobTracker接收到對submitJob()的呼叫後,會把改呼叫放入一個內部佇列,交由作業排程器(job scheduler)進行排程。
    同時會對Job初始化,包括建立一個表示Job正在執行的物件,用來封裝任務和記錄的資訊,用於追蹤任務的狀態和程式。

  6. 為了建立人物執行列表,作業排程器會從共享檔案系統中獲取JobCient已經計算好的輸入分片資訊。
    然後為每一個分片建立一個map任務。
    至於reduce任務則由JonConfmapred.reduce.task決定,通過setNumReduceTask()設定,
    然後排程器建立相應數量的reduce任務。
    此時會被指定任務ID

  7. tasktrackerjobtracker之間維持一個心跳,
    作為訊息通道,tasktracker或告知自身存活情況與是否可以執行新的任務。
    根據資訊,jobtracker會決定是否為tasktracker分配任務(通過排程演算法)。
    這個過程中,對於map任務會考慮資料本地性,對於reduce則不需要。

  8. 一旦tasktracker被分配了任務,接下里就是執行,首先通過Hdfs把作業的Jar檔案複製到tasktracker所在的檔案系統。
    實現作業Jar本地化。
    同時,tasktracker把需要的檔案從Hdfs複製到本地磁碟。
    然後為任務建立一個本地工作目錄,並將Jar中的內容解壓到這裡。
    最後建立一個TaskRunner例項執行該任務。

  9. TaskerRunner啟動一個新的JVM用來執行每一個任務。

  10. 分別執行MapTask或者ReduceTask,結束後告知TaskTracker結束資訊,同時TaskTracker將該資訊告知JobTracker

上面就是Maopreduce1作業執行的流程。我們先有個概念,後面介紹Yarn的時候做下對比。

這裡說的Mapreduce1 指的是Hadoop初始版本(版本1以及更早的)中的Mapreduce分散式執行框架,也就是我們上面的作業流程。
Mapreduce2 指的是使用Yarn(Hadoop 2 以及以後版本)的Mapreduce執行方式。
這裡Mapreduce1、Mapreduce2指的不是Hadoop版本,指的是Mapreduce程式的不同執行機制而已。


Yarn

Yarn (Yet Another Resource Negotiator)是在Hadoop 2引入的叢集資源管理系統,最初的目的是為了改善MapReduce的實現。
但是由於其具有強大的通用性,可以支援其他的分散式計算框架。

在引入的Yarn後,Hadoop 2的生態就發生了一變化,如下:

Yarn提供請求和使用叢集資源的API,但是一般都是由分散式框架(Saprk、Flink等)內部呼叫這些API
使用者則使用分散式系統提供的更高層的API

這種方式向使用者隱藏了資源管理的細節,一定程度上降低了開發難度和運維成本。


Yarn的結構很簡單,如下

Yarn的核心思想是將資源管理和作業排程/監視功能拆分為單獨的守護程式。

具體實現就是:
一個管理叢集上資源使用的全域性資源管理器(RM,ResourceManager);
執行在叢集所有結點上並且能夠啟動和監控容器(Container)的結點管理器(Node Manager)

Container是用於執行特定應用程式的程式,每個資源都有資源限制(記憶體、CPU等)
Container可以是Unix程式,也可以Linux cgroup

Yarn的組成介紹就這麼簡單,接下來我們就看看它怎麼提交執行一個任務。


提交任務

這裡分為兩部分,
第一部分會介紹Yarn任務提交流程,
第二部分會介紹Mapreduce 2 的提交流程


Yarn任務提交流程

Yarn 任務的提交流程如下:

  1. 為了在Yarn上執行任務,Client會向ResourceManager發出執行 Application Master process的請求。

  2. Resource Manager找到一個可以執行Application MasterNodeManager

  3. NodeMager啟動一個容器,執行Application Master

  4. 時Application Master會做什麼操作取決於Application本身,
    可以是在在Application Master執行一個簡單計算任務,將結果返回Client
    也可以向Resource Manager申請更多容器。

  5. 申請到更多的container

從上面的步驟可以發現,Yarn本身是不會為應用的各個部分(Client, Master, Process)之間提供互動。
大多數基於Yarn的任務使用某些遠端通訊機制(比如Hadoop RPC)向客戶端傳遞資訊。
這些RPC通訊機制一般都是專屬於該應用的。


MapReduce 2 任務提交流程

有了上面的基礎,具體的應用怎麼提交。
此處選用MapReduce 2,與一開始MapReduce 1做個對比

涉及到五個實體:

  1. Client:提交 MapReduce job的客戶端
  2. YARN Resource Manager:負責協調分配叢集計算資源
  3. YARN Node Managers:啟動並監視叢集中機器上的計算容器。
  4. MapReduce Application Master:協調MapReduce job的任務執行。
  5. HDFS:用於在其他實體之間共享Job檔案

Application MasterMapReduce Tasks 在容器中執行,他們由Resource Manager排程,由Node Managers管理

提交流程如下:

  1. Job.sumbit()方法建立一個內部的JobSummiter例項,並呼叫其sunbmitJobInternal()方法。
    作業提交後,waitForCompletion()會每秒輪詢返回作業的進度。
    如果作業完成後,如果成功則顯示作業計數器,否則輸出錯誤。

  2. JobSummiterResource Manager申請一個用於 MapReduce job ID的新Application ID
    這個過程會檢查作業,輸出說明:
    例如,如果沒有指定輸出目錄,或者輸出目錄已經存在,則不會提交作業,並向MapReduce程式丟擲錯誤;
    計算作業的輸入分片。
    如果無法計算分片(例如,因為輸入路徑不存在),則作業不提交,並向MapReduce程式丟擲錯誤。

  3. 將執行作業需要的資源(作業Jar檔案,配置檔案,計算所得的輸入分片)複製到以JobId命名的HDFS的目錄下。
    作業Jar的會有多個副本(mapreduce.client.submit.file.replication預設10),
    Node Managers執行任務時,可以跨叢集訪問許多副本。

  4. 通過呼叫Resource ManagersubmitApplication()提交任務。

  5. Resource Manager收到submitApplication()的呼叫請求後,將請求傳遞給Yarn的排程器(Scheduler)。
    Scheduler會為其分配一個容器,

  6. Node Manager在容器中啟動一個Application Master,主類為MRAppMaster

  7. 由於MRAppMaster將從任務接收進度和完成報告,它通過建立許多簿記物件(bookkeeping objects)來初始化作業,以跟蹤作業的進度。

  8. 接下來,MRAppMaster從共享檔案系統檢索在客戶機中計算的輸入切片,
    它會為每個切片建立一個map task;
    建立mapreduce.job.reduces(由Job.setNumReduceTasks())數量的reduce task
    MRAppMaster根據任務的情況決定是執行一個uber task還是向Resource Manager請求更多的資源。

  9. MRAppMasterResource Managerjob中所有的map、reduce tasks申請容器。

  10. 一旦Resource ManagerSchedulertask在指定的Node Manager分配了容器以後,Application Master就會請求Node Manager分配容器。

  11. Node Manager收到請,啟動容器。容器中的主類為YarnChild,執行在專用的JVM中,所以map、reduce、甚至YarnChild本身出現的錯誤都不會影響Node Manager

  12. 在執行task之前,YarnChild會對任務需要的資源進行本地化,包括job配置、JAR檔案以及其他來自Hdfs的檔案。

  13. 最後執行map 或 reduce 任務。

關於的ubertask細節說明:

MRAppMaster必須決定如何執行MapReduce job
利用並行的優勢,確實可以提高任務的執行效率,
但是在小任務或少任務的情況下,
在新的容器中分配和執行任務所額外消耗的時間大於並行執行帶來效率的提升。
這個時候在一個節點上順序執行這些任務反而能獲得更好的效率。
這樣的job被稱為uber task

簡單的說就是並行執行的時候任務效率的提升還不夠彌補你重新申請資源、建立容器、分發任務等消耗的時間。

那麼怎樣才算small job呢?

預設情況下:small job是有少於10個mapper,只有一個reducer,一個輸入大小小於一個HDFS Block大小的job。
當然也可以通過引數 mapreduce.job.ubertask.maxmaps ,mapreduce.job.ubertask.maxreduces , mapreduce.job.ubertask.maxbytes 進行設定。
對於Ubertasks,mapreduce.job.ubertask.enable必須設定為true

對於步驟9補充說明:

在這個過程中,會先申請map任務的容器,
因為所有的map任務都必須在reduce的排序階段開始之前完成(Shuffle and Sort機制)。
reduce任務的請求直到5%的map任務完成才會發出(reduce slow start機制)。
對於reduce任務,可以在叢集的任何結點執行,
但是對map任務,會有資料本地性的要求(詳情此處不展開)
申請還為任務指定記憶體和cpu。預設情況下,每個mapreduce任務分配1024 MB記憶體和1個虛擬核,
可以通過mapreduce.map.memory.mb , mapreduce.reduce.memory.mb , mapreduce.map.cpu.vcores mapreduce.reduce.cpu.vcores進行配置


Yarn 與mapreduce 1

上面就是Mapreduce2的任務提交執行流程,一開始我們就介紹了Mapreduce1,現在我們對比下兩個有啥區別。

本質就是結合Mapreduce 2 對比YarnMapreduce1排程的區別,所以後面Mapreduce 2 直接用Yarn替換

Mapreduce 1 中,作業執行過程由兩類守護程式控制,分別為一個jobtracker和多個tasktracker

jobtracker 通過排程tasktracker上的任務來協調執行在系統的Job,並記錄返回的任務進度。
tasktracker負責執行任務並向`jobtracker``傳送任務進度。

jobtracker同時負責作業的排程(分配任務與tasktracker匹配)和任務進度監控(任務跟蹤、失敗重啟、記錄流水、維護進度、計數器等)

Yarn 中,也有兩類守護程式Resource ManagerNonde Manager分別類比jobtrackertasktracker

但是不一樣的地方在於,jobtracker的職責在Yarn中被拆分,由兩個實體Reource Manger 和``Application Master```(每個Job有一個)。

jobtracker 也可以儲存作業歷史,或者通過執行一個獨立守護程式作為歷史作業伺服器。而與對應的,Yarn提供一個時間軸伺服器(timeline server)來儲存應用的歷史。

二者組成類比

Mapreduce 1 Yarn
jobtracker Reource Manger
Application Master
timeline server
tasktracker Nonde Manager
Slot container

對於二者的區別,心血來潮想了個例子,希望能夠幫助理解。
有三個角色:皇帝、大臣、打工人

現在有兩個情況,
1:發生水災,需要賑災
2:敵寇入侵,邊疆告急

在這種情況下 Mapreduce 1 的做法是:

Yarn的做法:

簡單的說,就是Yarn讓專業的人做專業的事情。
遇到事情找個專家,我只負責提需求和提供資源,
其他的讓專家去做。

這個專家就是MRAppMaster(Mapreduce),而對應的Spark也有自己的專家

由此也總結下Yarn帶來的優勢:

  1. 可擴充性(Scalability)
    Yarn可以在比MapReduce 1更大的叢集上執行。
    MapReduce 1在4000個節點和40000個任務的時候達到擴充性的瓶頸。
    主要是因為jobtracker需要管理作業和任務。
    Yarn就拆分了這個,將作業與任務拆分,由Manager/Application Master分別負責,可以輕鬆將擴充至10,000 個節點 100,000 個任務。
  2. 可用性(Availability)
    高可用性(HA)通常是通過複製另一個守護程式所需的狀態來實現的,以便在活躍狀態的守護程式掛掉時接管提供服務所需的工作。
    但是,jobtracker的記憶體中有大量快速變化的複雜狀態(例如,每個任務狀態每隔幾秒更新一次),這使得將在jobtracker服務配置HA非常困難。
    而對於Yarn而言,由於職責被拆分,那麼HA也隨之變成了分治問題。
    可以先提供Resource Manager的HA,同時如果有需要可以為每個人應用也提供HA。

實際上對於Mapreduce 2Resource ManagerApplication Master都提供了HA,稍候介紹。

  1. 利用率(Utilization)
    MapReduce 1 中,每個tasktracker都靜態配置若干個slot,在配置的時候被劃分為map slotreduce slot,只能執行固定的任務。
    而在Yarn中,Node Manager管理一個資源池,只要有資源,任務就可以執行。
    同時資源是精細化管理的,任務可以按需申請資源。
  2. 多租戶(Multitenancy
    其實,某種程度上來說,統一的資源管理,向其他分散式應用開放Hadoop才是Yarn的最大優勢。
    比如我們可以部署Spark、Flink等等。此時MapReduce也僅僅是在這之上的一個應用罷了

High Availability

接下來再說一下HA吧。
這裡主要結合Mapreduce 2 來說明
HA 針對的都是出現錯誤或失敗的情況。
在我們這裡,出現錯誤或失敗的場景有以下幾個

  1. 任務失敗
  2. Application Master失敗
  3. Node Manager失敗
  4. Resource Manager失敗

接下來我們分別看看這些情況怎麼解決。


task 失敗

任務失敗的情況有可能出現下面的情況:

  1. 使用者map、reduce task程式碼問題,這種失敗最常見,此時在task JVM在退出前會向Application Master傳送錯誤報告,該報告會被計入使用者日誌。最後Application Master會將該任務將任務嘗試標記為failed,並釋放容器,使其資源可供另一個任務使用。
  2. 另一種情況是task JVM突然退出,可能存在一個JVM bug,導致JVM在特定環境下退出MapReduce的使用者程式碼。
    這種情況下,Node Manager發現程式已經退出,會告知Application Master,並將任務嘗試標記為failed
  3. 還有一種是任務超時或者掛起,一旦Application Master注意到有一段時間沒有收到任務進度更新了,就會將該任務標記為failed,由引數mapreduce.task.timeout(預設10分鐘,0表示關閉超時,此時長時間執行任務永遠不會標記為failed,慎用)設定。

task 失敗的處理方式:

  1. Application Master發現任務失敗後,會重新排程該任務,會進行避免在之前失敗的Node Manager上排程該任務。
    如果一個任務連續失敗四次(mapreduce.map.maxattempts,mapreduce.reduce.maxattempts),就不會繼續重調,整個Job也就失敗。
  2. 而有些場景在少數任務失敗,結果仍舊可以使用,那麼此時我們不希望停止任務,可以配置一個允許任務失敗的閥值(百分比),此時不會觸發Job失敗。
    通過mapreduce.map.failures.maxpercentmapreduce.reduce.failures.maxpercent設定。
  3. 還有一個情況是任務嘗試被kill,這種情況Application Master制動標記killed不屬於任務失敗。

推測機制(Speculative Execution),如果發現task執行的時間執行速度明顯慢於平均水平,就會在其他的結點啟動一個相同的任務,稱為推測執行。
這個不一定有效,僅僅是投機性的嘗試。
當任務成功完成時,任何正在執行的重複任務都將被終止,因為不再需要它們。
就是推測任務與原始任務誰能上位就看誰先完成了。


Application Master 失敗

當遇到Application Master失敗時,Yarn也會進行嘗試。
可以通過配置mapreduce.am.max-attempts property(預設:2)配置重試次數,
同時,Yarn對於叢集中執行的Application Master最大嘗試次數加了限制,也需要通過 yarn.resourcemanager.am.max-attempts(預設:2)進行配置。

重試的流程如下:

Application MasterResource Manager傳送心跳,如果Application Master發生故障,Resource Manager將檢測故障,並在新的容器中啟動執行Application Master的新例項

在MapReduce,它可以使用作業歷史記錄來恢復(失敗的)應用程式已經執行的任務的狀態,這樣它們就不必重新執行。
預設情況下恢復是啟用的,但是可以通過設定yarn.app.mapreduce.am.job.recovery來禁用。


MapReduce client輪詢Application Master的進度報告,
但如果它的Application Master失敗,客戶端需要定位新的例項。

Job初始化期間,clientResource Manager請求Application Master的地址,然後對其進行快取,這樣在每次需要輪詢Application Master時,
就不會向Resource Manager發出請求,從而使Resource Manager負擔過重。

但是,如果Application Master失敗,client將發出狀態更新時超時,此時client將向Resource Manager請求新的Application Master的地址。


Node Manager 失敗

如果Node Manager因崩潰或執行緩慢而發生故障,它將停止向Resource Manager傳送心跳(或傳送頻率非常低)。
Resource Manager 如果在10分鐘內(yarn.resourcemanager.nm.liveness-monitor.expiry-interval-ms )沒有收到Node Manager的心跳資訊,
就會告訴該Node Manager,停止傳送心跳,並將它從自己的Nodes池中移除。

在此Node Manager失敗的taskApplication Master 都會按照之前的說的方式恢復。

此外,即使map tasks在失敗的Node Manager上執行併成功完成但屬於未完成的job
Application Master也會安排它們重新執行,
因為它們的中間輸出駐留在故障Node Manager的本地檔案系統上,reduce任務可能無法訪問。

如果一個Node Manager失敗任務次數過多,該Node Manager會被Application Master拉入黑名單。

對於 MapReduce,如果一個Job在某個Node Manager失敗3個任務( mapreduce.job.maxtaskfailures.per.tracker),就會嘗試在其他的結點進行排程。
注意,Resource Manager不會跨應用程式執行黑名單(編寫時),
因此來自新作業的任務可能會在壞節點上排程,即使它們已被執行較早作業的應用程式主程式列入黑名單。


Resource Manager 失敗

Resource Manager失敗是很嚴重的,一旦它失敗, jobtask容器都無法啟動。
在預設配置中,Resource Manager是一個單故障點,因為在(不太可能的)機器故障的情況下,所有正在執行的作業都失敗了並且無法恢復。

要實現高可用性(HA),必須在一個active-standby配置中執行一對Resource Manager
如果 active Resource Manager發生故障,則standby Resource Manager可以接管,而不會對client造成重大中斷。

通過將執行中的應用程式資訊儲存在高可用的狀態儲存區中(通過ZooKeeper/HDFS備份),實現standby Resource Manager 恢復active Resource Manager(失敗)的關鍵狀態。

Node Manager 資訊沒有儲存在狀態儲存中,因為當Node Manager 發出第一次心跳時,新的Resource Manager可以相對較快地對其進行重構。

因為taskApplication Master管理,所以task不屬於Resource Manager的狀態,因此於Resource Manager儲存的狀態比jobtracker中的狀態更容易管理。

目前,有兩種持久化RMStateStore的方式,分別為:FileSystemRMStateStoreZKRMStateStore

整體架構如下:

我們可以通過手動或自動重啟ResourceManager

被提升為active 狀態的ResourceManager載入ResourceManager內部狀態,並根據ResourceManager restart特性儘可能從上一個active Resource Manager 離開的地方繼續操作。

對於以前提交給ResourceManager的每個託管Application,都會產生一個新的嘗試。
應用程式可以定期checkpoint,以避免丟失任何工作。
狀態儲存必須在兩個Active/Standby Resource Managers中都可見。

從上圖可以看到我們可以選擇的狀態儲存介質有兩個FileSystemRMStateStoreZKRMStateStore

ZKRMStateStore隱式地允許在任何時間點對單ResourceManagers進行寫訪問,
因此在HA叢集中推薦使用ZKRMStateStore。

在使用ZKRMStateStore時,不需要單獨的防禦機制來解決可能出現的腦裂情況,即多個Resource Manager可能扮演active角色。

並且ResourceManager可以選擇嵌入基於zookeeperActiveStandbyElector來決定哪個Resource Manager應該是active的。
activeResourceManager關閉或失去響應時,另一個Resource Manager會自動被選為active,然後由它接管。

注意,不需要像HDFS那樣執行一個單獨的ZKFC守護程式,因為嵌入在Resource Manager中的ActiveStandbyElector充當故障檢測器和leader elector,所以不需要單獨的ZKFC守護程式。


關於Yarn的內容就介紹到這裡,更詳細的內容可以參考官網

之後會更新一些Hdfs讀寫的原始碼追蹤相關文章,有興趣可以關注【兔八哥雜談】

相關文章