今天是周五, 明天周末, 晚上正好有时间倒腾下hbase, 在笔记本上安装了虚拟机, 并安装了 ubuntu-11.04-server-i386.iso 系统, 下载了jdk-7-linux-i586.tar.gz, hadoop-0.20.203.0rc1.tar.gz, hbase-0.90.4.tar.gz, 接着安装jdk, 配置环境变量, 准备就绪后开始启动hadoop, 正常, 接着启动hbase, 发现报错, 查看日志, 大意是hbase下的hadoop版本与运行中的hadoop版本不同

       OK, 来检查下, hbase_home/lib下的hadoop版本是 hadoop-core-0.20-append-r1056497.jar, hadoop-0.20.203 下的为hadoop-core-0.20.203.0.jar.  将hbase_home/lib下的hadoop版本替换为hadoop-core-0.20.203.0.jar, 重新启动hbase, 发现还是有错误, 大意是某些类找不到, 这个好解决, 在hbase启动脚本中把hadoop下的jar包设置到classpath路径, 重启后错误解决, 可以建表和查询了。

   因为ubuntu-11.04-server-i386 没有界面, 我的eclipse安装在本地windows系统中, 需要通过虚拟机连接hbase

查看下虚拟机IP地址:

yinjie@ubuntu:~/soft/hadoop-0.20.203.0/bin$ ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0c:29:bd:62:7b 
          inet addr:192.168.203.129  Bcast:192.168.203.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:febd:627b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:300269 errors:0 dropped:0 overruns:0 frame:0
          TX packets:78941 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:417200572 (417.2 MB)  TX bytes:7264996 (7.2 MB)
          Interrupt:19 Base address:0x2000
 

 ip是 192.168.203.129  
创建一张表试试:

 
  
  1.  Configuration HBASE_CONFIG = new Configuration();  
  2.   HBASE_CONFIG.set("hbase.zookeeper.quorum", "192.168.203.129");  
  3.   HBASE_CONFIG.set("hbase.zookeeper.property.clientPort", "2181");  
  4.   cfg = new HBaseConfiguration(HBASE_CONFIG);  
  5.  
  6. /**  
  7.   * 创建一张表  
  8.   */  
  9.  public static void creatTable(String tablename) throws Exception {  
  10.   HBaseAdmin admin = new HBaseAdmin(cfg);  
  11.   if (admin.tableExists(tablename)) {  
  12.    System.out.println("table   Exists!!!");  
  13.   } else {  
  14.    HTableDescriptor tableDesc = new HTableDescriptor(tablename);  
  15.    HColumnDescriptor hColumnDescriptor=null;  
  16.    hColumnDescriptor = new HColumnDescriptor("f1");  
  17.    hColumnDescriptor.setMaxVersions(1);  
  18.    tableDesc.addFamily(hColumnDescriptor);  
  19.      
  20.    hColumnDescriptor = new HColumnDescriptor("f2");  
  21.    hColumnDescriptor.setMaxVersions(2);  
  22.    tableDesc.addFamily(hColumnDescriptor);  
  23.      
  24.    hColumnDescriptor = new HColumnDescriptor("f3");  
  25.    hColumnDescriptor.setMaxVersions(3);  
  26.    tableDesc.addFamily(hColumnDescriptor);  
  27.    admin.createTable(tableDesc);  
  28.    System.out.println("create table ok .");  
  29.   }  
  30.  }  
  31.  
  32.  public static void main(String[] args) {  
  33.   try {  
  34.    String tablename = "t1";  
  35.    Main.creatTable(tablename);  
  36.   } catch (Exception e) {  
  37.    e.printStackTrace();  
  38.   }  
  39.  

执行后发现总是异常
查看zookeeper连接是否正常: 192.168.203.129:2181
zkCli -server 192.168.203.129:2181

 
  
  1. [zk: 192.168.203.129:2181(CONNECTED) 5] ls /  
  2. [hbase, zookeeper]  
  3. [zk: 192.168.203.129:2181(CONNECTED) 6] ls /hbase  
  4. [root-region-server, rs, table, unassigned, master, shutdown]  
  5. [zk: 192.168.203.129:2181(CONNECTED) 7]  
  6. [zk: 192.168.203.129:2181(CONNECTED) 7]  
  7. [zk: 192.168.203.129:2181(CONNECTED) 7] get /hbase/master  
  8. localhost:60000  
  9. cZxid = 0x15 
  10. ctime = Sat Aug 27 19:43:27 CST 2011  
  11. mZxid = 0x15 
  12. mtime = Sat Aug 27 19:43:27 CST 2011  
  13. pZxid = 0x15 
  14. cversion = 0 
  15. dataVersion = 0 
  16. aclVersion = 0 
  17. ephemeralOwner = 0x1320b0d2e2f0000 
  18. dataLength = 12 
  19. numChildren = 0 

     问题很明显, 通过zookeeper拿到的hmaster地址是 localhost:60000, 这样当然就连接不上hmaster了
接下去看看 hbase中的默认配置文件hbase-default.xml
挖几个配置项出来, 在hbase-site.xml中重新设置:

 
  
  1. <property> 
  2.     <name>hbase.master.dns.interface</name> 
  3.     <value>eth0</value> 
  4.     <description>The name of the Network Interface from which a master  
  5.       should report its IP address.  
  6.     </description> 
  7.   </property> 
  8.   <property> 
  9.     <name>hbase.master.dns.nameserver</name> 
  10.     <value>192.168.203.129</value> 
  11.     <description>The host name or IP address of the name server (DNS)  
  12.       which a master should use to determine the host name used  
  13.       for communication and display purposes.  
  14.     </description> 
  15.   </property> 
  16. <property> 
  17.     <name>hbase.regionserver.dns.interface</name> 
  18.     <value>eth0</value> 
  19.     <description>The name of the Network Interface from which a region server  
  20.       should report its IP address.  
  21.     </description> 
  22.   </property> 
  23.   <property> 
  24.     <name>hbase.regionserver.dns.nameserver</name> 
  25.     <value>192.168.203.129</value> 
  26.     <description>The host name or IP address of the name server (DNS)  
  27.       which a region server should use to determine the host name used by the  
  28.       master for communication and display purposes.  
  29.     </description> 
  30.   </property> 

上面明确指定了使用192.168.203.129地址. 以为这样就没问题了, 但事实上zookeeper里面注册的hmaster 地址仍旧是localhost, 为什么会返回localhost而不是192.168.203.129, 真的有点沉不住气了, 那就直接翻源码吧。
翻源码过程就不说了, 截几个重要的函数来看下:
在HMaster.java中有一个函数:

 
  
  1. /*  
  2.  * @return This masters' address.  
  3.  * @throws UnknownHostException  
  4.  */  
  5. private static String getMyAddress(final Configuration c)  
  6. throws UnknownHostException {  
  7.   // Find out our address up in DNS.  
  8.   String s = Strings.domainNamePointerToHostName(DNS.getDefaultHost(c.get(  
  9.       "hbase.master.dns.interface", "default"), c.get(  
  10.       "hbase.master.dns.nameserver", "default")));  
  11.   s += ":" + c.get(HConstants.MASTER_PORT,  
  12.       Integer.toString(HConstants.DEFAULT_MASTER_PORT));  
  13.   return s;  

来看看DNS.getDefaultHost方法

 
  
  1. public static String getDefaultHost(String strInterface, String nameserver)  
  2.     throws UnknownHostException {  
  3.     if (strInterface.equals("default"))   
  4.       return InetAddress.getLocalHost().getCanonicalHostName();  
  5.  
  6.     if (nameserver != null && nameserver.equals("default"))  
  7.       return getDefaultHost(strInterface);  
  8.  
  9.     String[] hosts = getHosts(strInterface, nameserver);  
  10.     return hosts[0];  
  11.   }  
  12.  

 
  
  1. public static String[] getHosts(String strInterface, String nameserver)  
  2.     throws UnknownHostException {  
  3.     String[] ips = getIPs(strInterface);  
  4.     Vector<String> hosts = new Vector<String>();  
  5.     for (int ctr = 0; ctr < ips.length; ctr++)  
  6.       try {  
  7.         hosts.add(reverseDns(InetAddress.getByName(ips[ctr]),  
  8.                              nameserver));  
  9.       } catch (Exception e) {  
  10.       }  
  11.  
  12.     if (hosts.size() == 0)  // 为空,返回localhost  
  13.       return new String[] { InetAddress.getLocalHost().getCanonicalHostName() };  
  14.     else  
  15.       return hosts.toArray(new String[] {});  
  16.   }  
  17.  

步步跟踪,最后来到:

 
  
  1. public static String reverseDns(InetAddress hostIp, String ns)  
  2.     throws NamingException {  
  3.     //  
  4.     // Builds the reverse IP lookup form  
  5.     // This is formed by reversing the IP numbers and appending in-addr.arpa  
  6.     //  
  7.     String[] parts = hostIp.getHostAddress().split("\\.");  
  8.     String reverseIP = parts[3] + "." + parts[2] + "." + parts[1] + "."  
  9.       + parts[0] + ".in-addr.arpa";  
  10.  
  11.     DirContext ictx = new InitialDirContext();  
  12.     Attributes attribute =  
  13.       ictx.getAttributes("dns://"               // Use "dns:///" if the default  
  14.                          + ((ns == null) ? "" : ns) +   
  15.                          // nameserver is to be used  
  16.                          "/" + reverseIP, new String[] { "PTR" });  
  17.     ictx.close();  
  18.       
  19.     return attribute.get("PTR").get().toString();  
  20.   }  


从这个函数可以看出,hbase会根据ip来得到域名, 如果中间有异常则在 getHosts函数中返回localhost既然是ip->域名反向查找过程有问题, 那就来看看DNS服务吧

 
  
  1. yinjie@ubuntu:~/soft/hadoop-0.20.203.0/bin$ nslookup  
  2. > 192.168.203.129  
  3. Server:         192.168.203.2  
  4. Address:        192.168.203.2#53  
  5.  
  6. ** server can't find 129.203.168.192.in-addr.arpa.: NXDOMAIN  

问题很明显, 是192.168.203.129这个IP没有被DNS反向解析成域名, 也就是说192.168.203.129在dns里找不到对应的域名

真是一路坎坷啊, 那就来安装一个DNS

 
  
  1. yinjie@ubuntu:~/soft/hbase-0.90.4/conf$ sudo apt-get install bind9 

安装完毕后来查看下/etc/bind下有哪些文件

 
  
  1. yinjie@ubuntu:~/soft/hbase-0.90.4/conf$ cd /etc/bind  
  2. yinjie@ubuntu:/etc/bind$ ls  
  3. bind.keys  db.0  db.127  db.255  db.empty  db.local  db.root   named.conf  named.conf.default-zones  named.conf.local  named.conf.options  rndc.key  zones.rfc1918  
  4. yinjie@ubuntu:/etc/bind$  

接着配置 192.168.203.129 IP地址与域名的对应关系(这里把域名设置成yinjie):
照样画葫芦,创建db.129 文件:
vi  db.129 输入:

 
  
  1. ;  
  2. ; BIND reverse data file  
  3. ;  
  4. $TTL    604800  
  5. @       IN      SOA     yinjie. root.localhost. (  
  6.                               1         ; Serial  
  7.                          604800         ; Refresh  
  8.                           86400         ; Retry  
  9.                         2419200         ; Expire  
  10.                          604800 )       ; Negative Cache TTL  
  11. ;  
  12. @       IN      NS      yinjie.  
  13. 129.203.168     IN      PTR     yinjie. 

创建db.yinjie文件
vi db.yinjie 输入:

 
  
  1. ;  
  2. ; BIND data file   
  3. ;  
  4. $TTL    604800  
  5. @       IN      SOA     yinjie. root.localhost. (  
  6.                               2         ; Serial  
  7.                          604800         ; Refresh  
  8.                           86400         ; Retry  
  9.                         2419200         ; Expire  
  10.                          604800 )       ; Negative Cache TTL  
  11. ;  
  12. @       IN      NS      yinjie.  
  13. @       IN      A       192.168.203.129  
  14. @       IN      AAAA    ::1  

然后编辑: named.conf.default-zones 添加:

 
  
  1. zone "yinjie" {  
  2.         type master;  
  3.         file "/etc/bind/db.yinjie";  
  4. };  
  5.  
  6. zone "192.in-addr.arpa" {  
  7.         type master;  
  8.         file "/etc/bind/db.129";  
  9. };  
  10.  

这样 192.168.203.129就与域名yinjie关联起来了, 通过yinjie可以解析出地址 192.168.203.129, 通过地址192.168.203.129可以解析出域名yinjie

重启一下DNS服务, 使配置生效:

 
  
  1. yinjie@ubuntu:/etc/bind$ sudo /etc/init.d/bind9 restart 

好了, 来验证下:

 
  
  1. yinjie@ubuntu:~/soft/hadoop-0.20.203.0/bin$ nslookup  
  2. > 192.168.203.129  
  3. Server:         192.168.203.129  
  4. Address:        192.168.203.129#53  
  5.  
  6. 129.203.168.192.in-addr.arpa    name = yinjie.  
  7. >   
  8. >   
  9. > yinjie  
  10. Server:         192.168.203.129  
  11. Address:        192.168.203.129#53  
  12.  
  13. Name:   yinjie  
  14. Address: 192.168.203.129  
  15. >   
  16. >   

从上面可以看出, 输入192.168.203.129返回的域名确实是yinjie, 而通过域名yinjie可以得到IP地址192.168.203.129好了,DNS已经解决, 成功已经接近, 不过最后还要做些配置上的改动
修改hadoop配置core-site.xml

 
  
  1. <property> 
  2.   <name>fs.default.name</name> 
  3.   <value>hdfs://yinjie:9090</value> 
  4.   <description>The name of the default file system.  A URI whose  
  5.   scheme and authority determine the FileSystem implementation.  The  
  6.   uri's scheme determines the config property (fs.SCHEME.impl) naming  
  7.   the FileSystem implementation class.  The uri's authority is used to  
  8.   determine the host, port, etc. for a filesystem.</description> 
  9. </property> 

修改hadoop配置mapred-site.xml

 
  
  1. <property> 
  2.   <name>mapred.job.tracker</name> 
  3.   <value>yinjie:9999</value> 
  4.   <description>The host and port that the MapReduce job tracker runs  
  5.   at.  If "local", then jobs are run in-process as a single map  
  6.   and reduce task.  
  7.   </description> 
  8. </property> 

 修改hbase配置 hbase-site.xml

 
  
  1. <property> 
  2.     <name>hbase.master.dns.interface</name> 
  3.     <value>eth0</value> 
  4.     <description>The name of the Network Interface from which a master  
  5.       should report its IP address.  
  6.     </description> 
  7.   </property> 
  8.   <property> 
  9.     <name>hbase.master.dns.nameserver</name> 
  10.     <value>192.168.203.129</value> 
  11.     <description>The host name or IP address of the name server (DNS)  
  12.       which a master should use to determine the host name used  
  13.       for communication and display purposes.  
  14.     </description> 
  15.   </property> 
  16. <property> 
  17.     <name>hbase.regionserver.dns.interface</name> 
  18.     <value>eth0</value> 
  19.     <description>The name of the Network Interface from which a region server  
  20.       should report its IP address.  
  21.     </description> 
  22.   </property> 
  23.   <property> 
  24.     <name>hbase.regionserver.dns.nameserver</name> 
  25.     <value>192.168.203.129</value> 
  26.     <description>The host name or IP address of the name server (DNS)  
  27.       which a region server should use to determine the host name used by the  
  28.       master for communication and display purposes.  
  29.     </description> 
  30.   </property> 

最后修改/etc/hosts文件
增加:
192.168.203.129 yinjie

OK了, 启动hadoop, hbase, 然后在windows下运行创建表程序, 部分日志打印如下:
11/08/27 19:45:18 INFO zookeeper.ZooKeeper: Client environment:user.name=Administrator
11/08/27 19:45:18 INFO zookeeper.ZooKeeper: Client environment:user.home=C:\Documents and Settings\Administrator
11/08/27 19:45:18 INFO zookeeper.ZooKeeper: Client environment:user.dir=D:\Development\Source\workspace\hbase-test
11/08/27 19:45:18 INFO zookeeper.ZooKeeper: Initiating client connection, connectString=yinjie:2181 sessionTimeout=180000 watcher=hconnection
11/08/27 19:45:19 INFO zookeeper.ClientCnxn: Opening socket connection to server yinjie/192.168.203.129:2181
11/08/27 19:45:19 INFO zookeeper.ClientCnxn: Socket connection established to yinjie/192.168.203.129:2181, initiating session
11/08/27 19:45:19 INFO zookeeper.ClientCnxn: Session establishment complete on server yinjie/192.168.203.129:2181, sessionid = 0x1320b0d2e2f0005, negotiated timeout = 180000
create table ok .

成功!!!
HADOOP-5339.doc 附件是问题解决过程中找到的一些资料, 是关于
reverse DNS doesnt resolve local loop address DNS解析问题