集群版本:HDP3.1.5
Hadoop版本:Hadoop3.1.1
原始碼地址:https://github.com/hortonworks/hadoop-release/tree/HDP-3.1.5.152-1-tag
一、前置知識
? 大家都知道hadoop的核心組件是HDFS和YARN,HDFS負責存盤,YARN負責計算資源管理,今天要重點扯一扯YARN,YARN的架構跟眾多分布式架構一樣是主從式,為了維護可靠性,ResourceManager(RM)支持High Available(HA)功能,在所有人的認知中,只要是主從架構,掛了一個slave節點或master節點,框架內部的容錯機制都會保證整個系統的正常運行,加上下游的計算應用的重試機制,甚至對用戶無感知,貌似所有人都關心一種情況,就是某個或者某種型別的節點掛掉,但是,還有沒有其它情況呢?非死即生?不,還有一種叫stop the world生不如死,類似jvm的gc,這也是java生態框架最頭疼問題之一,當然,今天要講的不是gc,而是另一種情況下的java行程stop the world導致的嚴重問題——鎖,
為什么需要鎖
RM作為資源管理服務,必然要維護存盤資源的資訊,最簡單的,比如Container被客戶端申請分配,多執行緒的情況下,要保證Container數值的準備性,多執行緒下客戶端要申請資源,會對數值進行更改,避免可能會出現資料不一致的問題,因此對此類資源的操作必須要加鎖,在RM相關的代碼中,有大量的加鎖操作,在hadoop2.x中,RM對資源操作的鎖都是最原始的syncrinized鎖,而在hadoop3.x中,社區考慮到性能問題,把syncrinized鎖全部換成了ReentrantReadWriteLock鎖,
ReentrantReadWriteLock
ReentrantReadWriteLock可以多個Thread可以同時進行讀取操作,但是同一時刻只允許一個Thread進行寫入操作,而synchronized 不論讀寫,只要執行緒進入synchronized代碼就互斥,所以,會出現一個執行緒讀另一個執行緒不能進入的現像,ReentrantReadWriteLock里其實是加了兩把鎖,寫鎖排斥讀、寫,讀鎖只排斥 寫,所以能達到并發讀的效果,克服了synchronized 讀互斥的缺點,所以說 ReentrantReadWriteLock比synchronized 快,這也是hadoop3.x版本中優化中對鎖原因,
二、事發背景
? 考慮成本問題公司今年遷移到新集群,由原來的cdh5.13和hdp2.6兩個集群(都是hadoop2.x)遷移到HDP3.1.5,最后一個開源版本,打包的組件版本都比較新,眾多新特性等待發掘,不至于技識訓礎上落后,遷移之初,業務并沒有從其余兩個集群完全遷移過來,遷移過來的業務也并沒有對外服務,考慮到中間磨合程序,俗話說小病重啟,大病重裝,某一天,突然發現所有任務都提交不到yarn上去,正在運行的任務也無法結束,但是yarn的8088 web ui能夠正常打開,看RM的log也沒看到任何報錯資訊,于是直接重啟RM,一切恢復正常,我大意了啊,沒有進一步排查,這好嗎?這不好,之后的一段時間里,貌似平靜許多,后面才知道,同樣的情況原來越來越頻繁,運維小哥哥每天凌晨偷偷起來重啟RM保障集群穩定,傳說中的人肉運維,隨著集群遷移的完成,所有業務正式上線,每天在yarn上跑的任務越來越多,好幾萬個實體,然鵝RM出現這種情況的頻率從一周一次變成幾個小時一次,啪的一下又hang住了,掛掉還好,會主備切換,但是hang住后zkfc并不會判斷active節點不可用,所以不會切換,幾百上千個任務卡住無法運行,嚴重影響業務,導致集群完全不可用!!瀕臨奔潰邊緣!看樣子Hadoop3.X顯然是有bear而來不講武德!
三、問題分析
-
分析日志
出問題,第一時間看日志,我們分析了每一次出問題時候的RM日志,基本的現象是,RM hang住前,控制container生命周期的列印的log越來越少,比如申請、分配、完成、釋放等等,等到hang住后,這些日志完全沒列印了,也沒有任何報錯資訊,沒結果,
-
分析監控
接著我們把RM的所有監控metrics都看了個遍,包括rpc、gc、cpu、記憶體、網路、資源分配等等,包含了RM大部分常用的指標和RM所在服務器的指標,大概上百個,我們分析出問題的那一刻指標的異動情況,中間只要有個別指標的例外,都刨根問底的查到低,最后也沒查出個所以然,
-
分析任務
為了找出RM hang住的原因,無所不用其極啊,甚至有人把調度任務跟出事的時間點扯上關系,看是不是某些在yarn上跑的特殊的任務導致RM處理問題,在之前hadoop2.x中跑任務從來沒出過這樣的問題,然鵝,還是沒有結果,
-
分析現象
每次出問題的時候,RM的webui能夠正常打開,但是有個地方一直不會跳轉,如下圖:

