Hadoop異常解決:Yarn Failed to launch container
問題
在伺服器上起了HDFS+Yarn,然後提交了一個作業:
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.1.jar pi 1 2
但是執行的時候報錯,Console的log如下:
2020-11-03 14:31:44,840 WARN org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch: Failed to launch container. java.io.FileNotFoundException: File /home/hadoop/app/tmp/nm-local-dir/usercache/root/appcache/application_1604385076312_0001/container_1604385076312_0001_02_000001 does not exist at org.apache.hadoop.fs.RawLocalFileSystem.deprecatedGetFileStatus(RawLocalFileSystem.java:606) at org.apache.hadoop.fs.RawLocalFileSystem.getFileLinkStatusInternal(RawLocalFileSystem.java:819) at org.apache.hadoop.fs.RawLocalFileSystem.getFileStatus(RawLocalFileSystem.java:596) at org.apache.hadoop.fs.FileSystem.primitiveMkdir(FileSystem.java:1052) at org.apache.hadoop.fs.DelegateToFileSystem.mkdir(DelegateToFileSystem.java:161) at org.apache.hadoop.fs.FilterFs.mkdir(FilterFs.java:197) at org.apache.hadoop.fs.FileContext$4.next(FileContext.java:730) at org.apache.hadoop.fs.FileContext$4.next(FileContext.java:726) at org.apache.hadoop.fs.FSLinkResolver.resolve(FSLinkResolver.java:90) at org.apache.hadoop.fs.FileContext.mkdir(FileContext.java:726) at org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor.createDir(DefaultContainerExecutor.java:562) at org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor.launchContainer(DefaultContainerExecutor.java:161) at org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.call(ContainerLaunch.java:302) at org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.call(ContainerLaunch.java:82) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
檢視NodeManager的log,也是類似的資訊:
WARN org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch: Failed to launch container.
簡單分析可知:(參考Yarn的作業流程圖,)程式在比較早期就掛了,即 RM 在 NM 上啟動 Container,但是起不來。
分析1
看了一會log,發現有的地方寫到關於Container記憶體大小的,動輒就是1G多。(好像,預設分配給Container的記憶體大小是1024MB)
我的伺服器的配置是: 1 core + 2GB memory。算上已經執行的程式,剩下的也就600MB的記憶體。
所以,我的第一個思路是:會不會Container因為記憶體不足起不來?
搗鼓了一陣,把記憶體使用改小了。
yarn-site.xml
<property> <name>yarn.nodemanager.resource.memory-mb</name> <value>512</value> </property> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>256</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>256</value> </property> <property> <name>yarn.app.mapreduce.am.resource.mb</name> <value>128</value> </property>
mapr-site.xml
<property>
<name>mapreduce.map.memory.mb</name>
<value>128</value>
</property>
<property>
<name>mapreduce.reduce.memory.mb</name>
<value>128</value>
</property>
<property>
<name>mapreduce.map.java.opts</name>
<value>-Xmx128m</value>
</property>
<property>
<name>mapreduce.reduce.java.opts</name>
<value>-Xmx128m</value>
</property>
重啟再試一遍,還是不行。看來不是因為這個原因。改回來。
(後來看log可以知道:Container的記憶體管理分為實際實體記憶體佔用和虛擬記憶體佔用。執行這個exmaple程式,實體記憶體可能只有十幾MB,虛擬記憶體佔用可達幾個GB)
分析2
還是回到log中來。
DefaultContainerExecutor
想要launchContainer
,但是 somehow 有個檔案路徑不存在,所以需要createDir
。
但是為什麼建立資料夾的操作最終會報檔案不存在的Error呢?繼續分析。
一路呼叫Hadoop自帶的工具類建立資料夾,最終,到了FileSystem.primitiveMkdir
中,會做一個判斷,parent dir存不存在,原始碼如下:
protected void primitiveMkdir(Path f, FsPermission absolutePermission,
boolean createParent)
throws IOException {
if (!createParent) { // parent must exist.
// since the this.mkdirs makes parent dirs automatically
// we must throw exception if parent does not exist.
final FileStatus stat = getFileStatus(f.getParent());
if (stat == null) {
throw new FileNotFoundException("Missing parent:" + f);
}
if (!stat.isDirectory()) {
throw new ParentNotDirectoryException("parent is not a dir");
}
// parent does exist - go ahead with mkdir of leaf
}
// Default impl is to assume that permissions do not matter and hence
// calling the regular mkdirs is good enough.
// FSs that implement permissions should override this.
if (!this.mkdirs(f, absolutePermission)) {
throw new IOException("mkdir of "+ f + " failed");
}
}
也就是在這個時候,getFileStatus(f.getParent());
這裡報錯了。
報錯的路徑是這個:
/home/hadoop/app/tmp/nm-local-dir/usercache/root/appcache/application_1604385076312_0001/container_1604385076312_0001_02_000001
在系統搜了一下確實沒有。除此之外,我們還知道,這個是一個parent dir,裡面還有東西的。
這個container_1604385076312_0001_02_000001
到底在哪裡呢?
搜了一圈,發現在這個路徑下:
/home/hadoop/app/tmp/nm-local-dir/nmPrivate/application_1604385076312_0001/container_1604385076312_0001_02_000001
注意:一個是nm-local-dir/usercache
,一個是nm-local-dir/nmPrivate
。
並且,在container_1604385076312_0001_02_000001
路徑下,我們可以發現兩個檔案:
container_1604385076312_0001_02_000001.tokens
launch_container.sh
所以,我們大概能猜想到背後發生的事:Yarn想要呼叫launch_container.sh
這個檔案啟動一個Contianer。但是somehow由於路徑不對,沒有找到這個檔案。
關於這個default路徑,我看了DefaultContainerExecutor.launchContainer
的原始碼,猜想是使用的DEFAULT_CONTAINER_TEMP_DIR
。
Path tmpDir = new Path(containerWorkDir,
YarnConfiguration.DEFAULT_CONTAINER_TEMP_DIR);
createDir(tmpDir, dirPerm, false, user);
具體是怎麼找的我就沒有深究了,反正這裡把它換了試一下先。
修改
這裡,我所作的修改是:換一個路徑,手動指定 Yarn 的local dir。
yarn-site.xml
<property>
<name>yarn.nodemanager.local-dirs</name>
<value>/home/hadoop/yarn/nm/localdir</value>
</property>
重啟再試,成功!
觀察 NM 的log,能看到如下語句,顯示地指定了執行bash命令的路徑。
INFO org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor:
launchContainer:
[bash, /home/hadoop/yarn/nm/localdir/usercache/root/appcache/application_1604388443689_0001/container_1604388443689_0001_01_000005/default_container_executor.sh]
參考
- Yarn簡單介紹及記憶體配置 https://www.cnblogs.com/oumiga/articles/4174502.html
- Hadoop YARN – 如何限制requestedMemory https://www.qedev.com/bigdata/231265.html
- yarn-site.xml 配置說明 https://www.jianshu.com/p/35374384a1aa
- DefaultContainerExecutor 與 LinuxContainerExecutor https://blog.csdn.net/zhanglong_4444/article/details/89380307