You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by in...@apache.org on 2019/04/03 23:11:28 UTC
[hadoop] branch trunk updated: HDFS-14327. Using FQDN instead of IP
to access servers with DNS resolving. Contributed by Fengnan Li.
This is an automated email from the ASF dual-hosted git repository.
inigoiri pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git
The following commit(s) were added to refs/heads/trunk by this push:
new 7b5b783 HDFS-14327. Using FQDN instead of IP to access servers with DNS resolving. Contributed by Fengnan Li.
7b5b783 is described below
commit 7b5b783f66f32012c00bef7593851392dd8cf2d5
Author: Inigo Goiri <in...@apache.org>
AuthorDate: Wed Apr 3 16:11:13 2019 -0700
HDFS-14327. Using FQDN instead of IP to access servers with DNS resolving. Contributed by Fengnan Li.
---
.../apache/hadoop/net/DNSDomainNameResolver.java | 33 ++++++++++++++-
.../org/apache/hadoop/net/DomainNameResolver.java | 23 +++++++++++
.../apache/hadoop/net/MockDomainNameResolver.java | 36 +++++++++++++++--
.../hadoop/hdfs/client/HdfsClientConfigKeys.java | 2 +
.../ha/AbstractNNFailoverProxyProvider.java | 18 +++++----
.../ha/TestConfiguredFailoverProxyProvider.java | 47 ++++++++++++++--------
.../src/main/resources/hdfs-default.xml | 14 +++++++
7 files changed, 145 insertions(+), 28 deletions(-)
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DNSDomainNameResolver.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DNSDomainNameResolver.java
index bb1aa90..5866e29 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DNSDomainNameResolver.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DNSDomainNameResolver.java
@@ -22,8 +22,9 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
/**
- * DNSDomainNameResolver takes one domain name and returns all of the IP
- * addresses from the underlying DNS service.
+ * DNSDomainNameResolver wraps up the default DNS service for forward/reverse
+ * DNS lookup. It also provides a function to resolve a host name to all of
+ * fully qualified domain names belonging to the IPs from this host name
*/
public class DNSDomainNameResolver implements DomainNameResolver {
@Override
@@ -31,4 +32,32 @@ public class DNSDomainNameResolver implements DomainNameResolver {
throws UnknownHostException {
return InetAddress.getAllByName(domainName);
}
+
+ @Override
+ public String getHostnameByIP(InetAddress address) {
+ String host = address.getCanonicalHostName();
+ if (host != null && host.length() != 0
+ && host.charAt(host.length()-1) == '.') {
+ host = host.substring(0, host.length()-1);
+ }
+ return host;
+ }
+
+ @Override
+ public String[] getAllResolvedHostnameByDomainName(
+ String domainName, boolean useFQDN) throws UnknownHostException {
+ InetAddress[] addresses = getAllByDomainName(domainName);
+ String[] hosts = new String[addresses.length];
+ if (useFQDN) {
+ for (int i = 0; i < addresses.length; i++) {
+ hosts[i] = getHostnameByIP(addresses[i]);
+ }
+ } else {
+ for (int i = 0; i < addresses.length; i++) {
+ hosts[i] = addresses[i].getHostAddress();
+ }
+ }
+
+ return hosts;
+ }
}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DomainNameResolver.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DomainNameResolver.java
index 6d2d800..4c44e9d 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DomainNameResolver.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/DomainNameResolver.java
@@ -36,4 +36,27 @@ public interface DomainNameResolver {
*/
InetAddress[] getAllByDomainName(String domainName)
throws UnknownHostException;
+
+ /**
+ * Reverse lookup an IP address and get the fully qualified domain name(fqdn).
+ *
+ * @param address
+ * @return fully qualified domain name
+ */
+ String getHostnameByIP(InetAddress address);
+
+ /**
+ * This function combines getAllByDomainName and getHostnameByIP, for a single
+ * domain name, it will first do a forward lookup to get all of IP addresses,
+ * then for each IP address, it will do a reverse lookup to get the fqdn.
+ * This function is necessary in secure environment since Kerberos uses fqdn
+ * in the service principal instead of IP.
+ *
+ * @param domainName
+ * @return all fully qualified domain names belonging to the IPs resolved from
+ * the input domainName
+ * @throws UnknownHostException
+ */
+ String[] getAllResolvedHostnameByDomainName(
+ String domainName, boolean useFQDN) throws UnknownHostException;
}
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/MockDomainNameResolver.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/MockDomainNameResolver.java
index cb55ae0..aa93709 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/MockDomainNameResolver.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/MockDomainNameResolver.java
@@ -19,14 +19,16 @@ package org.apache.hadoop.net;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import com.google.common.annotations.VisibleForTesting;
/**
- * This mock resolver class returns the predefined resolving results.
- * By default it uses a default "test.foo.bar" domain with two IP addresses.
+ * This mock resolver class returns the predefined resolving/reverse lookup
+ * results. By default it uses a default "test.foo.bar" domain with two
+ * IP addresses.
*/
public class MockDomainNameResolver implements DomainNameResolver {
@@ -37,16 +39,21 @@ public class MockDomainNameResolver implements DomainNameResolver {
public static final byte[] BYTE_ADDR_2 = new byte[]{10, 1, 1, 2};
public static final String ADDR_1 = "10.1.1.1";
public static final String ADDR_2 = "10.1.1.2";
+ public static final String FQDN_1 = "host01.com";
+ public static final String FQDN_2 = "host02.com";
/** Internal mapping of domain names and IP addresses. */
private Map<String, InetAddress[]> addrs = new TreeMap<>();
-
+ /** Internal mapping from IP addresses to fqdns. */
+ private Map<InetAddress, String> ptrMap = new HashMap<>();
public MockDomainNameResolver() {
try {
InetAddress nn1Address = InetAddress.getByAddress(BYTE_ADDR_1);
InetAddress nn2Address = InetAddress.getByAddress(BYTE_ADDR_2);
addrs.put(DOMAIN, new InetAddress[]{nn1Address, nn2Address});
+ ptrMap.put(nn1Address, FQDN_1);
+ ptrMap.put(nn2Address, FQDN_2);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
@@ -61,6 +68,29 @@ public class MockDomainNameResolver implements DomainNameResolver {
return addrs.get(domainName);
}
+ @Override
+ public String getHostnameByIP(InetAddress address) {
+ return ptrMap.containsKey(address) ? ptrMap.get(address) : null;
+ }
+
+ @Override
+ public String[] getAllResolvedHostnameByDomainName(
+ String domainName, boolean useFQDN) throws UnknownHostException {
+ InetAddress[] addresses = getAllByDomainName(domainName);
+ String[] hosts = new String[addresses.length];
+ if (useFQDN) {
+ for (int i = 0; i < hosts.length; i++) {
+ hosts[i] = this.ptrMap.get(addresses[i]);
+ }
+ } else {
+ for (int i = 0; i < hosts.length; i++) {
+ hosts[i] = addresses[i].getHostAddress();
+ }
+ }
+
+ return hosts;
+ }
+
@VisibleForTesting
public void setAddressMap(Map<String, InetAddress[]> addresses) {
this.addrs = addresses;
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/client/HdfsClientConfigKeys.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/client/HdfsClientConfigKeys.java
index 67904ee..1cd9018 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/client/HdfsClientConfigKeys.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/client/HdfsClientConfigKeys.java
@@ -291,6 +291,8 @@ public interface HdfsClientConfigKeys {
String RESOLVE_ADDRESS_NEEDED_KEY = PREFIX + "resolve-needed";
boolean RESOLVE_ADDRESS_NEEDED_DEFAULT = false;
String RESOLVE_SERVICE_KEY = PREFIX + "resolver.impl";
+ String RESOLVE_ADDRESS_TO_FQDN = PREFIX + "useFQDN";
+ boolean RESOLVE_ADDRESS_TO_FQDN_DEFAULT = true;
}
/** dfs.client.write configuration properties */
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/AbstractNNFailoverProxyProvider.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/AbstractNNFailoverProxyProvider.java
index 93452a3..e1e5fd0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/AbstractNNFailoverProxyProvider.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/AbstractNNFailoverProxyProvider.java
@@ -19,7 +19,6 @@
package org.apache.hadoop.hdfs.server.namenode.ha;
import java.io.IOException;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
@@ -180,7 +179,7 @@ public abstract class AbstractNNFailoverProxyProvider<T> implements
Collection<InetSocketAddress> addressesOfNns = addressesInNN.values();
try {
- addressesOfNns = getResolvedAddressesIfNecessary(addressesOfNns, uri);
+ addressesOfNns = getResolvedHostsIfNecessary(addressesOfNns, uri);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -209,7 +208,7 @@ public abstract class AbstractNNFailoverProxyProvider<T> implements
* @return The collection of resolved IP addresses.
* @throws IOException If there are issues resolving the addresses.
*/
- Collection<InetSocketAddress> getResolvedAddressesIfNecessary(
+ Collection<InetSocketAddress> getResolvedHostsIfNecessary(
Collection<InetSocketAddress> addressesOfNns, URI nameNodeUri)
throws IOException {
// 'host' here is usually the ID of the nameservice when address
@@ -223,6 +222,11 @@ public abstract class AbstractNNFailoverProxyProvider<T> implements
// Early return is no resolve is necessary
return addressesOfNns;
}
+ // decide whether to access server by IP or by host name
+ String useFQDNKeyWithHost =
+ HdfsClientConfigKeys.Failover.RESOLVE_ADDRESS_TO_FQDN + "." + host;
+ boolean requireFQDN = conf.getBoolean(useFQDNKeyWithHost,
+ HdfsClientConfigKeys.Failover.RESOLVE_ADDRESS_TO_FQDN_DEFAULT);
Collection<InetSocketAddress> addressOfResolvedNns = new ArrayList<>();
DomainNameResolver dnr = DomainNameResolverFactory.newInstance(
@@ -232,12 +236,12 @@ public abstract class AbstractNNFailoverProxyProvider<T> implements
LOG.info("Namenode domain name will be resolved with {}",
dnr.getClass().getName());
for (InetSocketAddress address : addressesOfNns) {
- InetAddress[] resolvedAddresses = dnr.getAllByDomainName(
- address.getHostName());
+ String[] resolvedHostNames = dnr.getAllResolvedHostnameByDomainName(
+ address.getHostName(), requireFQDN);
int port = address.getPort();
- for (InetAddress raddress : resolvedAddresses) {
+ for (String hostname : resolvedHostNames) {
InetSocketAddress resolvedAddress = new InetSocketAddress(
- raddress, port);
+ hostname, port);
addressOfResolvedNns.add(resolvedAddress);
}
}
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConfiguredFailoverProxyProvider.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConfiguredFailoverProxyProvider.java
index 5892246..e3f34e3 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConfiguredFailoverProxyProvider.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConfiguredFailoverProxyProvider.java
@@ -142,7 +142,8 @@ public class TestConfiguredFailoverProxyProvider {
* Add more DNS related settings to the passed in configuration.
* @param config Configuration file to add settings to.
*/
- private void addDNSSettings(Configuration config, boolean hostResolvable) {
+ private void addDNSSettings(Configuration config,
+ boolean hostResolvable, boolean useFQDN) {
config.set(
HdfsClientConfigKeys.DFS_HA_NAMENODES_KEY_PREFIX + "." + ns3, "nn");
String domain = hostResolvable
@@ -163,6 +164,10 @@ public class TestConfiguredFailoverProxyProvider {
config.setBoolean(
HdfsClientConfigKeys.Failover.RANDOM_ORDER + "." + ns3,
true);
+ config.setBoolean(
+ HdfsClientConfigKeys.Failover.RESOLVE_ADDRESS_TO_FQDN + "." + ns3,
+ useFQDN
+ );
}
/**
@@ -250,17 +255,18 @@ public class TestConfiguredFailoverProxyProvider {
nn1Count.get() + nn2Count.get() + nn3Count.get());
}
- @Test
- public void testResolveDomainNameUsingDNS() throws Exception {
+ private void testResolveDomainNameUsingDNS(boolean useFQDN) throws Exception {
Configuration dnsConf = new Configuration(conf);
- addDNSSettings(dnsConf, true);
+ addDNSSettings(dnsConf, true, useFQDN);
// Mock ClientProtocol
Map<InetSocketAddress, ClientProtocol> proxyMap = new HashMap<>();
final AtomicInteger nn1Count = addClientMock(
- MockDomainNameResolver.BYTE_ADDR_1, proxyMap);
+ useFQDN ? MockDomainNameResolver.FQDN_1 : MockDomainNameResolver.ADDR_1,
+ proxyMap);
final AtomicInteger nn2Count = addClientMock(
- MockDomainNameResolver.BYTE_ADDR_2, proxyMap);
+ useFQDN ? MockDomainNameResolver.FQDN_2 : MockDomainNameResolver.ADDR_2,
+ proxyMap);
// Get a client multiple times
final Map<String, AtomicInteger> proxyResults = new HashMap<>();
@@ -280,16 +286,18 @@ public class TestConfiguredFailoverProxyProvider {
proxy.getStats();
}
+ String resolvedHost1 = useFQDN ?
+ MockDomainNameResolver.FQDN_1 : "/" + MockDomainNameResolver.ADDR_1;
+ String resolvedHost2 = useFQDN ?
+ MockDomainNameResolver.FQDN_2 : "/" + MockDomainNameResolver.ADDR_2;
// Check we got the proper addresses
assertEquals(2, proxyResults.size());
assertTrue(
"nn1 wasn't returned: " + proxyResults,
- proxyResults.containsKey(
- "/" + MockDomainNameResolver.ADDR_1 + ":8020"));
+ proxyResults.containsKey(resolvedHost1 + ":8020"));
assertTrue(
"nn2 wasn't returned: " + proxyResults,
- proxyResults.containsKey(
- "/" + MockDomainNameResolver.ADDR_2 + ":8020"));
+ proxyResults.containsKey(resolvedHost2 + ":8020"));
// Check that the Namenodes were invoked
assertEquals(NUM_ITERATIONS, nn1Count.get() + nn2Count.get());
@@ -305,9 +313,17 @@ public class TestConfiguredFailoverProxyProvider {
}
@Test
+ public void testResolveDomainNameUsingDNS() throws Exception {
+ // test resolving to IP
+ testResolveDomainNameUsingDNS(false);
+ // test resolving to FQDN
+ testResolveDomainNameUsingDNS(true);
+ }
+
+ @Test
public void testResolveDomainNameUsingDNSUnknownHost() throws Exception {
Configuration dnsConf = new Configuration(conf);
- addDNSSettings(dnsConf, false);
+ addDNSSettings(dnsConf, false, false);
Map<InetSocketAddress, ClientProtocol> proxyMap = new HashMap<>();
exception.expect(RuntimeException.class);
@@ -321,19 +337,18 @@ public class TestConfiguredFailoverProxyProvider {
/**
* Add a ClientProtocol mock for the proxy.
- * @param addr IP address for the destination.
+ * @param host host name for the destination.
* @param proxyMap Map containing the client for each target address.
* @return The counter for the number of calls to this target.
* @throws Exception If the client cannot be created.
*/
private AtomicInteger addClientMock(
- byte[] addr, Map<InetSocketAddress, ClientProtocol> proxyMap)
- throws Exception {
+ String host, Map<InetSocketAddress, ClientProtocol> proxyMap)
+ throws Exception {
final AtomicInteger counter = new AtomicInteger(0);
- InetAddress inetAddr = InetAddress.getByAddress(addr);
InetSocketAddress inetSockerAddr =
- new InetSocketAddress(inetAddr, rpcPort);
+ new InetSocketAddress(host, rpcPort);
final ClientProtocol cpMock = mock(ClientProtocol.class);
when(cpMock.getStats()).thenAnswer(createAnswer(counter, 1));
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml b/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml
index 8a8b55d..bf4e7b9 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml
@@ -3799,6 +3799,20 @@
</property>
<property>
+ <name>dfs.client.failover.resolver.useFQDN</name>
+ <value>true</value>
+ <description>
+ Determines whether the resolved result is fully qualified domain name instead
+ of pure IP address(es). The config name can be extended with an optional
+ nameservice ID (of form dfs.client.failover.resolver.impl[.nameservice]) to
+ configure specific nameservices when multiple nameservices exist.
+ In secure environment, this has to be enabled since Kerberos is using fqdn
+ in machine's principal therefore accessing servers by IP won't be recognized
+ by the KDC.
+ </description>
+</property>
+
+<property>
<name>dfs.client.key.provider.cache.expiry</name>
<value>864000000</value>
<description>
---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org