我有针对Hadoop(2.7.1)名称节点和数据节点的单独Docker(1.9.1)映像.我可以从这些容器中创建容器,并让它们通过用户定义的Docker网络进行通信.但是,datanode似乎报告自己拥有网络网关的IP地址而不是自己的IP地址.虽然这不会导致单个数据节点出现任何问题,但在添加其他数据节点时会产生混淆.它们都使用相同的IP地址注册,名称节点在它们之间翻转,只报告单个数据节点是活动的.
为什么服务器(namenode)在用户定义的Docker网络上运行时从客户端(datanode)套接字连接读取错误的IP地址,我该如何解决?
更新:此问题似乎在Docker端
使用–net = bridge运行两个容器并执行netcat服务器:
nc -v -l 9000
在一个容器中,另一个容器中的netcat客户端:
nc 172.17.0.2 9000
导致第一个容器正确打印:
Connection from 172.17.0.3 port 9000 [tcp/9000] accepted
但是创建一个用户定义的网络:
sudo docker network create --driver bridge test
并且在以–net = test启动的容器中执行相同的命令会错误地打印网关/用户定义的网络接口的IP地址:
Connection from 172.18.0.1 port 9000 [tcp/9000] accepted
HDFS / Docker详细信息
每个datanode的hdfs-site.xml文件中的dfs.datanode.address属性设置为其主机名(例如,hdfs-datanode-1).
网络创建如下:
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),连接报告为:
... Live datanodes (1): Name: 172.18.0.1:50010 (172.18.0.1) Hostname: 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
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
default via 172.18.0.1 dev eth0 172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2
sudo docker exec hdfs-datanode-1 ip route
default via 172.18.0.1 dev eth0 172.18.0.0/16 dev eth0 proto kernel scope link 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: Number of Failed storage changes from 0 to 0 ... INFO net.NetworkTopology: Adding a new node: /default-rack/172.18.0.1:50010 ... INFO blockmanagement.DatanodeDescriptor: Number of Failed storage changes from 0 to 0 ... INFO blockmanagement.DatanodeDescriptor: Adding new storage ID DS-4ba1a710-a4ca-4cad-8222-cc5f16c213fb for DN 172.18.0.1:50010 ... INFO BlockStateChange: BLOCK* processReport: from storage DS-4ba1a710-a4ca-4cad-8222-cc5f16c213fb node DatanodeRegistration(172.18.0.1:50010,storageInfo=lv=-56;cid=CID-60401abd-4793-4acf-94dc-e8db02b27d59;nsid=1824008146;c=0),blocks: 1,hasStaleStorage: false,processing time: 3 msecs
并使用tcpdump捕获两者之间的流量(在连接到主机网络的Docker容器中运行 – 使用docker run –net = host)似乎显示发生错误(br-b59d498905c5是由创建的网络接口的名称) hadoop网络的Docker:
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 ....Q..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代码进行调试清楚地显示了datanode registration details are updated in BPServiceActor.register()
基于namenode返回的信息时出现的错误:
bpRegistration = bpNamenode.registerDatanode(bpRegistration);
调试namenode shows,它从datanode套接字连接读取错误的IP地址,并更新datanode注册详细信息.
补充说明
我可以通过在用户定义的Docker网络上运行的代码重现此问题:
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);
}
}
服务器和客户端都在172.18.0.2上运行,这正确输出172.18.0.2但客户端在172.18.0.3上运行时输出172.18.0.1错误.
在不使用用户定义的网络(在默认桥接网络/ docker0接口和暴露端口9000上)运行相同的代码可以得到正确的输出.
我在namenode的hdfs-site.xml文件中将dfs.namenode.datanode.registration.ip-hostname-check属性设置为false,以防止反向DNS查找错误.如果我让DNS工作,将来可能没有必要这样做但是现在,由于数据节点报告了错误的IP地址,我怀疑DNS工作会有所帮助.
我相信registerDatanode,sendHeartbeat和blockReport的相关有线协议是RegisterDatanodeRequestProto,HeartbeatRequestProto和BlockReportRequestProto以及their definitions can be found here.这些都包含DatanodeRegistrationProto作为它们的第一个数据成员.此消息是defined in here,如下所示:
/**
* Identifies a Datanode
*/
message DatanodeIDProto {
required string ipAddr = 1; // IP address
required string hostName = 2; // hostname
...
}
有一个merged pull request应该解决问题,并计划包含在Docker 1.10.0中.但与此同时,可以使用以下解决方法:
>使用sudo docker network rm删除所有用户创建的网络
>使用sudo service docker stop停止docker守护程序
>用sudo iptables -F&&清理iptables sudo iptables -F -t nat
>使用sudo service docker start重新启动docker守护程序
>重新创建用户定义的网络
>运行容器