Warum registriert sich der Dockerized Hadoop-Datenknoten mit der falschen IP-Adresse?

Ich habe separate Docker (1.9.1) Images für Hadoop (2.7.1) -Namen- und -Datenknoten. Ich kann daraus Container erstellen und diese über ein benutzerdefiniertes Docker-Netzwerk kommunizieren lassen. Der Datenknoten scheint sich jedoch als IP-Adresse des Netzwerk-Gateways und nicht als eigene IP-Adresse zu melden. Während dies keine Probleme mit einem einzelnen Datenknoten verursacht, herrscht Verwirrung, wenn zusätzliche Datenknoten hinzugefügt werden. Sie alle registrieren sich mit derselben IP-Adresse, und der Namensknoten wechselt zwischen ihnen und meldet immer nur, dass ein einzelner Datenknoten aktiv ist.

Warum liest der Server (namenode) die falsche IP-Adresse von der Client-Socket-Verbindung (datanode), wenn er über ein benutzerdefiniertes Docker-Netzwerk ausgeführt wird, und wie kann ich dies beheben?

Update: Dieses Problem scheint auf der Docker-Seite zu liegen

Running zwei Container mit--net=bridge und Ausführen eines Netcat-Servers:

nc -v -l 9000

in einem Container und einem Netcat-Client im anderen:

nc 172.17.0.2 9000

bewirkt, dass der erste Container korrekt gedruckt wird:

Connection from 172.17.0.3 port 9000 [tcp/9000] accepted

Aber ein benutzerdefiniertes Netzwerk erstellen:

sudo docker network create --driver bridge test

und Ausführen der gleichen Befehle in Containern, die mit @ gestartet wurd--net=test druckt die IP-Adresse des Gateways / der benutzerdefinierten Netzwerkschnittstelle falsch aus:

Connection from 172.18.0.1 port 9000 [tcp/9000] accepted

HDFS / Docker Details

Dasdfs.datanode.address Eigenschaft in jedem @ des Datenknotehdfs-site.xmlie Datei @ wird auf ihren Hostnamen gesetzt (z. B.hdfs-datanode-1).

Das Netzwerk wird folgendermaßen erstellt:

sudo docker network create --driver bridge hadoop-network

Der namenode hat so angefangen:

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

Und der Datenknoten begann folgendermaßen:

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

Die beiden Knoten verbinden sich gut und wenn abgefragt wird (mitsudo docker exec hdfs-namenode hdfs dfsadmin -report) Die Konnektivität wird gemeldet als:

...
Live datanodes (1):

Name: 172.18.0.1:50010 (172.18.0.1)
Hostname: hdfs-datanode-1
...

Die Ausgabe von running:

 sudo docker exec hdfs-namenode cat /etc/hosts

Zeigt an, dass dieser namenode denkt, dass er auf @ läu172.18.0.2 und der Datenknoten läuft auf172.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

Und das Äquivalent auf dem Datenknoten zeigt dasselbe:

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

Laufenip route an beiden bestätigt dies:

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

Und dennoch meldet der Namensknoten beim Start des Datenknotens die IP-Adresse des Datenknotens als172.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

Und mittcpdump, um den Datenverkehr zwischen den beiden zu erfassen (wird in einem Docker-Container ausgeführt, der mit dem Host-Netzwerk verbunden ist - mitdocker run --net=host) scheint den aufgetretenen Fehler anzuzeigen br-b59d498905c5 ist der Name der Netzwerkschnittstelle, die Docker für das @ erstellt hahadoop-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)"

Die IP-Adresse scheint innerhalb des @ korrekt gesendet zu werdregisterDatanode Anruf

...
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-
    ...

Aber in nachfolgenden Aufrufen ist es falsch. Zum Beispiel imsendHeartbeat rufe danach einen Sekundenbruchteil an:

...
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
    ...

Debugging durch den Datencode zeigt deutlich den Fehler, der auftritt, wenn dasdatanode Registrierungsdetails werden in @ aktualisierBPServiceActor.register() basierend auf den vom namenode zurückgegebenen Informationen:

bpRegistration = bpNamenode.registerDatanode(bpRegistration);

Debugging the namenodezeigt a dass es das @ liefalsc IP-Adresse von der Datenknoten-Socket-Verbindung und aktualisiert die Datenknoten-Registrierungsdetails.

Zusätzliche Bemerkunge

Ich kann das Problem mit diesem Code reproduzieren, der über ein benutzerdefiniertes Docker-Netzwerk ausgeführt wird:

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());
    }
}

un

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);
    }
}

Mit beidenServer undClient Laufen auf172.18.0.2 dies gibt richtig @ a172.18.0.2 aber mitClient Laufen auf172.18.0.3 es gibt falsch172.18.0.1.

Den gleichen Code ausführen, ohne ein benutzerdefiniertes Netzwerk zu verwenden (standardmäßigbridge network /docker0 interface und Port freigeben9000) gibt die richtige Ausgabe aus.

Ich habedfs.namenode.datanode.registration.ip-hostname-check Eigenschaft auf @ gesetfalse im @ des namenodhdfs-site.xml -Datei, um Reverse-DNS-Lookup-Fehler zu vermeiden. Dies könnte in Zukunft unnötig sein, wenn ich DNS zum Laufen bringe, aber im Moment, wenn die Datenknoten die falsche IP-Adresse melden, würde es wahrscheinlich nicht helfen, DNS zum Laufen zu bringen.

Ich glaube, die relevanten Kabelprotokolle fürregisterDatanode, sendHeartbeat undblockReport sindRegisterDatanodeRequestProto, HeartbeatRequestProto undBlockReportRequestProto undihre Definitionen finden Sie hier. Diese enthalten alleDatanodeRegistrationProto als erstes Datenelement. Diese Nachricht ist hier definiert und sieht so aus:

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

Antworten auf die Frage(2)

Ihre Antwort auf die Frage