为什么Docker化Hadoop datanode注册与错误的IP地址?

我有单独的Docker(1.9.1)Hadoop(2.7.1)名称节点和datanode的图像。 我可以从这些容器中创build容器,并通过用户定义的Dockernetworking进行通信。 但是,datanode似乎报告自己具有网关的IP地址,而不是自己的IP地址。 虽然这不会导致单个数据节点出现任何问题,但添加额外的datanode时会产生混乱。 它们都使用相同的IP地址进行注册,名称节点在它们之间翻转,只是报告一个数据节点是活的。

为什么服务器(namenode)通过用户定义的Dockernetworking运行时,从客户端(datanode)套接字连接读取错误的IP地址,我该如何解决?

更新:这个问题似乎在Docker方面

使用--net=bridge运行两个容器并执行netcat服务器:

 nc -v -l 9000 

在一个容器和另一个netcat客户端:

 nc 172.17.0.2 9000 

导致第一个容器正确打印:

172.17.0.3端口9000 [tcp / 9000]接受连接

但是创build一个用户定义的networking:

 sudo docker network create --driver bridge test 

并在以--net=test容器中执行相同的命令错误地输出网关/用户定义的networking接口的IP地址:

来自172.18.0.1端口9000 [tcp / 9000]的连接被接受

HDFS / Docker详细信息

每个数据dfs.datanode.addresshdfs-site.xml文件中的dfs.datanode.address属性都设置为其主机名(例如, hdfs-datanode-1 )。

networking是这样创build的:

 sudo docker network create --driver bridge hadoop-network 

namenode像这样开始:

 sudo docker run -d \ --name hdfs-namenode \ -v /hdfs/name:/hdfs-name \ --net=hadoop-network \ --hostname hdfs-namenode \ -p 50070:50070 \ hadoop:namenode 

datanode是这样开始的:

 sudo docker run -d \ --name hdfs-datanode-1 \ -v /hdfs/data_1:/hdfs-data \ --net=hadoop-network \ --hostname=hdfs-datanode-1 \ --restart=always \ hadoop:datanode 

这两个节点连接正常,当查询(使用sudo docker exec hdfs-namenode hdfs dfsadmin -report )时,连接报告为:

 ...
实时数据节点(1):

名称: 172.18.0.1 :50010(172.18.0.1)
主机名: hdfs-datanode-1
 ...

但是,从运行的输出:

  sudo docker exec hdfs-namenode cat /etc/hosts 

表示namenode认为它在172.18.0.2上运行,而datanode在172.18.0.3上运行:

 172.18.0.2 hdfs-namenode
 127.0.0.1 localhost
 :: 1 localhost ip6-localhost ip6-loopback
 fe00 :: 0 ip6-localnet
 ff00 :: 0 ip6-mcastprefix
 ff02 :: 1 ip6-allnodes
 ff02 :: 2 ip6-allrouters
 172.18.0.3 hdfs-datanode-1
 172.18.0.3 hdfs-datanode-1.hadoop-network

datanode上的等价物显示的是相同的:

 172.18.0.3 hdfs-datanode-1
 127.0.0.1 localhost
 :: 1 localhost ip6-localhost ip6-loopback
 fe00 :: 0 ip6-localnet
 ff00 :: 0 ip6-mcastprefix
 ff02 :: 1 ip6-allnodes
 ff02 :: 2 ip6-allrouters
 172.18.0.2 hdfs-namenode
 172.18.0.2 hdfs-namenode.hadoop-network

运行ip route同时证实了这一点:

 sudo docker exec hdfs-namenode ip route 
默认通过172.18.0.1 dev eth0
 172.18.0.0/16 dev eth0 proto内核作用域链接src 172.18.0.2
 sudo docker exec hdfs-datanode-1 ip route 
默认通过172.18.0.1 dev eth0
 172.18.0.0/16 dev eth0 proto内核作用域链接src 172.18.0.3

然而,当datanode启动时,namenode报告datanode的IP地址为172.18.0.1

 ... INFO hdfs.StateChange:BLOCK * registerDatanode:from DatanodeRegistration( 172.18.0.1:50010,datanodeUuid = 3abaf40c-4ce6-47e7-be2b-fbb4a7eba0e3,infoPort = 50075,infoSecurePort = 0,ipcPort = 50020,storageInfo = lv =  - 56; cid = CID-60401abd-4793-4acf-94dc-e8db02b27d59; nsid = 1824008146; c = 0)storage 3abaf40c-4ce6-47e7-be2b-fbb4a7eba0e3
 ... INFO blockmanagement.DatanodeDescriptor:失败的存储从0更改为0的数量
 ... INFO net.NetworkTopology:添加一个新节点:/ default-rack / 172.18.0.1:50010
 ... INFO blockmanagement.DatanodeDescriptor:失败的存储从0更改为0的数量
 ... INFO blockmanagement.DatanodeDescriptor:为DN 172.18.0.1添加新的存储标识DS-4ba1a710-a4ca-4cad-8222- cc5f16c213fb :50010
 ... INFO BlockStateChange:BLOCK * processReport:from storage DS-4ba1a710-a4ca-4cad-8222-cc5f16c213fb node DatanodeRegistration( 172.18.0.1:50010,datanodeUuid = 3abaf40c-4ce6-47e7-be2b-fbb4a7eba0e3,infoPort = 50075,infoSecurePort = 0,ipcPort = 50020,storageInfo = lv = -56; cid = CID-60401abd-4793-4acf-94dc-e8db02b27d59; nsid = 1824008146; c = 0),blocks:1,hasStaleStorage:false,处理时间:3ms

