记一次菜鸟的问题排查:Docker搭建hadoop集群,通过JAVA API上传文件时出现异常Exception in thread "main" org.apache.hadoop.ipc.RemoteException(java.io.IOException)

先说我的情况:

利用Docker搭建hadoop集群,包含1个master节点,2个slave节点。集群启动时将容器的50070、8088等端口映射到宿主机端口。在通过浏览器访问集群时访问"宿主机IP:映射端口"。到浏览器访问这一步都正常。但是在通过java api向集群上传文件时出现异常如下:

Exception in thread "main" org.apache.hadoop.ipc.RemoteException(java.io.IOException): File /tmp/hadoop-yarn/staging/root/.staging/job_1556205714449_0006/job.split could only be replicated to 0 nodes instead of minReplication (=1).  There are 3 datanode(s) running and 3 node(s) are excluded in this operation.
    at org.apache.hadoop.hdfs.server.blockmanagement.BlockManager.chooseTarget4NewBlock(BlockManager.java:1625)
    at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getNewBlockTargets(FSNamesystem.java:3132)
    at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getAdditionalBlock(FSNamesystem.java:3056)
    at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.addBlock(NameNodeRpcServer.java:725)
    at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolServerSideTranslatorPB.addBlock(ClientNamenodeProtocolServerSideTranslatorPB.java:493)
    at org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos$ClientNamenodeProtocol$2.callBlockingMethod(ClientNamenodeProtocolProtos.java)
    at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:616)
    at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:982)
    at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2217)
    at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2213)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:422)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1758)
    at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2213)

    at org.apache.hadoop.ipc.Client.call(Client.java:1476)
    at org.apache.hadoop.ipc.Client.call(Client.java:1413)
    at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229)
    at com.sun.proxy.$Proxy10.addBlock(Unknown Source)
    at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.addBlock(ClientNamenodeProtocolTranslatorPB.java:418)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:191)
    at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102)
    at com.sun.proxy.$Proxy11.addBlock(Unknown Source)
    at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.locateFollowingBlock(DFSOutputStream.java:1603)
    at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.nextBlockOutputStream(DFSOutputStream.java:1388)
    at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:554)

Process finished with exit code 1

 开始我的漫漫排查之路……

1、先查下异常的原因,最终找到一个靠谱的资料(关键时刻stack over flow是比国内网站各种鱼龙混杂的论坛好用啊)

https://stackoverflow.com/questions/15571584/writing-to-hdfs-could-only-be-replicated-to-0-nodes-instead-of-minreplication

根据这个资料,出现问题的原因有可能是集群格式化或者防火墙有问题,

2、尝试删除文件后重新格式化并启动,关闭防火墙,结果:doesn't work

偶然发现,在重启成功之后在linux下用hdp客户端进行了一些简单验证,创建文件夹,上传文件,发现都是正常的。这说明我的集群没有问题啊。

再看看用java api上传文件时,虽然程序提示异常,但是在集群上依然可以看到我上传的文件的文件名,但是文件大小为0。所以我通过api访问集群时和namenode节点的交互也是没有问题的。因为namenode存储的是数据的元数据信息,而datanode存储数据的具体内容。

所以很有可能是我的java 客户端和datanode的交互出现了问题。是我的编程有问题?重新检查了以下,应该没错。

我运行java程序的机器跟集群不能连通?通过浏览器访问也是这台机器没有问题啊。

再去重新看看1中查到的资料,还有一个原因是datanode节点的端口问题。结合我这里出现的只有文件名没有文件数据的情况,很有可能是这个问题。

hdp客户端和namenode之间有数据传输、client和datanode之间也有数据传输,我的集群没有把datanode和client进行数据传输的50010端口映射到宿主机上。当客户端通过访问“节点名:端口”的方式向端口发送数据的时候是不能成功的。

2、开放datanode的50010端口。

但是开放datanode端口有个问题,不像8088这样的端口,一个集群中就只有一个节点需要开放这个端口,每个datanode都会开放一个50010端口,不能全部映射到宿主机的50010端口。如果每个都指定一个映射的话,太麻烦了。其实这个时候,我启动容器的镜像是通过commit的方式创建的,之前查资料看到DockerFile制作的镜像,在启动时候启动端口随机映射就只需要加一个-P,之前一直遗留没有解决的Dockerfile制作镜像的工作决定拾起来做完。

3、通过Dockerfile制作hadoop镜像

这期间也是各种踩坑各种踩坑各种踩坑,终于找到了合适的方式,这里是总结的制作过程。在这一步踩坑之后想死的心都有了,总算弄好了,该解决了吧。。。

4、通过通过DockerFile制作镜像,开放50010端口,启动时使用-P参数。依然doesn't work。

这是咋回事?我不是已经映射到宿主机端口了吗?难道不是这个端口的问题?回头看了下1中的资料,总觉得问题就在这里。又仔细想了下,我把端口随机映射了,但是我的程序不知道啊,是不是需要配置哪个配置信息让我的客户端知道?配置hdfs-site.xml中的datanode端口为映射后的端口,但是这个每个节点都不一样,该怎么配?查资料,半天,无果。作为以命菜鸟,这中间的原理也没有特别清楚,不知道有没有啥不了解的细节,差不到资料,或许我可以换个思路?

5、其实,到这一步已经弄清楚了问题的根源。我的问题的关键点就在于,我用于编程的PC和docker的宿主机在一个网段,可以通信,所以当我把容器50070等端口映射到宿主机后就可以通过我的PC访问端口;docker宿主机也可以与运行在它上面的容器进行通信,所以我可以通过在宿主机上执行命令行正确操作集群。那么,我现在只要让我的PC可以和宿主机上的容器集群通信就可以了啊,要是PC可以通过容器的IP访问,那管它hdp内部啥机制,我就把我的环境模拟的更普通集群一样了不久没乱七八糟问题了。为了解决容器向局域网暴露独立IP这一问题,又是一个大坑,具体细节看这里

6、由于hdp严重依赖主机名进行通信,所以记得在运行java程序的机器上配置host,记录主机名和IP的映射。windows配置host自行百度。

 至此,问题算是解决了,终于可以在本地运行java程序操作集群了,不过啊,本地调试主要还是用单机版。。。。。。

原文地址:https://www.cnblogs.com/Jing-Wang/p/10855522.html