一个有趣的federation部署

最近碰到一个有意思的问题。QA需要3个集群用于测试,但只有5台机器,应该怎么搞?

常规的想法就是换端口、换dfs.[name|data].dir,在同一台机器上跑多个集群的daemon了。
这应该是可行的,虽然我没试过。
而且这批机器配置比较烂,跑多个daemon可能很慢。

突发奇想,是否可以用federation模拟多个hdfs。对federation而言,其实各个NN都是独立的。
只是一个DN会为多个NN服务,分多个BlockPool。
YARN先不考虑,用同一套就好了。

我们以前也测试过federation,那时用了4台机器,部署2个HA的NN。如果采用同样的方式,模拟3个hdfs要至少6台机器。
如果不开启HA,倒是3台机器就可以。但QA也需要测试一些HA相关的东西。

又一次突发奇想,在一个federation的集群中,是否可以同时存在HA和非HA的NN?这样我就可以部署一个HA的NN,2个非HA的NN,用4台机器模拟3个hdfs。

经测试,这是可行的。记录下部署过程和一些问题。
简单起见,以部署1个HA的NN+1个非HA的NN为例。

修改配置文件

角色:inspur116是一个非HA的NN;inspur122和inspur129组成一对HA的NN,其中inspur122 Active,inspur129 StandBy。

只要修改core-site.xml和hdfs-site.xml即可,其他配置都和正常的一样。

core-site.xml中注释掉fs.defaultFS属性。因为在federation中有多个NN,使用哪个NN应该由客户端选择,而不要在服务端写死。即使是“普通”的federation,也务必要取消这个属性。

hdfs-site.xml中修改一些属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<property>
<name>dfs.nameservices</name>
<value>hp1,hp2</value>
</property>
<property>
<name>dfs.ha.namenodes.hp1</name>
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.hp1.nn1</name>
<value>inspur122.photo.163.org:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.hp1.nn2</name>
<value>inspur129.photo.163.org:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.hp2</name>
<value>inspur116.photo.163.org:8020</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.hp1</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>

只列出了一些关键配置。注意HA和非HA配置的一些不同。对于HA NN而言,必须配置dfs.client.failover.proxy.provider属性。
其他配置,比如journal node、dfs.name.dir之类的,保持原样即可。

分发配置

将core-site.xml和hdfs-site.xml分发到所有节点。
然后修改inspur116上的hdfs-site.xml,注释掉dfs.namenode.shared.edits.dir属性。因为对于一个非HA的NN而言,不能有这个属性,否则启动会报错。

启动服务

和启动普通的federation类似。关键在于namenode format时用相同的clusterId。

启动ANN(inspur122)的过程不多说了,format、initShareEdits、formatZK、start zkfc、start namenode。
format后会得到一个clusterId:CID-f19b006a-63a3-4f69-9069-1a2baf7599d3,然后到inspur116上执行:

1
sudo /home/hadoop/hadoop-current/bin/hdfs namenode -format -clusterId CID-f19b006a-63a3-4f69-9069-1a2baf7599d3

之后再启动inspur116的NN。对于非HA的NN而言就没那么多步骤了,直接用hadoop-daemon.sh启动即可。
inspur129稍后再启动。

观察web界面,inspur116和inspur122都正常。
启动DN,inspur116和inspur122同时收到心跳。
分别向inspur116和inspur122写入数据,可以证明这是两个独立的hdfs。

启动SNN(inspur129)。bootstrapStandby、start zkfc、start namenode。观察web界面,SNN也收到了所有DN的心跳。

强制关掉inspur122上的NN,正常failover到inspur129,元数据也正常。

至此hdfs启动完毕。yarn的启动很简单,不多说了。

一些问题

  • 启动SNN时,可能要先saveNamespace才能bootstrapStandby。而dfsadmin相关命令是需要fs.defaultFS属性的。可以先取消core-site.xml中的fs.defaultFS的注释,再执行saveNamespace。
  • 分发客户端时,加上fs.defalutFS属性。因为是模拟多个集群,用户不应该感知到背后federation的存在。我们是模拟3个集群,所以有3个客户端配置。用户可以直接用不同客户端读写对应的hdfs并提交YARN任务。
  • 2个HA的NN+1个非HA的NN应该也是可以的。journal node应该是可以共用的。不想折腾了,就没测试。

