为什么 Dockerized Hadoop datanode 注册了错误的 IP 地址?
Why is Dockerized Hadoop datanode registering with the wrong IP address?
我有单独的 Docker (1.9.1) Hadoop (2.7.1) 名称节点和数据节点图像。我可以从这些创建容器并让它们通过用户定义的 Docker 网络进行通信。但是,数据节点似乎报告自己具有网络网关的 IP 地址,而不是它自己的 IP 地址。虽然这不会对单个数据节点造成任何问题,但在添加其他数据节点时会造成混乱。它们都使用相同的 IP 地址注册,名称节点在它们之间翻转,只报告单个数据节点处于活动状态。
当 运行 通过用户定义的 Docker 网络时,为什么服务器(名称节点)从客户端(数据节点)套接字连接读取错误的 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
开头的容器中执行相同的命令会错误地打印出 gateway/user-defined 网络接口的 IP 地址:
Connection from 172.18.0.1 port 9000 [tcp/9000] accepted
HDFS/Docker详情
每个数据节点的 hdfs-site.xml
文件中的 dfs.datanode.address
属性 设置为其主机名(例如,hdfs-datanode-1
)。
网络是这样创建的:
sudo docker network create --driver bridge hadoop-network
名称节点是这样开始的:
sudo docker run -d \
--name hdfs-namenode \
-v /hdfs/name:/hdfs-name \
--net=hadoop-network \
--hostname hdfs-namenode \
-p 50070:50070 \
hadoop:namenode
数据节点是这样启动的:
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
然而,当数据节点启动时,名称节点将数据节点的 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, 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, processing time: 3 msecs
并使用 tcpdump
捕获两者之间的流量(运行 在连接到主机网络的 Docker 容器中 - 使用 docker run --net=host
)似乎显示发生错误(br-b59d498905c5
是 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.abaf40c-
...
但在随后的调用中它是不正确的。例如在 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.abaf40c-4ce
...
通过datanode代码调试,根据namenode返回的信息,清楚的看到datanode registration details are updated in BPServiceActor.register()
时出现的错误:
bpRegistration = bpNamenode.registerDatanode(bpRegistration);
调试名称节点 shows 它从数据节点套接字连接中读取 不正确的 IP 地址并更新数据节点注册详细信息。
补充说明
我可以通过用户定义的 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);
}
}
Server
和 Client
运行 在 172.18.0.2
上正确输出 172.18.0.2
但在 Client
运行 上172.18.0.3
它错误地输出 172.18.0.1
.
运行 没有使用用户定义网络的相同代码(在默认 bridge
网络/docker0
接口和公开端口 9000
上)给出了正确的输出.
我在名称节点的 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. These all contain DatanodeRegistrationProto
as their first data member. This message is defined in here 看起来像这样:
/**
* Identifies a Datanode
*/
message DatanodeIDProto {
required string ipAddr = 1; // IP address
required string hostName = 2; // hostname
...
}
这是由 known docker issue (I also raised - and closed - this duplicate 引起的,它描述了问题中列出的步骤)。
有一个 merged pull request 应该可以解决这个问题,并计划包含在 Docker 1.10.0 中。但与此同时,可以使用以下解决方法:
- 使用
sudo docker network rm
删除所有用户创建的网络
- 用
sudo service docker stop
停止 docker 守护进程
- 使用
sudo iptables -F && sudo iptables -F -t nat
清理 iptables
- 使用
sudo service docker start
重新启动 docker 守护进程
- 重新创建用户定义的网络
- 运行 容器
我有单独的 Docker (1.9.1) Hadoop (2.7.1) 名称节点和数据节点图像。我可以从这些创建容器并让它们通过用户定义的 Docker 网络进行通信。但是,数据节点似乎报告自己具有网络网关的 IP 地址,而不是它自己的 IP 地址。虽然这不会对单个数据节点造成任何问题,但在添加其他数据节点时会造成混乱。它们都使用相同的 IP 地址注册,名称节点在它们之间翻转,只报告单个数据节点处于活动状态。
当 运行 通过用户定义的 Docker 网络时,为什么服务器(名称节点)从客户端(数据节点)套接字连接读取错误的 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
开头的容器中执行相同的命令会错误地打印出 gateway/user-defined 网络接口的 IP 地址:
Connection from 172.18.0.1 port 9000 [tcp/9000] accepted
HDFS/Docker详情
每个数据节点的 hdfs-site.xml
文件中的 dfs.datanode.address
属性 设置为其主机名(例如,hdfs-datanode-1
)。
网络是这样创建的:
sudo docker network create --driver bridge hadoop-network
名称节点是这样开始的:
sudo docker run -d \
--name hdfs-namenode \
-v /hdfs/name:/hdfs-name \
--net=hadoop-network \
--hostname hdfs-namenode \
-p 50070:50070 \
hadoop:namenode
数据节点是这样启动的:
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
然而,当数据节点启动时,名称节点将数据节点的 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, 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, processing time: 3 msecs
并使用 tcpdump
捕获两者之间的流量(运行 在连接到主机网络的 Docker 容器中 - 使用 docker run --net=host
)似乎显示发生错误(br-b59d498905c5
是 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.abaf40c- ...
但在随后的调用中它是不正确的。例如在 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.abaf40c-4ce ...
通过datanode代码调试,根据namenode返回的信息,清楚的看到datanode registration details are updated in BPServiceActor.register()
时出现的错误:
bpRegistration = bpNamenode.registerDatanode(bpRegistration);
调试名称节点 shows 它从数据节点套接字连接中读取 不正确的 IP 地址并更新数据节点注册详细信息。
补充说明
我可以通过用户定义的 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);
}
}
Server
和 Client
运行 在 172.18.0.2
上正确输出 172.18.0.2
但在 Client
运行 上172.18.0.3
它错误地输出 172.18.0.1
.
运行 没有使用用户定义网络的相同代码(在默认 bridge
网络/docker0
接口和公开端口 9000
上)给出了正确的输出.
我在名称节点的 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. These all contain DatanodeRegistrationProto
as their first data member. This message is defined in here 看起来像这样:
/**
* Identifies a Datanode
*/
message DatanodeIDProto {
required string ipAddr = 1; // IP address
required string hostName = 2; // hostname
...
}
这是由 known docker issue (I also raised - and closed - this duplicate 引起的,它描述了问题中列出的步骤)。
有一个 merged pull request 应该可以解决这个问题,并计划包含在 Docker 1.10.0 中。但与此同时,可以使用以下解决方法:
- 使用
sudo docker network rm
删除所有用户创建的网络
- 用
sudo service docker stop
停止 docker 守护进程
- 使用
sudo iptables -F && sudo iptables -F -t nat
清理 iptables
- 使用
sudo service docker start
重新启动 docker 守护进程
- 重新创建用户定义的网络
- 运行 容器