終于有了突破口,這個問題跟Scheduler有關!
-
執行緒堆疊分析
一般非jvm gc導致的行程卡死的問題,都是執行緒問題,查了一圈關于死鎖問題分析的資料,課參閱:https://blog.csdn.net/liwenxia626/article/details/80791704,我把正常狀態下和非正常住狀態下的RM的jvm執行緒堆疊日志dump出來,執行
jstack <RM PID> > rm.stack比較是頭一回正兒八經的在生產上分析jvm的堆疊,事先也補了大量的課,前面所有的分析步驟都像無頭蒼蠅亂打亂撞,對比兩個日志,雖然沒有明顯的dead lock字眼,但是有了重大發現,看到了曙光,非正常日志下:

**RM內部有執行緒在等待某個鎖的釋放,但是一直沒釋放!**根據上面的資訊,可以知道是由于
org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.LeafQueue#completedContainer里面有寫鎖的操作,有寫鎖必然會有釋放鎖,但是根據上面的執行緒堆疊日志,顯然是有行程在等待它釋放鎖,但是它沒有進入finally釋放鎖,而另一個行程一直在等待它釋放,于是出現了死鎖!!
public void completedContainer(Resource clusterResource,
FiCaSchedulerApp application, FiCaSchedulerNode node, RMContainer rmContainer,
ContainerStatus containerStatus, RMContainerEventType event, CSQueue childQueue,
boolean sortQueues) {
// Careful! Locking order is important!
try {
// 獲取鎖
writeLock.lock();
// 中間邏輯略
} finally {
// 釋放鎖
writeLock.unlock();
}
}
除了上面日志的WriteLock.lock(),堆疊日志中同樣有ReadLock.lock()

根據堆疊日志鎖出現的地方,均在Scheduler相關的類方法,org.apache.hadoop.yarn.server.resourcemanager.scheduler.*下存放的是RM資源調度相關的類,有我們熟知的FIFO、Fair、 CapacityScheduler,本集群使用CapacityScheduler,CS調度下每個佇列內默認FIFO,這也解釋了上面點擊web ui只有Scheduler那一欄卡住不動,可以肯定的是點擊前端頁面有后端方法進入了scheduler包下相關的類方法,
四、定位問題
上面就定位到根本問題了么?只知道是鎖的問題,但不知道為何會導致死鎖問題,翻了yarn的原始碼所有鎖的操作都沒問題,找不到任何毛病,此時又進入無頭蒼蠅模式,病急亂投醫之下瘋狂的調yarn引數,最后還是沒有任何作用,在社區jira上瘋狂的提問,YARN-10440 , YARN-10482 , YARN-10483,最后都沒得到想要的結果,估計現在沒有多少大公司在生產環境使用hadoop3.X,99%還停留在2.x的時代,花了很長時間排查hadoop原始碼的鎖這一塊,最侄訓是沒頭緒,自己解決不了的還不能找人幫忙么?最后,所幸想起參加過開源社區的線下meetup加了一位京東yarn開發組大佬,同時也是hadoop的commitor,在這里要感謝他抽時間幫我看這個問題,最后確定了這是一個jdk1.8的bug引起的,而不是hadoop的bug,詳情參考bug地址:https://bugs.openjdk.java.net/browse/JDK-8134855,ReentrantReadWriteLock在某些情況下會導致解鎖失敗,進而導致死鎖問題,這個bug在jdk9及以上版本中已修復,
五、解決方案
由于現在大部分公司都使用jdk1.8,已經找到了根本原因是jdk的bug,這下就有了正確的解決方向
更換jdk
我們集群使用的是oracle jdk1.8,jdk9由于社區不再維護,不知道更換后有沒有其它會不會有其它問題,考慮風險就直接放棄了,
于是直接更換到jdk11,但是發現更換后RM根本起不來,仔細一查,原來hadoop3還不支持jdk11的runtime,此路不通,也放棄了,
修改yarn的鎖
沒法解決的bug,只能想辦法避開,亦是另一種解決方式,參考hadoop2.x的原始碼,CapacityScheduler相關的類幾乎都使用的synchronized鎖,于是 ,我參考Hadoop2.7的原始碼,把當前Hadoop3.1.1的ReentrantReadWriteLock全部改回synchronized鎖,這樣雖然性能會有些損失,對于沒達到一定量級的公司來說,可以忽略不計,
patch已提交到 YARN-10482
也可以參考本人單獨維護的hadoop3.1.1完整提交記錄:https://github.com/lijufeng2016/HDP-hadoop3.1.1/tree/yarn-capacity-hotfix-20201104
最后,集群終于穩定!
六、小結
這是從業以來解決的跨度時間最大的一個問題,從最初出現,到徹底解決,中間跨度3-4個月,其中花了整整一周時間集中排查解決這個問題,中間排查解決的程序遠不止上面描述的這個簡單,引入新技術框架,出現問題,要接的住,化解的了,最后發到社區給同樣遇到問題的人,不會接化發,年輕人不要輕易在生產環境使用過新的技術,不然碰到棘手問題,耗子尾汁,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/233993.html
標籤:其他
