Hadoop 1.2.1升級2.6.0的一次崎嶇之旅(包括Hive、HBase對應的升級)

 爲了更好地緊跟大數據發展先進性的技術趨勢,追趕業界最頂尖的發展技術,我們把線上環境的Hadoop 1.2.1升級到2.6.0版本。這次的升級其實早有預謀,但鑑於升級過程較爲繁瑣,並且不但涉及HDFS的底層存儲的升級,還涉及Hive、Hbase等上層系統的使用,有一定的風險,因此一直都在等待一個合適的時機。直到最近,我們的利用了Hadoop2.6.0和HBase的0.98.8的API的ETL系統正式完成開發,要在線上環境進行部署的時候,我們才決心要升級。

    雖然之前已經在測試環境進行了一次升級的嘗試,並且已經踩過不少的坑,但在線上環境進行升級的過程,還是出現了各種問題,不過最終還是順利完成了整個升級過程。我們的線上環境之前總共13個節點,其中有12個DataNode和TaskTracker,另外還有1個NameNode和JobTracker,在這次的升級之前,剛好有一臺新機器可以一併部署,於是就決定全部14臺機器一併升級。另外集羣升級之前,所有DataNode節點總共約有60TB的HDFS空間,並且已經使用其中的26TB來存放HDFS數據。

    整個升級步驟總體分爲三步:

1、Hadoop升級。Hadoop 1.2.1 -> Hadoop 2.6.0

2、HBase升級。HBase 0.94.17 -> HBase 0.96.2 -> HBase 0.98.8

3、Hive依賴配置。

    另外要說明的是,這次的升級沒有進行HDFS的HA特性的配置升級,由於考慮到HA特性需要更改相關的Hive、HBase相關元數據,所以還是用回舊的secondary namenode特性。建議在參考本文升級步驟之前,請先在官網閱讀相關文檔,理解相關特性。

Hadoop升級   

  • 準備

(1)停止相關服務。包括HBase,HiveServer2。

        首先停止上層系統,防止升級過程中對Hadoop的影響。

(2)進入安全模式。

        Hadoop有一個安全模式,進入安全模式之後,會禁止HDFS系統的文件進行修改和刪除,一般是在系統啓動的時候,進入初始化的過程中會進入該狀態。由於我們在之後的備份需要讀取HDFS節點的狀態,因此進入安全模式防止對文件系統的修改。相關命令:

[hadoop@namenode]$ hadoop dfsadmin -safemode enter

(3)備份相關數據。

   運行這些命令來進行相關HDFS狀態的備份。

//文件塊數情況
[hadoop@namenode]$ hadoop fsck / -files -blocks -locations > dfs-old-fsck-1.log
//目錄情況
[hadoop@namenode]$ hadoop dfs -lsr / > dfs-old-lsr-1.log
//HDFS結點情況
[hadoop@namenode]$ hadoop dfsadmin -report > dfs-old-report-1.log
//把NameNode在內存中的HDFS節點情況保存到硬盤裏
[hadoop@namenode]$ hadoop dfsadmin -saveNamespace
//手動備份NameNode數據目錄,{dfs.name.dir}是hdfs-site.xml設置的路徑
[hadoop@namenode]$ cp {dfs.name.dir} .

    fsck命令會在最後輸出HDFS是否爲健康狀態(Status: HEALTHY),若有損壞block則要進行恢復。

(4)終結之前的升級狀態。

    防止在升級過程中,存有上次升級過程遺留的狀態,需要清理掉。

[hadoop@namenode]$ hadoop namenode -finalize

(5)備份Hive元數據(可選)

    我們集羣使用了mysql來保留,因此對mysql進行數據庫備份。

(6)停止Hadoop集羣。

[hadoop@namenode]$ {$hadoop_home}/bin/stop-all.sh

(7)校驗HDFS修改記錄是否爲空。

    Hadoop把HDFS文件的修改命令保留到${dfs.name.dir}/name/current/edits*該文件中, 一般只有4bytes數據。如果發現仍然有其它數據,則需要重啓Hadoop集羣,然後等待Hadoop把修改應用到fsimage上。

  • HDFS升級

(1)分發文件,設置軟鏈接。

    分發Hadoop-2.6.0目錄到各個節點。重新設置軟鏈接;另外還要注意修改目錄用戶權限。

(2)修改對應的conf文件夾裏的配置。

    要注意Hadoop 2.x開始很多屬性都更改了名稱,如果要複製原來的舊配置,要修改對應的配置名稱。這些舊的廢棄配置名稱可以在這裏找到。

(3)執行升級腳本

