戰(zhàn))
1. 先說(shuō)清楚Windows 上跑 Hadoop 偽分布式到底在解決什么問(wèn)題我最早接觸 Hadoop 是在一臺(tái)只有 Windows 筆記本的環(huán)境下當(dāng)時(shí)手里沒(méi)有任何服務(wù)器可用虛擬機(jī)跑兩三個(gè)節(jié)點(diǎn)又卡得厲害于是只能退而求其次在 Windows 本機(jī)搭一套 Hadoop 偽分布式。所謂偽分布式就是把 NameNode、DataNode、ResourceManager、NodeManager 這些角色全部塞在同一臺(tái)機(jī)器上各自以獨(dú)立的 Java 進(jìn)程運(yùn)行用本地磁盤模擬出“分布式”的存儲(chǔ)與計(jì)算結(jié)構(gòu)。它不算真正的集群但足夠把 HDFS 的讀寫流程、YARN 的資源調(diào)度、MapReduce 的作業(yè)提交鏈路完整走一遍。對(duì)于想搞明白 HDFS 副本機(jī)制、塊存儲(chǔ)原理、作業(yè)提交流程的人來(lái)說(shuō)偽分布式的性價(jià)比極高——一臺(tái)電腦、一個(gè) JDK、一個(gè)解壓即用的 Hadoop 發(fā)行包就能把整個(gè)生態(tài)的骨架跑通。這篇文章記錄的是我基于 Windows 系統(tǒng)從零部署 Hadoop 偽分布式的完整過(guò)程包括版本怎么選、目錄怎么規(guī)劃、四個(gè)配置文件怎么寫、格式化啟動(dòng)時(shí)踩了哪些坑、YARN 上跑第一個(gè) WordCount 作業(yè)時(shí)又卡在哪里。適合的人群很明確剛接觸大數(shù)據(jù)組件但又不想一上來(lái)就折騰虛擬機(jī)的同學(xué)、需要在本地快速?gòu)?fù)現(xiàn)某個(gè) HDFS 或 MapReduce 行為的開(kāi)發(fā)者、以及被 Linux 環(huán)境慣壞想看看 Windows 下到底多難纏的老手。我把每一步的判斷依據(jù)和失敗現(xiàn)場(chǎng)都寫下來(lái)了你照著做大概率能少走兩三個(gè)晚上的彎路。2. 方案選型Windows 原生、子系統(tǒng)還是容器2.1 偽分布式在 Windows 上的三種落地方式對(duì)比在動(dòng)手之前我先把三條路都試了一遍結(jié)論是它們各有適用場(chǎng)景不存在誰(shuí)絕對(duì)優(yōu)于誰(shuí)。第一種是 Windows 原生部署直接下載 Hadoop 的二進(jìn)制壓縮包解壓補(bǔ)上 Windows 平臺(tái)缺失的原生庫(kù)文件然后跑起來(lái)。第二種是裝一個(gè) Linux 子系統(tǒng)在子系統(tǒng)里按標(biāo)準(zhǔn) Linux 流程部署Windows 只作為宿主。第三種是用容器拉一個(gè)現(xiàn)成的 Hadoop 鏡像映射幾個(gè)端口就能訪問(wèn) Web UI。我當(dāng)時(shí)的判斷依據(jù)是“學(xué)習(xí)目標(biāo)”而不是“省事程度”。如果只是想看看 HDFS 的 Web 界面長(zhǎng)什么樣、跑一個(gè)現(xiàn)成的示例作業(yè)容器方案十分鐘就能搞定。但如果目標(biāo)是理解 Hadoop 啟動(dòng)時(shí)到底讀了哪些配置、進(jìn)程之間的通信端口是哪些、日志里的報(bào)錯(cuò)分別指向哪個(gè)組件原生部署帶來(lái)的“痛感”反而是有價(jià)值的——每一個(gè)報(bào)錯(cuò)都對(duì)應(yīng)一個(gè)真實(shí)存在的機(jī)制。我最終選了原生方案主要原因是它最貼近真實(shí)的生產(chǎn)環(huán)境配置方式四個(gè) XML 文件怎么寫、環(huán)境變量怎么傳這些經(jīng)驗(yàn)換到 Linux 集群上是通用的。方案上手耗時(shí)學(xué)習(xí)價(jià)值主要痛點(diǎn)適合人群Windows 原生2 到 4 小時(shí)高配置項(xiàng)全部親手寫缺原生庫(kù)、路徑空格、權(quán)限問(wèn)題想吃透配置細(xì)節(jié)的人Linux 子系統(tǒng)1 到 2 小時(shí)中高接近標(biāo)準(zhǔn)流程文件系統(tǒng)互通、內(nèi)存占用有一定 Linux 基礎(chǔ)的人容器鏡像10 到 30 分鐘低黑盒程度高配置改動(dòng)需重建鏡像只想驗(yàn)證功能的人需要說(shuō)明的是原生方案有一個(gè)繞不開(kāi)的前提Hadoop 的很多腳本和原生庫(kù)是按類 Unix 環(huán)境編寫的Windows 上沒(méi)有對(duì)應(yīng)實(shí)現(xiàn)。所以社區(qū)里一直有人維護(hù)一套 Windows 平臺(tái)的補(bǔ)充二進(jìn)制文件主要包含winutils.exe和hadoop.dll兩個(gè)文件。前者負(fù)責(zé)模擬文件權(quán)限、創(chuàng)建目錄等操作后者是本地庫(kù)的 Windows 編譯版本。缺少它們程序不一定立刻報(bào)錯(cuò)但會(huì)在某些節(jié)點(diǎn)突然拋出本地庫(kù)加載失敗這個(gè)后面會(huì)專門講。2.2 為什么偽分布式不需要配置免密登錄很多教程一上來(lái)就讓你裝 SSH、配免密那是針對(duì)完全分布式多機(jī)環(huán)境的。偽分布式下所有進(jìn)程都在本機(jī)Windows 原生部署時(shí)啟動(dòng)腳本本身就不走 SSH 通道Windows 自帶的啟動(dòng)腳本是.cmd后綴的批處理文件直接start拉起各個(gè) Java 進(jìn)程。所以如果你看到某篇教程讓你在 Windows 上裝 OpenSSH、生成密鑰對(duì)、把公鑰追加到授權(quán)文件里可以直接跳過(guò)——那套流程解決的是跨機(jī)器啟動(dòng)的問(wèn)題偽分布式用不上。我一開(kāi)始也照做了結(jié)果白白花了一個(gè)多小時(shí)最后發(fā)現(xiàn)啟動(dòng)腳本壓根不讀這些。真正需要搞清楚的是啟動(dòng)腳本的調(diào)用鏈start-dfs.cmd會(huì)依次拉起 NameNode 和 DataNodestart-yarn.cmd會(huì)拉起 ResourceManager 和 NodeManager每個(gè)進(jìn)程背后都是一個(gè)java命令參數(shù)里的內(nèi)存大小、類路徑都由hadoop-env.cmd和四個(gè) XML 配置文件決定。理解了這條鏈路后面排查啟動(dòng)失敗就有方向了——先看窗口有沒(méi)有一閃而過(guò)再看日志目錄里對(duì)應(yīng)組件的.log文件末尾幾十行。2.3 版本搭配這件事容錯(cuò)率比想象中低Hadoop 和 JDK 的版本搭配是這套環(huán)境里最容易埋雷的地方。Hadoop 3.x 系列官方明確支持的 JDK 版本是 8 和 11用 JDK 17 及以上會(huì)直接撞上模塊訪問(wèn)限制啟動(dòng)時(shí)報(bào)IllegalAccessError或者反射相關(guān)的異常改配置也救不回來(lái)。我自己用的是 JDK 8 的一個(gè)較新小版本穩(wěn)定跑通。Hadoop 本身選的是 3.3 系列的一個(gè)穩(wěn)定小版本這個(gè)系列對(duì) Windows 的兼容性資料最多遇到問(wèn)題容易搜到同類案例。還有一個(gè)容易被忽略的點(diǎn)JDK 的安裝路徑。默認(rèn)安裝到C:\Program Files\Java\...這種帶空格的目錄下Hadoop 的批處理腳本在拼接類路徑時(shí)對(duì)空格處理得很粗糙容易出現(xiàn)“找不到主類”或者路徑被截?cái)嗟那闆r。我的做法是把 JDK 單獨(dú)裝到一個(gè)沒(méi)有空格、沒(méi)有中文的短路徑下比如C:\Java\jdk8后面所有配置都指向這個(gè)路徑。同理Hadoop 的解壓目錄我也放在了根目錄下的短路徑里比如D:\hadoop\hadoop-3.3.x。這一步多花兩分鐘后面能省掉一堆莫名其妙的報(bào)錯(cuò)。3. 環(huán)境準(zhǔn)備目錄規(guī)劃、環(huán)境變量與 Windows 補(bǔ)充庫(kù)3.1 目錄結(jié)構(gòu)怎么規(guī)劃才不給自己挖坑我見(jiàn)過(guò)不少人把 Hadoop 解壓到桌面或者下載文件夾里然后一路報(bào)錯(cuò)到最后放棄。問(wèn)題的根源在于 Hadoop 運(yùn)行時(shí)會(huì)往自己所在的目錄寫日志、寫臨時(shí)文件如果路徑里有中文、空格或者權(quán)限受限比如系統(tǒng)盤的某些目錄進(jìn)程就會(huì)在某個(gè)不起眼的環(huán)節(jié)掛掉。我習(xí)慣的目錄規(guī)劃是這樣的D:\hadoop\ ├── hadoop-3.3.x\ 解壓出來(lái)的發(fā)行包本體 │ ├── bin\ │ ├── etc\hadoop\ 四個(gè)配置文件都在這里 │ ├── logs\ 運(yùn)行日志排錯(cuò)第一現(xiàn)場(chǎng) │ └── sbin\ 啟動(dòng)停止腳本 └── data\ 所有運(yùn)行時(shí)數(shù)據(jù)獨(dú)立于程序目錄 ├── tmp\ 臨時(shí)目錄 ├── namenode\ NameNode 元數(shù)據(jù) └── datanode\ DataNode 數(shù)據(jù)塊把數(shù)據(jù)和程序分開(kāi)是我堅(jiān)持的一個(gè)習(xí)慣。原因很實(shí)際想升級(jí) Hadoop 版本時(shí)直接解壓新版本、改一下環(huán)境變量就行數(shù)據(jù)目錄原封不動(dòng)想徹底重來(lái)一遍時(shí)只需要?jiǎng)h掉data目錄重新格式化程序本體不用動(dòng)。data目錄一定要放在非系統(tǒng)盤因?yàn)?HDFS 寫入數(shù)據(jù)塊時(shí)會(huì)產(chǎn)生大量小文件系統(tǒng)盤容易被塞滿而且 Windows 對(duì)系統(tǒng)盤某些位置的寫入有額外限制。目錄建好之后還需要手工把data下的三個(gè)子目錄創(chuàng)建出來(lái)。雖然 Hadoop 在格式化時(shí)會(huì)自動(dòng)創(chuàng)建但提前建好可以避免“父目錄不存在導(dǎo)致創(chuàng)建失敗”這類邊界問(wèn)題尤其是當(dāng)磁盤權(quán)限設(shè)置比較嚴(yán)格的時(shí)候。3.2 環(huán)境變量的三個(gè)關(guān)鍵項(xiàng)與實(shí)際作用Windows 上需要配置三組環(huán)境變量每一項(xiàng)都有明確用途不是隨便加的。第一項(xiàng)是JAVA_HOME指向 JDK 的根目錄不帶bin。這個(gè)變量的作用范圍比想象中大不只是 Hadoop 讀它很多 Java 生態(tài)的工具鏈都依賴它。配置成C:\Java\jdk8這種形式結(jié)尾不要帶反斜杠。第二項(xiàng)是HADOOP_HOME指向 Hadoop 解壓后的根目錄也就是包含bin、etc、sbin的那一層。這個(gè)變量是 Windows 批處理腳本定位自身的關(guān)鍵start-dfs.cmd內(nèi)部會(huì)用它拼接類路徑。如果沒(méi)配雙擊腳本會(huì)一閃而過(guò)什么提示都沒(méi)有。第三項(xiàng)是在Path里追加%HADOOP_HOME%\bin和%HADOOP_HOME%\sbin。加bin是為了能在任意目錄下敲hadoop、hdfs這些命令加sbin是為了能直接敲start-dfs、stop-yarn這類管理腳本。提示環(huán)境變量改完一定要關(guān)掉所有已打開(kāi)的命令行窗口重新開(kāi)一個(gè)舊的窗口讀的是舊環(huán)境。我吃過(guò)這個(gè)虧改完變量在當(dāng)前窗口里敲hadoop version一直提示“不是內(nèi)部或外部命令”折騰半天才發(fā)現(xiàn)是窗口沒(méi)刷新。驗(yàn)證方式很簡(jiǎn)單新開(kāi)一個(gè)命令行窗口敲hadoop version能打印出版本號(hào)、編譯信息、構(gòu)建時(shí)間就說(shuō)明環(huán)境變量通了。如果這一步就失敗后面的所有操作都不用往下做先把這里解決掉。3.3 winutils.exe 和 hadoop.dllWindows 上繞不開(kāi)的兩個(gè)文件這是 Windows 原生部署 Hadoop 最核心的一道坎。Hadoop 的部分功能依賴本地庫(kù)實(shí)現(xiàn)比如文件權(quán)限模擬、磁盤空間查詢、符號(hào)鏈接處理這些在 Linux 上有系統(tǒng)調(diào)用可以直接用Windows 上需要專門的編譯產(chǎn)物。社區(qū)長(zhǎng)期有人維護(hù)針對(duì)各個(gè) Hadoop 版本的 Windows 二進(jìn)制包主要就是winutils.exe和hadoop.dll兩個(gè)文件。放置位置有講究winutils.exe放到%HADOOP_HOME%\bin目錄下和hadoop.cmd、hdfs.cmd這些批處理文件放在一起。hadoop.dll需要放到兩個(gè)地方一是%HADOOP_HOME%\bin二是C:\Windows\System32。放系統(tǒng)目錄這一步看起來(lái)粗暴但確實(shí)是最省事的做法因?yàn)?Java 通過(guò)系統(tǒng)路徑加載本地庫(kù)放在System32下能被穩(wěn)定找到。版本匹配這件事要特別注意。這些二進(jìn)制文件需要和 Hadoop 主版本對(duì)應(yīng)跨大版本混用可能出現(xiàn)符號(hào)解析失敗。我用的 Hadoop 是 3.3 系列配套的 Windows 二進(jìn)制包也選 3.3 系列的版本。如果你手頭只有 3.2 或 3.1 的包先試試能不能跑如果啟動(dòng)時(shí)拋出UnsatisfiedLinkError或者NativeIO相關(guān)的異常那就說(shuō)明不匹配得去找對(duì)應(yīng)版本。配完這兩個(gè)文件還要在hadoop-env.cmd里顯式指定HADOOP_HOME因?yàn)?Windows 上批處理腳本有時(shí)讀不到系統(tǒng)環(huán)境變量尤其是從快捷方式啟動(dòng)的時(shí)候。我一般會(huì)在文件開(kāi)頭補(bǔ)一行set HADOOP_HOMED:\hadoop\hadoop-3.3.x這一步看起來(lái)冗余但它能避免“明明配了環(huán)境變量卻提示找不到 winutils”的詭異情況。4. 四個(gè)配置文件逐行拆解每一項(xiàng)都在控制什么4.1 core-site.xml整個(gè)集群的入口地址core-site.xml是所有配置里最基礎(chǔ)的一個(gè)它定義的是整個(gè) Hadoop 運(yùn)行時(shí)的通用參數(shù)其中最關(guān)鍵的只有兩三項(xiàng)。configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name valuefile:///D:/hadoop/data/tmp/value /property property nameio.file.buffer.size/name value131072/value /property /configurationfs.defaultFS決定了所有 HDFS 客戶端默認(rèn)連接的地址格式是hdfs://主機(jī)名:端口。端口我用了 9000這是傳統(tǒng)習(xí)慣值也可以改成別的空閑端口但改了之后所有引用這個(gè)地址的地方都要同步改。主機(jī)名寫localhost是最簡(jiǎn)單的做法因?yàn)閭畏植际街辉诒緳C(jī)通信不需要解析真實(shí)主機(jī)名。hadoop.tmp.dir這一項(xiàng)特別重要它是所有臨時(shí)文件的根目錄NameNode 的元數(shù)據(jù)、DataNode 的中間數(shù)據(jù)、YARN 容器的工作目錄都可能落在它下面。如果不顯式配置Hadoop 會(huì)默認(rèn)寫到系統(tǒng)的臨時(shí)目錄里這個(gè)位置在 Windows 上經(jīng)常被清理而且重啟后可能被系統(tǒng)清空導(dǎo)致元數(shù)據(jù)和數(shù)據(jù)不一致下次啟動(dòng)直接報(bào)錯(cuò)。所以我強(qiáng)烈建議把它指向自己規(guī)劃的數(shù)據(jù)目錄。路徑的寫法需要注意Windows 路徑要用正斜杠并且前面加file:///寫成file:///D:/hadoop/data/tmp這種形式。寫成反斜杠或者漏掉file:///前綴解析時(shí)會(huì)出錯(cuò)。這個(gè)細(xì)節(jié)我在第一次配置時(shí)就寫錯(cuò)了報(bào)錯(cuò)信息非常隱晦只是提示某個(gè)路徑不存在看不出是格式問(wèn)題。io.file.buffer.size是讀寫緩沖區(qū)大小默認(rèn)值偏小調(diào)到 128KB 能提升一點(diǎn)大文件讀寫效率。這個(gè)參數(shù)不是必填項(xiàng)屬于順手優(yōu)化配置不配置都不影響啟動(dòng)。4.2 hdfs-site.xml副本、目錄與權(quán)限hdfs-site.xml管的是 HDFS 自身的行為偽分布式下最重要的幾項(xiàng)是副本數(shù)、元數(shù)據(jù)和數(shù)據(jù)目錄、權(quán)限開(kāi)關(guān)。configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name valuefile:///D:/hadoop/data/namenode/value /property property namedfs.datanode.data.dir/name valuefile:///D:/hadoop/data/datanode/value /property property namedfs.permissions.enabled/name valuefalse/value /property property namedfs.namenode.http-address/name valuelocalhost:9870/value /property /configurationdfs.replication默認(rèn)是 3意味著每份數(shù)據(jù)要存三個(gè)副本。偽分布式只有一臺(tái)機(jī)器、一個(gè) DataNode根本湊不出三個(gè)副本HDFS 會(huì)一直處于“副本不足”狀態(tài)頁(yè)面上永遠(yuǎn)掛著紅色的警告。改成 1 是必然選擇既符合單節(jié)點(diǎn)的物理現(xiàn)實(shí)也避免了后臺(tái)反復(fù)做副本復(fù)制帶來(lái)的資源浪費(fèi)。dfs.namenode.name.dir和dfs.datanode.data.dir分別指定元數(shù)據(jù)目錄和數(shù)據(jù)塊目錄。這兩個(gè)目錄必須分開(kāi)絕對(duì)不能指向同一個(gè)路徑否則格式化時(shí)會(huì)產(chǎn)生混亂。我把它們放在data下的不同子目錄里互不干擾。這兩個(gè)路徑同樣要用file:///前綴加正斜杠的寫法。dfs.permissions.enabled設(shè)為false是 Windows 下的實(shí)用妥協(xié)。HDFS 有一套類似 POSIX 的權(quán)限模型但 Windows 的文件系統(tǒng)沒(méi)有對(duì)應(yīng)的權(quán)限位winutils.exe只是做了模擬很多情況下模擬出來(lái)的結(jié)果和預(yù)期不一致容易導(dǎo)致“明明有權(quán)限卻提示拒絕訪問(wèn)”。學(xué)習(xí)環(huán)境下關(guān)掉權(quán)限檢查能省掉大量無(wú)謂的排查時(shí)間。真要做權(quán)限相關(guān)實(shí)驗(yàn)時(shí)再打開(kāi)那時(shí)你對(duì)機(jī)制已經(jīng)有概念了知道該從哪個(gè)方向查。dfs.namenode.http-address指定 Web 管理界面的監(jiān)聽(tīng)地址Hadoop 3.x 的默認(rèn)端口是 98702.x 時(shí)代是 50070。配置成localhost:9870之后啟動(dòng)成功就能在瀏覽器里打開(kāi)http://localhost:9870看到 HDFS 的概覽頁(yè)、文件瀏覽、DataNode 列表等信息。這個(gè)頁(yè)面是驗(yàn)證部署是否成功最直觀的方式。4.3 mapred-site.xml 與 yarn-site.xml計(jì)算框架的接線這兩個(gè)文件是一對(duì)前者告訴 MapReduce 作業(yè)往哪兒提交后者告訴 YARN 怎么接收和運(yùn)行這些作業(yè)。configuration property namemapreduce.framework.name/name valueyarn/value /property /configurationmapreduce.framework.name設(shè)成yarn表示 MapReduce 作業(yè)跑在 YARN 上。另一個(gè)可選值是local本地模式不需要 YARN但那樣就失去了練習(xí)資源調(diào)度的意義。偽分布式下選yarn才有完整的作業(yè)提交鏈路客戶端連 ResourceManager、申請(qǐng)容器、NodeManager 拉起任務(wù)進(jìn)程、任務(wù)跟 HDFS 交換數(shù)據(jù)。yarn-site.xml稍微復(fù)雜一點(diǎn)configuration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.resourcemanager.hostname/name valuelocalhost/value /property property nameyarn.nodemanager.vmem-check-enabled/name valuefalse/value /property property nameyarn.nodemanager.pmem-check-enabled/name valuefalse/value /property /configurationyarn.nodemanager.aux-services這一項(xiàng)是必須的它注冊(cè)了 MapReduce 的 Shuffle 服務(wù)。Map 階段的輸出要經(jīng)過(guò) Shuffle 傳給 Reduce 階段這個(gè)輔助服務(wù)負(fù)責(zé)那部分?jǐn)?shù)據(jù)傳輸。不配這一項(xiàng)作業(yè)提交上去會(huì)一直卡在 map 或者 reduce 階段日志里提示 Shuffle 服務(wù)不可用。yarn.resourcemanager.hostname指定 ResourceManager 所在的主機(jī)。偽分布式寫localhost就行。有些教程會(huì)寫具體的主機(jī)名那樣一旦計(jì)算機(jī)名變了就要改配置不如直接用localhost省心。后面兩個(gè)內(nèi)存檢查開(kāi)關(guān)我建議一并關(guān)掉。YARN 默認(rèn)會(huì)對(duì)每個(gè)容器的虛擬內(nèi)存和物理內(nèi)存做檢查超出限制就殺掉容器。Windows 下這個(gè)檢查的精度很差經(jīng)常在內(nèi)存完全夠用的情況下誤判把任務(wù)容器直接干掉表現(xiàn)為作業(yè)莫名其妙失敗、日志里寫著“Container killed”。關(guān)掉之后內(nèi)存分配由 Java 堆參數(shù)控制行為更可控。這不是生產(chǎn)環(huán)境該有的做法但在本機(jī)學(xué)習(xí)環(huán)境里能省掉很多干擾。4.4 hadoop-env.cmdJava 路徑與內(nèi)存閾值hadoop-env.cmd在etc\hadoop目錄下它的作用是給所有啟動(dòng)腳本注入環(huán)境變量。最關(guān)鍵的只有一行set JAVA_HOMEC:\Java\jdk8注意這里用的是set而不是setx作用范圍僅限當(dāng)前腳本及其子進(jìn)程不會(huì)污染系統(tǒng)環(huán)境變量。這行配置寫錯(cuò)啟動(dòng)時(shí)會(huì)直接報(bào)“JAVA_HOME 未正確設(shè)置”。還有幾個(gè)可選的內(nèi)存參數(shù)值得順手調(diào)一下。Windows 機(jī)器內(nèi)存普遍不小但 Hadoop 默認(rèn)給 NameNode 分配的內(nèi)存偏保守處理大量小文件時(shí)容易觸發(fā)頻繁 GC??梢栽谕晃募锛觭et HADOOP_HEAPSIZE_MAX1024 set HADOOP_NAMENODE_OPTS-Xmx1024m -Xms1024m set HADOOP_DATANODE_OPTS-Xmx512m -Xms512mHADOOP_HEAPSIZE_MAX是全局上限單位和 Linux 上一樣用 MB。NameNode 因?yàn)橐S護(hù)整個(gè)文件系統(tǒng)的元數(shù)據(jù)內(nèi)存給大一點(diǎn)DataNode 相對(duì)輕量512MB 夠用ResourceManager 和 NodeManager 也用默認(rèn)值即可。這些值不用給太夸張本機(jī)學(xué)習(xí)環(huán)境把總內(nèi)存控制在 3GB 以內(nèi)比較穩(wěn)妥剩下的留給系統(tǒng)和其他軟件。5. 格式化與啟動(dòng)第一次跑通的完整過(guò)程5.1 格式化 NameNode 的正確姿勢(shì)配置寫完第一件事是格式化 NameNode。這一步會(huì)在dfs.namenode.name.dir指定的目錄里初始化元數(shù)據(jù)生成current目錄和fsimage文件。命令是hdfs namenode -format執(zhí)行后會(huì)輸出一大段日志最后幾行如果有Storage directory ... has been successfully formatted就說(shuō)明成功了。如果看到Cannot lock storage說(shuō)明該目錄已經(jīng)被占用可能是之前的進(jìn)程沒(méi)退干凈或者已經(jīng)格式化過(guò)。這種情況下不要硬來(lái)先把所有 Hadoop 進(jìn)程確認(rèn)停止再檢查目錄里有沒(méi)有殘留文件。注意格式化只能做一次。重復(fù)格式化會(huì)把 NameNode 的集群 ID 重新生成而 DataNode 目錄里存的還是舊的集群 ID兩者對(duì)不上DataNode 就起不來(lái)日志里會(huì)提示ClusterId mismatch。真遇到了解決辦法是刪掉data目錄下的namenode和datanode兩個(gè)子目錄重新格式化代價(jià)是之前存進(jìn) HDFS 的數(shù)據(jù)全部丟失。學(xué)習(xí)環(huán)境下這不算事但一定要知道這個(gè)因果關(guān)系否則會(huì)對(duì)著報(bào)錯(cuò)猜很久。格式化完成之后namenode目錄下應(yīng)該出現(xiàn)current子目錄里面有VERSION、fsimage_0000000000000000000這類文件??梢匀ゴ_認(rèn)一下這也是判斷格式化是否真的生效的依據(jù)。5.2 啟動(dòng)腳本與進(jìn)程驗(yàn)證啟動(dòng)分兩步。第一步起 HDFSstart-dfs.cmd這個(gè)批處理會(huì)打開(kāi)兩個(gè)新的命令行窗口一個(gè)跑 NameNode一個(gè)跑 DataNode。窗口里會(huì)滾動(dòng)大量日志正常情況下最后會(huì)停在某個(gè)狀態(tài)。這兩個(gè)窗口不能關(guān)關(guān)了進(jìn)程就終止了。我習(xí)慣把它們最小化放著。第二步起 YARNstart-yarn.cmd同樣會(huì)打開(kāi)兩個(gè)窗口分別是 ResourceManager 和 NodeManager。四個(gè)窗口都正常滾動(dòng)日志、沒(méi)有立刻退出說(shuō)明進(jìn)程都起來(lái)了。如果某個(gè)窗口一閃就沒(méi)了說(shuō)明對(duì)應(yīng)的進(jìn)程啟動(dòng)失敗去logs目錄找對(duì)應(yīng)組件的日志文件看末尾的異常堆棧。驗(yàn)證進(jìn)程是否真的活著可以用jps命令。這個(gè)命令在 JDK 的bin目錄下輸出類似12345 NameNode 12678 DataNode 13021 ResourceManager 13345 NodeManager 14567 Jps五個(gè)進(jìn)程對(duì)應(yīng)上HDFS 和 YARN 的關(guān)鍵角色就齊了。如果少了哪個(gè)對(duì)應(yīng)的窗口一定已經(jīng)關(guān)閉了。這一步是我每次重裝環(huán)境時(shí)必做的確認(rèn)比看窗口滾動(dòng)日志更直觀。5.3 Web 界面與命令行雙重驗(yàn)證進(jìn)程起來(lái)之后打開(kāi)瀏覽器訪問(wèn)http://localhost:9870能看到 HDFS 的管理界面。重點(diǎn)看兩個(gè)地方一是首頁(yè)的Summary部分Live Nodes應(yīng)該是 1表示有一個(gè) DataNode 在線二是Utilities菜單下的Browse the file system能列出 HDFS 根目錄的內(nèi)容說(shuō)明元數(shù)據(jù)服務(wù)正常。YARN 的界面在http://localhost:8088首頁(yè)能看到集群的資源總量、已用資源、正在運(yùn)行的作業(yè)數(shù)量。剛啟動(dòng)時(shí)作業(yè)數(shù)是 0資源占用接近 0這是正常的。命令行這邊先建一個(gè)目錄、傳一個(gè)文件上去hdfs dfs -mkdir -p /user/test/input hdfs dfs -ls / hdfs dfs -put D:\local\somefile.txt /user/test/input/ hdfs dfs -cat /user/test/input/somefile.txt這幾條命令能跑通說(shuō)明 HDFS 的讀寫鏈路完全正常。-ls能列出目錄-put能把本地文件寫到 HDFS-cat能把內(nèi)容讀回來(lái)三個(gè)方向都驗(yàn)證到了。5.4 跑通第一個(gè) WordCountWordCount 是驗(yàn)證 MapReduce 鏈路的標(biāo)準(zhǔn)動(dòng)作。Hadoop 發(fā)行包里自帶示例 JAR路徑一般在share\hadoop\mapreduce\hadoop-mapreduce-examples-版本.jar。先準(zhǔn)備輸入數(shù)據(jù)隨便造兩個(gè)文本文件里面放一些重復(fù)的單詞傳到 HDFS 的一個(gè)目錄里hdfs dfs -mkdir -p /user/test/wc/input hdfs dfs -put D:\local\a.txt /user/test/wc/input/ hdfs dfs -put D:\local\b.txt /user/test/wc/input/然后提交作業(yè)。注意輸出目錄必須是 HDFS 上不存在的路徑否則作業(yè)會(huì)直接失敗提示輸出目錄已存在。這是 MapReduce 的設(shè)計(jì)約定為的是防止覆蓋已有結(jié)果。hadoop jar %HADOOP_HOME%\share\hadoop\mapreduce\hadoop-mapreduce-examples-3.3.x.jar wordcount /user/test/wc/input /user/test/wc/output提交之后控制臺(tái)會(huì)不斷打印進(jìn)度百分比。同時(shí)打開(kāi) YARN 的 8088 頁(yè)面能看到一個(gè)應(yīng)用從ACCEPTED變成RUNNING再變成FINISHED。整個(gè)過(guò)程走完用下面的命令看結(jié)果hdfs dfs -cat /user/test/wc/output/part-r-00000輸出的每行是“單詞 出現(xiàn)次數(shù)”按字母序排列??吹竭@個(gè)結(jié)果說(shuō)明 HDFS、YARN、MapReduce 三層已經(jīng)全部打通。6. 常見(jiàn)問(wèn)題速查與排查思路實(shí)錄6.1 啟動(dòng)階段的高頻報(bào)錯(cuò)與定位方法啟動(dòng)階段的問(wèn)題占了全部問(wèn)題的一大半我把遇到過(guò)的整理成一張表方便對(duì)照排查?,F(xiàn)象大概率原因處理方式窗口一閃而過(guò)無(wú)任何輸出HADOOP_HOME沒(méi)配或路徑帶空格在命令行里手動(dòng)執(zhí)行腳本看報(bào)錯(cuò)提示 JAVA_HOME 未正確設(shè)置hadoop-env.cmd里的路徑寫錯(cuò)確認(rèn)路徑存在且不帶引號(hào)NameNode 啟動(dòng)后立刻退出元數(shù)據(jù)目錄被舊數(shù)據(jù)占用清空data目錄重新格式化DataNode 起來(lái)但 Live Nodes 為 0ClusterId 不一致清空數(shù)據(jù)目錄重新格式化提示找不到 winutils補(bǔ)充二進(jìn)制未放到bin目錄補(bǔ)文件并檢查HADOOP_HOMEUnsatisfiedLinkErrorhadoop.dll未放系統(tǒng)目錄復(fù)制到System32后重啟命令行端口被占用9000 或 9870 被其他程序占用換端口或結(jié)束占用進(jìn)程計(jì)算機(jī)名含中文導(dǎo)致注冊(cè)失敗主機(jī)名解析異常改用localhost或修改主機(jī)名“窗口一閃而過(guò)”這個(gè)現(xiàn)象最讓人抓狂因?yàn)榭床坏饺魏五e(cuò)誤信息。我的應(yīng)對(duì)辦法是不雙擊運(yùn)行而是先開(kāi)一個(gè)命令行窗口cd到sbin目錄直接敲腳本名回車。這樣報(bào)錯(cuò)信息會(huì)留在窗口里一眼就能看到是哪一行出的問(wèn)題。這個(gè)技巧適用于所有.cmd批處理腳本。端口占用也值得一提。9000 是 HDFS 的 RPC 端口如果本機(jī)裝了其他用 9000 端口的服務(wù)NameNode 就綁不上。查占用用netstat -ano | findstr :9000能拿到進(jìn)程號(hào)再用任務(wù)管理器找到對(duì)應(yīng)程序。9870 是 Web 端口沖突概率低一些但也不是沒(méi)有。真沖突了改hdfs-site.xml里的端口配置就行只影響 Web 訪問(wèn)不影響數(shù)據(jù)讀寫。6.2 運(yùn)行作業(yè)時(shí)才暴露的問(wèn)題有些問(wèn)題在啟動(dòng)階段完全看不出來(lái)直到提交作業(yè)才炸出來(lái)而且報(bào)錯(cuò)信息往往指向不明顯的地方。最常見(jiàn)的是Container exited with a non-zero exit code 1。這個(gè)報(bào)錯(cuò)本身幾乎不提供任何信息只說(shuō)容器退出碼非零。真正的線索在 NodeManager 的日志里搜這個(gè)容器 ID能找到它拉起的具體命令和標(biāo)準(zhǔn)錯(cuò)誤輸出。我遇到過(guò)的原因有三種一是類路徑拼接在 Windows 下用了錯(cuò)誤的路徑分隔符需要檢查mapreduce.application.classpath配置二是工作目錄權(quán)限不足NodeManager 無(wú)法在臨時(shí)目錄下創(chuàng)建容器目錄三是hadoop.dll沒(méi)被容器進(jìn)程找到因?yàn)槿萜魇切碌倪M(jìn)程環(huán)境變量繼承可能不完整。另一個(gè)是作業(yè)長(zhǎng)時(shí)間卡在ACCEPTED狀態(tài)一直不進(jìn)入RUNNING。這說(shuō)明 ResourceManager 收到了作業(yè)但沒(méi)有分配到容器。原因通常是 NodeManager 沒(méi)注冊(cè)上或者可用資源不足。去 8088 頁(yè)面看Cluster Nodes列表如果為空說(shuō)明 NodeManager 根本沒(méi)連上 ResourceManager回去檢查yarn-site.xml里的主機(jī)名配置和 NodeManager 窗口的日志。如果列表有節(jié)點(diǎn)但資源顯示為 0那是內(nèi)存參數(shù)配得太極端把這個(gè)節(jié)點(diǎn)的可分配內(nèi)存擠沒(méi)了。還有一種比較隱蔽作業(yè)能跑完但結(jié)果不對(duì)比如輸出文件是空的。這種情況先看 Map 階段的輸入記錄數(shù)是不是 0。如果是 0說(shuō)明輸入路徑?jīng)]讀到文件可能是路徑寫錯(cuò)也可能是文件權(quán)限問(wèn)題。我在關(guān)掉權(quán)限檢查之前遇到過(guò)一次本地傳上去的文件所有者是當(dāng)前 Windows 用戶名但作業(yè)運(yùn)行時(shí)的用戶名解析出了偏差導(dǎo)致讀不到文件。關(guān)掉dfs.permissions.enabled之后就沒(méi)再出現(xiàn)過(guò)。6.3 停止、重啟與二次啟動(dòng)的注意事項(xiàng)停止的順序有講究一般是先停 YARN 再停 HDFSstop-yarn.cmd stop-dfs.cmd反過(guò)來(lái)停也不是不行但 YARN 上如果還有運(yùn)行中的作業(yè)直接停 HDFS 會(huì)讓作業(yè)以很奇怪的方式失敗日志里一堆連接異??雌饋?lái)像是配置問(wèn)題實(shí)際上只是停止順序?qū)е碌?。二次啟?dòng)時(shí)最容易踩的坑是“沒(méi)等上一個(gè)進(jìn)程完全退出就重新啟動(dòng)”。Windows 上進(jìn)程退出有時(shí)會(huì)有延遲端口還沒(méi)釋放新進(jìn)程綁不上報(bào)Address already in use。我的做法是停完之后用jps確認(rèn)四個(gè)進(jìn)程都不在了再執(zhí)行啟動(dòng)。如果jps顯示還有殘留去任務(wù)管理器里結(jié)束對(duì)應(yīng)的java.exe進(jìn)程。還有一個(gè)細(xì)節(jié)如果某次啟動(dòng)失敗data目錄里可能會(huì)留下鎖文件或者不完整的臨時(shí)文件導(dǎo)致下一次啟動(dòng)也失敗。判斷方法是看 NameNode 日志里有沒(méi)有Cannot lock storage或者Directory is already locked這類關(guān)鍵詞。遇到了把data目錄下的tmp子目錄清空通常就能恢復(fù)不需要?jiǎng)觧amenode和datanode目錄也就不會(huì)丟數(shù)據(jù)。提示養(yǎng)成一個(gè)習(xí)慣每次改動(dòng)配置文件之前先備份一份。Windows 上排查問(wèn)題時(shí)經(jīng)常要改配置試錯(cuò)改來(lái)改去就忘了最初是什么樣。我一般在etc\hadoop下建一個(gè)bak目錄把改之前的四個(gè) XML 文件復(fù)制進(jìn)去出問(wèn)題了一鍵還原比憑記憶回滾靠譜得多。7. 一些只有實(shí)際搭過(guò)才會(huì)有的體會(huì)整套環(huán)境搭下來(lái)我最深的感受是Windows 上跑 Hadoop 的難點(diǎn)不在 Hadoop 本身而在兩個(gè)系統(tǒng)的“接口層”。配置文件怎么寫、參數(shù)是什么意思這些看文檔就能明白真正耗時(shí)間的是路徑格式、權(quán)限模擬、本地庫(kù)加載這些邊界地帶它們不在任何一份官方文檔里只能靠一次次報(bào)錯(cuò)去試。所以我的建議是第一次搭的時(shí)候把每個(gè)報(bào)錯(cuò)都記下來(lái)包括完整的報(bào)錯(cuò)文本和你最終的解決方式形成自己的排查清單。下次換機(jī)器或者換版本時(shí)這份清單能讓你在半小時(shí)內(nèi)定位問(wèn)題而不是重新經(jīng)歷一遍摸索。另一個(gè)體會(huì)是關(guān)于“夠用就好”這個(gè)判斷。偽分布式下很多人會(huì)照搬生產(chǎn)環(huán)境的參數(shù)副本數(shù)設(shè) 3、內(nèi)存給 8G、各種超時(shí)時(shí)間拉到很長(zhǎng)結(jié)果本機(jī)資源被吃光反而跑不動(dòng)。學(xué)習(xí)環(huán)境的配置應(yīng)該反過(guò)來(lái)做減法副本數(shù)降到 1、內(nèi)存控制在小幾百兆、能關(guān)的檢查都關(guān)掉目標(biāo)是讓鏈路跑通、讓機(jī)制看得見(jiàn)而不是追求性能指標(biāo)。等你把流程走順了再逐項(xiàng)加回去觀察每項(xiàng)參數(shù)帶來(lái)的變化這個(gè)過(guò)程本身就是很好的學(xué)習(xí)。最后分享一個(gè)我在排查時(shí)常用的小技巧不確定某個(gè)配置項(xiàng)有沒(méi)有真正生效時(shí)用hdfs getconf -confKey 配置項(xiàng)名稱直接查運(yùn)行時(shí)的實(shí)際值。比如hdfs getconf -confKey dfs.replication返回的就是當(dāng)前生效的副本數(shù)。這個(gè)命令比翻配置文件可靠因?yàn)樗x的是進(jìn)程實(shí)際加載后的結(jié)果能排除“改了文件但沒(méi)重啟”“改錯(cuò)了文件”這類低級(jí)失誤。搭環(huán)境的過(guò)程中大部分所謂的“疑難問(wèn)題”其實(shí)都是配置沒(méi)生效先用這個(gè)命令確認(rèn)一遍能排除掉一半的可能性。