log aggression失效

所有federation集群都存在这个问题。
因为取消了fs.defaultFS属性,导致NM端做log aggression时,不会传到hdfs上,而是传到本地磁盘。
比如我们的设置中,log aggression的目录:

1
2
3
4
<property>
<name>yarn.nodemanager.remote-app-log-dir</name>
<value>/var/log/hadoop-yarn/apps</value>
</property>

NM在一个container执行结束后,应该把它的日志上传到hdfs的/var/log/hadoop-yarn目录下。但取消了fs.defaultFS后,FileSystem.get方法会认为默认的URI是file:///,而去操作本地磁盘。于是NM的日志也传到本地磁盘了:

1
2
3
# 每个用户的日志是分目录存放的
hadoop@classb-bigdata4:~$ ls /var/log/hadoop-yarn/apps
hadoop hdfs intern qatest test_hadoop_maomao

如果在启动NM的时候,加上fs.defaultFS属性,比如设置成hdfs://inspur116.photo.163.org:8020,log aggression就正常了,所有日志都会传到这个hdfs上。但有些副作用。

当NM端没有fs.defaultFS、客户端有fs.defaultFS时,可以用类似下面的命令提交任务:

1
hadoop jar wordcount.jar wordcount /user/test/input /user/test/output

这样我只要改动客户端的配置,就可以在YARN中操作不同hdfs的数据,用户不会感知到。

如果NM端有fs.defaultFS,无论客户端如何配置,都会以NM的为准。以上面的命令为例,如果/user/test/input不在NM端指向的那个hdfs上,就会出错。
用户提交任务时必须这么写,明确指定要访问哪个NN:

1
hadoop jar wordcount.jar wordcount hdfs://hp1/user/test/input hdfs://hp1/user/test/output

这相当于跨集群访问了。

问题的根源在于fs.defalutFS这个属性是final的,客户端不能覆盖服务端的配置。
如果不是final,应该就可以了,但我没试过。

曾经尝试过直接修改log aggression的目标路径:

1
2
3
4
<property>
<name>yarn.nodemanager.remote-app-log-dir</name>
<value>hdfs://inspur116.photo.163.org:8020/var/log/hadoop-yarn/apps</value>
</property>

但不行。hadoop似乎没有把这个属性当一个URI来处理。提交任务时NM会有异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2015-06-01 23:08:30,970 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.application.Application: Application application_1432887785054_0083 transitioned from NEW to INITING
2015-06-01 23:08:30,970 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.application.Application: Adding container_1432887785054_0083_01_000001 to application application_1432887785054_0083
2015-06-01 23:08:31,049 FATAL org.apache.hadoop.yarn.event.AsyncDispatcher: Error in dispatcher thread
java.lang.IllegalArgumentException: Wrong FS: hdfs://classb-bigdata5.server.163.org:8020/var/log/hadoop-yarn/apps, expected: file:///
at org.apache.hadoop.fs.FileSystem.checkPath(FileSystem.java:642)
at org.apache.hadoop.fs.RawLocalFileSystem.pathToFile(RawLocalFileSystem.java:69)
at org.apache.hadoop.fs.RawLocalFileSystem.getFileStatus(RawLocalFileSystem.java:516)
at org.apache.hadoop.fs.FilterFileSystem.getFileStatus(FilterFileSystem.java:398)
at org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.LogAggregationService.verifyAndCreateRemoteLogDir(LogAggregationService.java:181)
at org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.LogAggregationService.initApp(LogAggregationService.java:301)
at org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.LogAggregationService.handle(LogAggregationService.java:413)
at org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.LogAggregationService.handle(LogAggregationService.java:64)
at org.apache.hadoop.yarn.event.AsyncDispatcher.dispatch(AsyncDispatcher.java:134)
at org.apache.hadoop.yarn.event.AsyncDispatcher$1.run(AsyncDispatcher.java:81)
at java.lang.Thread.run(Thread.java:662)
2015-06-01 23:08:31,050 INFO org.apache.hadoop.yarn.event.AsyncDispatcher: Exiting, bbye..

任务就卡住了。杀掉这个任务后NM也自杀了。。。