[hadoop@namenode]$ {$hadoop_home}/sbin/hadoop-daemon.sh start namenode -upgrade
[hadoop@namenode]$ {$hadoop_home}/sbin/hadoop-daemon.sh start secondarynamenode
//在每個datanode上執行
[hadoop@datanode]$ {$hadoop_home}/sbin/hadoop-daemon.sh start datanode
//以上命令順利執行後,等待退出安全模式
[hadoop@namenode]$ hdfs dfsadmin -safemode wait

    先用-upgrade參數啓動namenode(注意由於namenode自動在後臺運行,不需要screen或者nohup等防止會話中斷的措施),這樣namenode就會在後臺執行升級過程,並且等待DataNode的連接,然後再啓動secondary namenode,接着就在每個datanode節點上啓動DataNode進程。要注意使用jps命令來確認每次進程都能順利啓動。

   在這裏,我們遇到第一個意外:嘗試啓動DataNode發現失敗,查看log發現是由於我們使用了short-circuit特性,但該特性需要加載本地so庫,需要更高版本的GLIBC,因此需要先升級GLIBC,順利升級後,啓動DataNode成功。之前在測試環境上也出現過此問題,只不過當時是後來升級完成才啓動該特性,才進行GLIBC升級。

    大約過了10分鐘後,集羣關閉了安全模式,完成升級過程。

    查看NameNode的50070端口的時候,發現大約有18000個block處於under replicated 狀態,貌似是HDFS的一次機架之間的節點平衡。原本打算等待複製完成,但等待了約30分鐘後,考慮到Hadoop自行關閉了安全模式,於是決定繼續進行升級步驟。事實上,這些1萬多個block花費了5-6個小時才完成複製,我們實在無法等待那麼長的時間,畢竟還要晚上進行數據導入和處理的任務。

(4)校驗HDFS狀態

[hadoop@namenode]$ hadoop fsck / -files -blocks -locations > dfs-new-fsck-1.log
[hadoop@namenode]$ hadoop dfs -lsr / > dfs-new-lsr-1.log
[hadoop@namenode]$ hadoop dfsadmin -report > dfs-new-report-1.log

    根據文件的內容和之前的狀態備份進行對比,查看是否有問題。

(5)確認升級完畢

[hadoop@namenode]$ hadoop dfsadmin -finalizeUpgrade

    在之前執行的升級命令,會把升級前的${dfs.name.dir}/name/current/該目錄下的內容進行備份,具體會備份在${dfs.name.dir}/name/previous目錄。該備份是用於升級失敗進行回滾的。因此如果我們確認升級沒有問題後,可以執行以上命令,把之前的備份數據刪除。另外要注意的是,如果進行了回滾,在升級之後的數據寫入會完全丟失。建議在運行一段時間沒有出現問題後才執行該命令。

  • MapReduce和YARN部署

//啓動YARN集羣
[hadoop@namenode]$ {$hadoop_home}/sbin/start-yarn.sh
//啓動MapReduce的history server
[hadoop@namenode]$ {$hadoop_home}/sbin/mr-jobhistory-daemon.sh start historyserver

    由於MapReduce不再需要後臺守護進程,不過提供了一個歷史紀錄的服務,我們可以啓動它來記錄MR任務的執行情況。

  • 測試驗證

    確認所有進程都順利啓動,並且log裏沒有異常信息後,我們可以運行測試命令來驗證Hadoop是否成功升級。

