Closed Knight-Wu closed 6 years ago
记录一下这个排查了挺久的生产hive集群跑批问题, 使用场景是hive on spark, hive版本是2.2, 使用的原生社区的版本, spark版本是1.6, 使用的是cdh的spark. 大致场景是hive on spark每天晚上跑批的时候, 集群高峰时段偶尔会有任务卡主, 然后超时被我们的调度系统杀掉, 影响跑批的进度 现象 yarn logs收集到driver端的日志, 报错是 commonTree classNotFound exception
记录一下这个排查了挺久的生产hive集群跑批问题, 使用场景是hive on spark, hive版本是2.2, 使用的原生社区的版本, spark版本是1.6, 使用的是cdh的spark. 大致场景是hive on spark每天晚上跑批的时候, 集群高峰时段偶尔会有任务卡主, 然后超时被我们的调度系统杀掉, 影响跑批的进度
然后因为有部分task没有完成, 导致下一个stage没能开始, 这个job就一直卡住了.
因为hdfs写文件的时候, 当client把最后一个block 提交到dn之后, 最后通过 DFSInputStream.close() 去关闭连接, 会轮训请求nn 进行一系列的检查, 包括文件副本最小数必须大于1(默认配置), 否则抛出异常给client. 同时namenode日志如下:
hive -hiveconf spark.driver.extraClassPath=./antlr-runtime-3.4.jar -hiveconf spark.executor.extraClassPath=./antlr-runtime-3.4.jar -hiveconf spark.yarn.dist.files=/opt/cloudera/parcels/CDH-5.12.1-1.cdh5.12.1.p0.3/jars/antlr-runtime-3.4.jar
并且打印executor和driver加载的class, 仍然没发现加载了commonTree这个class, 且仍然报错
spark.executor.extraJavaOptions=-verbose:class spark.driver.extraJavaOptions=-verbose:class
期间还设置了eventLog, 以上方法都没有解决这个问题, 后面发现了现象2.
针对executor写datanode报错, IO层面的异常就比较难排查了, 暂且想到了几个优化集群的方案.
根据图3的异常, 看了下源码, completeFile的时候会检查block的最小副本数是否达到, 客户端会轮询等待nn, 根据后续block 结束completeFile的时间(大概有二十几秒), 增加了retry次数之后, 后续的达不到最小副本数的异常有所减小,
但是仍然出现异常, 故想到写pipeline的dn失败时,重试其他dn, 故找到以下配置:
dfs.client.block.write.replace-datanode-on-failure.policy, 这个配置的解释一开始没看懂n这个参数( let n be the number of existing datanodes), 后面参考了这篇文章, http://blog.cloudera.com/blog/2015/03/understanding-hdfs-recovery-processes-part-2/, 应该是集群中现有的dn的数量,如果是default则按照公式进行计算, 如果是always, 每次都新加一个dn到pipeline中. dfs.client.block.write.replace-datanode-on-failure.best-effort, 再说一下这个配置, 一开始也是没看懂, 也是根据"understanding-hdfs-recovery-processes-part-2", 这篇文章才理解的, 假设这个参数是false, 如果作为replacement的dn也写失败的话就会直接抛出异常, 终止重试; 如果设为true, 则假设replacement的dn也写失败, 仍然会找新的dn去重试. 所以我们想要的是反复重试新的dn, 直到客户端发起completeFile请求时, 轮询nn超时, 故把dfs.client.block.write.replace-datanode-on-failure.policy设置为always, dfs.client.block.write.replace-datanode-on-failure.best-effort设为true, namenode的block state change的日志级别调成debug, 再观察后续出现写异常的时候是否有重试其他dn.
dfs.client.block.write.replace-datanode-on-failure.policy, 这个配置的解释一开始没看懂n这个参数( let n be the number of existing datanodes), 后面参考了这篇文章, http://blog.cloudera.com/blog/2015/03/understanding-hdfs-recovery-processes-part-2/, 应该是集群中现有的dn的数量,如果是default则按照公式进行计算, 如果是always, 每次都新加一个dn到pipeline中.
dfs.client.block.write.replace-datanode-on-failure.best-effort, 再说一下这个配置, 一开始也是没看懂, 也是根据"understanding-hdfs-recovery-processes-part-2", 这篇文章才理解的, 假设这个参数是false, 如果作为replacement的dn也写失败的话就会直接抛出异常, 终止重试; 如果设为true, 则假设replacement的dn也写失败, 仍然会找新的dn去重试.
所以我们想要的是反复重试新的dn, 直到客户端发起completeFile请求时, 轮询nn超时, 故把dfs.client.block.write.replace-datanode-on-failure.policy设置为always, dfs.client.block.write.replace-datanode-on-failure.best-effort设为true, namenode的block state change的日志级别调成debug, 再观察后续出现写异常的时候是否有重试其他dn.
用stackEdit直接编译路径中的文章了
然后因为有部分task没有完成, 导致下一个stage没能开始, 这个job就一直卡住了.
并且打印executor和driver加载的class, 仍然没发现加载了commonTree这个class, 且仍然报错
期间还设置了eventLog, 以上方法都没有解决这个问题, 后面发现了现象2.
针对executor写datanode报错, IO层面的异常就比较难排查了, 暂且想到了几个优化集群的方案.
根据图3的异常, 看了下源码, completeFile的时候会检查block的最小副本数是否达到, 客户端会轮询等待nn, 根据后续block 结束completeFile的时间(大概有二十几秒), 增加了retry次数之后, 后续的达不到最小副本数的异常有所减小,
但是仍然出现异常, 故想到写pipeline的dn失败时,重试其他dn, 故找到以下配置: