以MapReduce為例:
原圖:
手繪圖:
YARN中的提交作業的API和經典的MapReduce很像(第1步). 作業提交的過程和經典的MapReduce很像, 新的作業ID(應用ID)由資源管理器分配(第2步). 作業的客戶端核實作業的輸出, 計算輸入的split, 將作業的資源(包括Jar包, 配置文件, split信息)拷貝給HDFS(第3步). 最後, 通過調用資源管理器的submitApplication()來提交作業(第4步).
當資源管理器收到submitApplciation()的請求時, 就將該請求發給調度器(scheduler), 調度器分配第一個container, 然後資源管理器在該container內啟動應用管理器進程, 由節點管理器監控(第5a和5b步).
MapReduce作業的應用管理器是一個主類為MRAppMaster的Java應用. 其通過創造一些bookkeeping對象來監控作業的進度, 得到任務的進度和完成報告(第6步). 然後其通過分散式文件系統得到由客戶端計算好的輸入split(第7步). 然後為每個輸入split創建一個map任務, 根據mapreduce.job.reduces創建reduce任務對象.
然後應用管理器決定如何運行構成整個作業的任務. 如果作業很小, 應用管理器會選擇在其自己的JVM中運行任務, 這種作業稱作是被unerized, 或者是以uber task的方式運行. 在任務運行之前, 作業的setup方法被調用來創建輸出路徑. 與MapRuduce 1中該方法由tasktracker運行的一個任務調用不同, 在YARN中是由應用管理器調用的.
如果不是小作業, 那麼應用管理器向資源管理器請求container來運行所有的map和reduce任務(第8步). (註:每個任務對應一個container,且只能在該container上運行)這些請求是通過心跳來傳輸的, 包括每個map任務的數據位置, 比如存放輸入split的主機名和機架(rack). 調度器利用這些信息來調度任務, 盡量將任務分配給存儲數據的節點, 或者退而分配給和存放輸入split的節點相同機架的節點.
請求也包括了任務的內存需求, 默認情況下map和reduce任務的內存需求都是1024MB. 可以通過mapreduce.map.memory.mb和mapreduce.reduce.memory.mb來配置.
分配內存的方式和MapReduce 1中不一樣, MapReduce 1中每個tasktracker有固定數量的slot, slot是在集群配置是設置的, 每個任務運行在一個slot中, 每個slot都有最大內存限制, 這也是整個集群固定的. 這種方式很不靈活.
在YARN中, 資源劃分的粒度更細. 應用的內存需求可以介於最小內存和最大內存之間, 並且必須是最小內存的倍數.
當一個任務由資源管理器的調度器分配給一個container後, 應用管理器通過練習節點管理器來啟動container(第9a步和9b步). 任務有一個主類為YarnChild的Java應用執行. 在運行任務之前首先本地化任務需要的資源, 比如作業配置, JAR文件, 以及分散式緩存的所有文件(第10步). 最後, 運行map或reduce任務(第11步).
YarnChild運行在一個專用的JVM中, 但是YARN不支持JVM重用.
YARN中的任務將其進度和狀態(包括counter)返回給應用管理器, 後者通過每3秒的臍帶介面有整個作業的視圖(view). 這和MapRduce 1不太一樣, 後者的進度流從tasktracker到jobtracker。
客戶端每秒(通過mapreduce.client.progressmonitor.pollinterval設置)嚮應用管理器請求進度更新, 展示給用戶。
在MapReduce 1中, jobtracker的UI有運行的任務列表及其對應的進度. 在YARN中, 資源管理器的UI展示了所有的應用以及各自的應用管理器的UI。
除了嚮應用管理器請求作業進度外, 客戶端每5分鐘都會通過調用waitForCompletion()來檢查作業是否完成. 時間間隔可以通過mapreduce.client.completion.pollinterval來設置。
作業完成之後, 應用管理器和container會清理工作狀態, OutputCommiter的作業清理方法也會被調用. 作業的信息會被作業歷史伺服器存儲以備之後用戶核查。