[hadoop@namenode]$ hadoop jar {$hadoop_home}/share/hadoop/mapreduce/*examples*.jar randomwriter -Dmapreduce.randomwriter.totalbytes=10000000 test-after-upgrade

    執行以上命令後,則會向HDFS目錄/user/hadoop/test-after-upgrade隨機寫入10000000 bytes數據。執行完成後,查看目錄輸出:

-rw-r--r-- 3 hadoop supergroup 0 2015-01-27 14:23 /user/hadoop/test-after-upgrade/_SUCCESS
-rw-r--r-- 3 hadoop supergroup 10038747 2015-01-27 14:23 /user/hadoop/test-after-upgrade/part-m-00000

    可見,隨機寫任務成功,Hadoop升級完成。

HBase升級

    由於線上的HBase的0.94版本不兼容Hadoop 2.6.0,因此需要進行HBase的升級。根據官網說明爲了升級到HBase 0.98.8版本,必須先升級到0.96.2。在整個升級出現了很多問題,也是耗費了最長時間的地方。有些地方是之前沒有考慮到,也有一些是之前遇到過,比如說某個jar包忘記添加,導致要重新添加然後重啓。 

  • 0.94.17 -> 0.96.2

(1)分發hbase-0.94.17到節點,修改配置文件

(2)檢查損壞文件以及HFileV1格式

[hadoop@hmaster]$ hbase upgrade -check

    要注意這個hbase命令是0.96.2版本目錄下的hbase。最後會輸出是否有損壞文件和HFileV1格式。由於0.96.2不兼容HFileV1,因此如果有該格式文件要進行修改,具體可以查看官網指導。

(3)執行升級命令

[hadoop@hmaster]$ hbase upgrade -execute

    執行命令後會有類似如下內容:

2015-01-27 14:32:43,051 INFO [main] migration.UpgradeTo96: Successfully completed Znode upgrade
2015-01-27 14:32:43,051 INFO [main] migration.UpgradeTo96: Starting Log splitting
2015-01-27 14:32:43,054 INFO [main] migration.UpgradeTo96: No log directories to split, returning

    可以看到升級完成。

    這裏要重點注意一個問題。執行命令的機器必須是啓動HMaster的機器!之前由於測試環境上HMaster和NameNode都在同一個機器,所以沒有留意這個問題,待執行升級命令後,準備啓動HBase的時候一直報錯,後來重新在HMaster機器上執行命令後才成功啓動HMaster。

  • 0.96.2 -> 0.98.8

    這個升級步驟和之前大體類似,但由於0.96版本是一個過渡升級,因此不需要太多設置。0.98.8要啓動HMaster和HRegionServer進程,因此要小心兩者區別。

(1)分發hbase-0.94.17到節點,修改配置文件,設置軟鏈接

(2)檢查損壞文件以及HFileV1格式

[hadoop@hmaster]$ hbase upgrade -check

(3)執行升級命令

[hadoop@hmaster]$ hbase upgrade -execute

    看到和之前0.96.2升級過程一樣的輸出後,成功升級。

(4)替換Hadoop的jar包

    由於HBase 0.98.8還是用Hadoop 2.2.0的包進行編譯,因此我們需要替換掉裏面Hadoop 2.2.0相關的jar包。還注意要添加htrace-core-3.0.4.jar的包。由於之前用到了Phoenix,還需要把jar包替換爲4.1.0版本。

    之前在測試環境配置的時候,只是把lib目錄下所有以hadoop開頭的包替換成對應的2.6.0版本,但發現由於hadoop 2.6.0的RPC依賴的一個包htrace-core版本從2.04升級到3.0.4,但由於HBase自身依賴2.04版本,因此需要把兩個包共存,這樣才能成功啓動HRegionServer。但當時忘記了做備份,導致後面啓動的時候失敗,查看log才發現問題。

(5)啓動HMaster和HRegionServer

[hadoop@hmaster]$ {$HBASE_HOME}/bin/start-hbase.sh

    這個簡單的命令進行啓動即可,但這個過程遇到各種問題導致無法順利啓動HMaster和HRegion進程,其中包括:

1、由於之前運行HBase的升級命令的時候,不是在HMaster的節點上,導致啓動的時候,初始化訪問Zookeeper出現ClusterId read in ZooKeeper is null的錯誤。

    後來重新在HMaster節點上運行升級命令。

2、替換Hadoop依賴包的時候,忘記添加一個htrace-core-3.0.4.jar的包,導致HRegionServer啓動失敗;

    重新添加htrace-core-3.0.4.jar包。

3、複製以前舊的配置的時候,hbase.hregion.memstore.mslab.chunksize和hbase.hregion.memstore.mslab.max.allocation這兩個屬性,用的是2m和256k這樣的字符串,導致HBase無法解析;

    修改爲對應的bytes爲單位的數值。

4、LZO和Snappy壓縮方法無效;

   由於之前在測試環境升級過程中,沒有去配置這兩個壓縮方法,導致在線上升級要進行配置的時候遇到不少坑。

 (1)LZO需要重新編譯,把jar包依賴放到lib下,然後把so放到hadoop/lib/native路徑下。

(2)Snappy的問題最爲奇怪。

    在hadoop/lib/native目錄下建立軟鏈後,依然一直提示壓縮方法失效,HRegionServer無法啓動。當時查看HBase的Web狀態頁面的時候用戶表全爲空,當時還以爲是丟數據了。。。後來發現HRegionServer一直都沒有啓動起來。調查發現libsnappy.so一直都沒有被加載起來。

    奇怪的是,在某個節點上運行HBase使用Snappy的壓縮進行測試則顯示成功,該命令如下:

[hadoop@hmaster]$$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.CompressionTest file:///tmp/test.txt snappy

    該測試命令最後打印出SUCCESS,表示能夠順利利用Snappy壓縮方法對本地文件進行壓縮。

    無奈之下,最後通過在hbase-env.sh下設置HBASE_LIBRARY_PATH環境變量到$HADOOP_HOME/lib/native才成功加載。

    這個問題後來經過閱讀HBase的啓動腳本,並做了大量的測試,發現了根本原因。具體原因的描述見後記。

    解決了以上4個大問題後,終於順利啓動了HBase集羣,並且數據也沒有丟失,也最起碼達到預期的目標。

Hive依賴配置

    由於線上的Hive 0.13.1版本兼容Hadoop 2.6.0,因此我們並不需要對其進行升級。但由於Hive創建了HBase的external表,因此我們要替換這些jar包。由於HBase 0.98.8把之前一個jar包的內容分散到不同的jar包裏,經過了多次嘗試才把具體的依賴jar包找全。另外還要注意修改hive.aux.jars.path的屬性。

後記

    最終從早上10點開始,到晚上20:00左右完成了整個集羣的升級。由於升級時間過長,導致當天來不及導入數據,到第二天的時候很多日常任務都跑失敗,幸好後來重跑一下任務恢復了數據。整個升級過程雖然錯綜曲折,但最後還是能夠頂着壓力完成升級。

    完成升級之後,我們的下一步就是OLAP框架的搭建。爲了能夠更好地提供快速的SQL查詢過程,OLAP的框架的使用是必須的。另外,是否進行HDFS HA的升級也是需要考慮的,畢竟HDFS的高可用特性能夠解決單點故障問題,之前在測試環境的NameNode崩潰的時候,該特性能夠順利啓用,讓我們對這個特性有了很大的信心。

HBase無法加載libsnappy.so的原因分析

    HBase和Hadoop一樣,通過ssh到其它節點上執行啓動命令來啓動進程。但在ssh啓動HRegionServer的過程中,java.library.path路徑並沒有設置!這個環境變量就是Java加載so的查找路徑,因此沒法加載libsnappy.so。查看啓動腳本hbase,有這樣的一段代碼:

#If avail, add Hadoop to the CLASSPATH and to the JAVA_LIBRARY_PATH
HADOOP_IN_PATH=$(PATH="${HADOOP_HOME:-${HADOOP_PREFIX}}/bin:$PATH"which hadoop 2>/dev/null)
if[ -f ${HADOOP_IN_PATH} ]; then
  HADOOP_JAVA_LIBRARY_PATH=$(HADOOP_CLASSPATH="$CLASSPATH"${HADOOP_IN_PATH} \
                             org.apache.hadoop.hbase.util.GetJavaProperty java.library.path 2>/dev/null)
  if[ -n "$HADOOP_JAVA_LIBRARY_PATH"]; then
    JAVA_LIBRARY_PATH=$(append_path"${JAVA_LIBRARY_PATH}""$HADOOP_JAVA_LIBRARY_PATH")
  fi
  CLASSPATH=$(append_path"${CLASSPATH}"`${HADOOP_IN_PATH} classpath 2>/dev/null`)
fi

    這段代碼的作用是通過啓動hadoop命令加載類GetJavaProperty,原本是Hadoop提供用來方便CLI執行自定義MR任務的。這個類的實現如下:

publicclass GetJavaProperty {
  publicstatic void main(String args[]) {
    if(args.length == 0) {
      for(Object prop: System.getProperties().keySet()) {
        System.out.println(prop + "="+ System.getProperty((String)prop, ""));
      }
    }else{
      for(String prop: args) {
        System.out.println(System.getProperty(prop,""));
      }
    }
  }
}

    該類的作用是調用Java函數System.getProperty來打印出環境變量。由於是利用hadoop命令來加載該類,因此hadoop會把自己的本地so庫目錄設置到java.library.path環境變量。這樣來確定具體的hadoop本地so路徑,這樣而不是直接hardcode用{$HADOOP_HOME}/lib/native能夠避免由於後來hadoop的目錄修改導致路徑設置實效,這是相當靈活的配置方法。

    但爲什麼不起效呢?原來因爲HBase節點上ssh到其它節點啓動HRegionServer的時候,由於我們之前升級Hadoop的時候,更換軟鏈接/usr/local/hadoop到hadoop-2.6.0的新目錄,而/usr/local/hadoop是hadoop的用戶目錄,裏面包含幾個hadoop用戶相關的配置,更換軟鏈接的時候,並沒有複製到hadoop-2.6.0裏面,這樣導致設置環境變量的腳本.bashrc和.bash_profile兩個腳本的丟失,所以Hbase啓動的時候通過SSH來啓動命令的時候會因爲無法找到$HADOOP_HOME環境變量而導致失效。雖然之前在測試環境的時候已經發現這個嚴重的問題,但在線上升級的時候,仍然沒有引起足夠的重視,才導致到這樣的問題,以後需要多加註意。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章