问题Review
因为oozie4.2版本修复了4.1版本的rerun的bug,而且考虑升级成本不大,so在某一天的下午启动了升级,升级步骤参看Oozie4.2安装笔记
重启oozie server,打开oozie webui,一切正常,长舒一口气,没有引发线上任务异常。
但是欢乐的时光注定短暂,一个小时左右生产任务报警,上线后查看oozie历史,报警是一个执行bulkload的java action任务失败触发,这个任务主要是将上一个action生成的hfile导入到hbase表中。
为什么会失败?查看oozie server的oozie.log,其中有如下异常日志:
org.apache.oozie.action.hadoop.JavaMainException: org.apache.hadoop.hbase.io.hfile.CorruptHFileException: Problem reading HFile Trailer from file hdfs://xxxxxxx/user/hadoop/myproject/tmp/import_2_hbase_hfile/0000000-160223221418726-oozie-hado-W/meta/5bd82eb70707448d917ab7e319caec09
at org.apache.oozie.action.hadoop.JavaMain.run(JavaMain.java:58)
at org.apache.oozie.action.hadoop.LauncherMain.run(LauncherMain.java:39)
at org.apache.oozie.action.hadoop.JavaMain.main(JavaMain.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.oozie.action.hadoop.LauncherMapper.map(LauncherMapper.java:226)
at org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:54)
at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:453)
at org.apache.hadoop.mapred.MapTask.run(MapTask.java:343)
at org.apache.hadoop.mapred.LocalContainerLauncher$EventHandler.runSubtask(LocalContainerLauncher.java:370)
at org.apache.hadoop.mapred.LocalContainerLauncher$EventHandler.runTask(LocalContainerLauncher.java:295)
at org.apache.hadoop.mapred.LocalContainerLauncher$EventHandler.access$200(LocalContainerLauncher.java:181)
at org.apache.hadoop.mapred.LocalContainerLauncher$EventHandler$1.run(LocalContainerLauncher.java:224)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.AbstractExecutorService$1.run(AbstractExecutorService.java:94)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caused by: org.apache.hadoop.hbase.io.hfile.CorruptHFileException: Problem reading HFile Trailer from file hdfs://xxxxxx/user/hadoop/myproject/tmp/import_2_hbase_hfile/0000000-160223221418726-oozie-hado-W/meta/5bd82eb70707448d917ab7e319caec09
at org.apache.hadoop.hbase.io.hfile.HFile.pickReaderVersion(HFile.java:463)
at org.apache.hadoop.hbase.io.hfile.HFile.createReader(HFile.java:506)
at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.groupOrSplit(LoadIncrementalHFiles.java:589)
at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles$3.call(LoadIncrementalHFiles.java:507)
at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles$3.call(LoadIncrementalHFiles.java:505)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.AbstractExecutorService$2.run(AbstractExecutorService.java:120)
... 3 more
Caused by: java.lang.UnsatisfiedLinkError: org.apache.hadoop.util.NativeCodeLoader.buildSupportsSnappy()Z
at org.apache.hadoop.util.NativeCodeLoader.buildSupportsSnappy(Native Method)
at org.apache.hadoop.io.compress.SnappyCodec.checkNativeCodeLoaded(SnappyCodec.java:63)
at org.apache.hadoop.io.compress.SnappyCodec.getDecompressorType(SnappyCodec.java:192)
at org.apache.hadoop.io.compress.CodecPool.getDecompressor(CodecPool.java:178)
at org.apache.hadoop.hbase.io.compress.Compression$Algorithm.getDecompressor(Compression.java:327)
at org.apache.hadoop.hbase.io.compress.Compression.decompress(Compression.java:422)
at org.apache.hadoop.hbase.io.encoding.HFileBlockDefaultDecodingContext.prepareDecoding(HFileBlockDefaultDecodingContext.java:91)
问题分析
java.lang.UnsatisfiedLinkError: org.apache.hadoop.util.NativeCodeLoader.buildSupportsSnappy()Z
这个异常引发的问题,分析应该是因为通过snappy压缩的hfile,在做bulkload的时候没有找到snappy的链接文件,导致抛出UnsatisfiedLinkError。
但是为什么在oozie4.1版本中没有出现这个问题?肯定是哪里没有将snappy的链接文件路径加入到LD_LIBRARY_PATH中,导致执行的container task找不到加载的链接文件。
因为执行的是java action,action指定的JAVA_MAIN_CLASS直接在launcher的mapper task中执行,所以应该是launcher的LD_LIBRARY_PATH不对导致的。在任务执行期间登陆到container task所在的机器,并在container运行目录下找到了launcher_container.sh的脚本,打开一看,果然LD_LIBRARY_PATH中缺少$HADOOP_INSTALL_ROOT/lib/native,而在oozie4.1中却有这个路径。
于是对比一下container运行目录下的job.xml配置文件,发现一个参数的配置应该是引起此问题的元凶:mapreduce.job.ubertask.enable,oozie4.1中任务配置为false,而oozie4.2中配置为true。
这种看来是由oozie的升级引起的,必定也来自于oozie的配置,于是进入oozie安装路径找到oozie的配置(oozie-site和oozie-default),对比发现oozie.action.launcher.mapreduce.job.ubertask.enable
这配置项在4.1中默认配置为false,而在4.2中默认配置为true。
好吧,重新看了一下oozie4.2的release-log,果然:OOZIE-1385 Make Uber Mode the default (rkanter)
解决问题
那么问题的原因应该找到了,开始修复并验证。
方案一:
将oozie.action.launcher.mapreduce.job.ubertask.enable
配置到oozie4.2版本的oozie-site.xml中并设置为false,重启oozie server
这种方案对所有的oozie job都关闭了ubertask的功能,不是很友好,毕竟uber mode能够提升launcher执行的效率-
方案二:
在bulkload的java action的confituration中增加配置项:<property> <name>oozie.launcher.mapreduce.user.classpath.first</name> <value>true</value> </property>
这个方案之所以能够生效,查看Oozie的JavaActionExecutor的代码有如下逻辑,USER CLASSPATH FIRST为true的时候禁用uber mode
private boolean checkPropertiesToDisableUber(Configuration launcherConf) { boolean disable = false; if (launcherConf.getBoolean(HADOOP_JOB_CLASSLOADER, false)) { disable = true; } else if (launcherConf.getBoolean(HADOOP_USER_CLASSPATH_FIRST, false)) { disable = true; } return disable; }
```
- 方案三:
显式的在launcher的job env中增加LD_LIBRARY_PATH的配置,配置项如下:<property> <name>oozie.launcher.mapred.map.child.env</name> <value>LD_LIBRARY_PATH=${hadoop_install_root}/lib/native</value> </property>
任一方案修改生效后,rerun失败的任务,All Done!!
问题解释
那这个uber参数到底影响了什么,为什么true和false会引发不同的行为?下面开始跟踪源码
首先看一下网络上对uber job的解释(具体细节可关注参考文档页说明)
当一个job较小时,AM有一种特殊的处理机制,AM可以选择在它自己的同一个JVM中运行该job。这种情况发生在AM为该job重新分配container时需要的开销是大于在AM自己的container上运行该job,那么这样的job是成为uberlized的job,或者称为ubertask job。
这种模式的关键是job的map,reduce过程是与AppMaster共享同一个container。
这种模式需要满足三个条件:
1. 只有一个reducer
2. 少于10个mapper
3. Job输入数据集的大小小于HDFS的数据块大小。
据Arun的说法,启用该功能能够让一些任务的执行效率提高2到3倍(“we've observed 2x-3x speedup for some jobs”)
通过launcher任务查看到的确launcher启动的command不同:
- uber模式为org.apache.hadoop.mapreduce.v2.app.MRAppMaster
- 非uber模式为org.apache.hadoop.mapred.YarnChild 111.111.111.111 47209 attempt_1454657264436_3326_m_000000_0 2
看来引发问题的原因就是因为任务符合uber的小application的定义,所以按照uber的方式执行。而对于uber方式因为复用master的JVM,而启动master的JVM的LD_LIBRARY_PATH没有设置${hadoop_install_root}/lib/natvie造成map task的container找不到snappy相关的链接库。