并使用tcpdump捕获两者之间的stream量(运行在连接到主机networking的Docker容器中 – 使用docker run --net=host )似乎显示错误发生( br-b59d498905c5是创build的networking接口的名称Docker为hadoop-network ):

 tcpdump -nnvvXS -s0 -i br-b59d498905c5 \ "(src host 172.18.0.3 or src host 172.18.0.2) and \ (dst host 172.18.0.3 or dst host 172.18.0.2)" 

IP地址似乎正确地发送registerDatanode调用内:

 ...
 172.18.0.3.33987> 172.18.0.2.9000:...
     ...
     0x0050:f828 004d 0a10 7265 6769 7374 6572 4461。(。M ..registerDa
     0x0060:7461 6e6f 6465 1237 6f72 672e 6170 6163 tanode.7org.apac
     0x0070:6865 2e68 6164 6f6f 702e 6864 6673 2e73 he.hadoop.hdfs.s
     0x0080:6572 7665 722e 7072 6f74 6f63 6f6c 2e44 erver.protocol.D
     0x0090:6174 616e 6f64 6550 726f 746f 636f 6c18 atanodeProtocol。
     0x00a0:01a7 010a a401 0a51 0a0a 3137 322e 3138 ....... Q .. 172.18
     0x00b0:2e30 2e33 120f 6864 6673 2d64 6174 616e .0.3 .. hdfs-datan
     0x00c0:6f64 652d 311a 2433 6162 6166 3430 632d ode-1 。$ 3abaf40c-
     ...

但在随后的调用中是不正确的。 例如,在sendHeartbeat调用之后的几分之一秒:

 ...
 172.18.0.3.33987> 172.18.0.2.9000:...
     ...
     0x0050:f828 004a 0a0d 7365 6e64 4865 6172 7462。(.J..sendHeartb
     0x0060:6561 7412 376f 7267 2e61 7061 6368 652e eat.7org.apache。
     0x0070:6861 646f 6f70 2e68 6466 732e 7365 7276 hadoop.hdfs.serv
     0x0080:6572 2e70 726f 746f 636f 6c2e 4461 7461 er.protocol.Data
     0x0090:6e6f 6465 5072 6f74 6f63 6f6c 1801 9d02 nodeProtocol ....
     0x00a0:0aa4 010a 510a 0a31 3732 2e31 382e 302e ....问.. 172.18.0。
     0x00b0:3112 0f68 6466 732d 6461 7461 6e6f 6465 1 .. hdfs-datanode
     0x00c0:2d31 1a24 3361 6261 6634 3063 2d34 6365 -1 。$ 3abaf40c-4ce
     ...

通过datanode代码进行debugging清楚地显示了当根据namenode返回的信息在BPServiceActor.register()中更新了datanode注册详细信息时发生的错误:

 bpRegistration = bpNamenode.registerDatanode(bpRegistration); 

debuggingnamenode会显示它从datanode套接字连接读取不正确的 IP地址,并更新datanode注册详细信息。

补充笔记

我可以通过在用户定义的Dockernetworking上运行的代码重现此问题:

 import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { // 9000 is the namenode port ServerSocket server = new ServerSocket(9000); Socket socket = server.accept(); System.out.println(socket.getInetAddress().getHostAddress()); } } 

 import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { // 172.18.0.2 is the namenode IP address Socket socket = new Socket("172.18.0.2", 9000); } } 

ServerClient运行在172.18.0.2情况下,正确输出172.18.0.2但是在172.18.0.3运行的Client错误地输出172.18.0.1

运行相同的代码而不使用用户定义的networking(在默认bridgenetworking/ docker0接口并暴露端口9000 )可以提供正确的输出。

我在namenode的hdfs-site.xml文件中将dfs.namenode.datanode.registration.ip-hostname-check属性设置为false ,以防止反向DNS查找错误。 这可能是没有必要的,如果我得到DNS的工作,但现在,由datanode报告错误的IP地址,我怀疑得到DNS的工作将有所帮助。

我相信registerDatanodesendHeartbeatblockReport的相关有线协议是RegisterDatanodeRequestProtoHeartbeatRequestProtoBlockReportRequestProto , 它们的定义可以在这里find 。 这些都包含DatanodeRegistrationProto作为他们的第一个数据成员。 这个消息在这里定义,如下所示:

 /** * Identifies a Datanode */ message DatanodeIDProto { required string ipAddr = 1; // IP address required string hostName = 2; // hostname ... } 

这是由一个已知的docker问题引起的(我也提出了 – closures – 这个重复的描述了问题中提出的步骤)。

有一个合并的拉取请求应该解决这个问题,并计划包含在Docker 1.10.0中。 但与此同时,可以使用以下解决方法:

  1. 使用sudo docker network rm删除所有用户创build的networking
  2. sudo service docker stop停止docker守护进程
  3. sudo iptables -F && sudo iptables -F -t nat清理sudo iptables -F && sudo iptables -F -t nat
  4. sudo service docker start重启docker守护进程
  5. 重新创build用户定义的networking
  6. 运行容器