向一个正在运行的Hadoop集群中增加几个新的Nodes
1. 新节点上部署java/hadoop程序,配置相应的环境变量
2. 新节点上增加用户,从master上拷贝id_rsa.pub并配置authorized_keys
3. 新节点上设置host,需要有集群中各节点的host对应
4. 新节点上建立相关的目录,并修改属主
5. master的slaves文件中增加上相的节点,master上增加相应的host
6. 在新节点上启动datanode和tasktracker
/opt/sohuhadoop/hadoop/bin/hadoop-daemon.sh start datanode
/opt/sohuhadoop/hadoop/bin/hadoop-daemon.sh start tasktracker
7. 进行block块的均衡
在hdfs-site.xml中增加设置balance的带宽,默认只有1M:
<property>
<name>dfs.balance.bandwidthPerSec</name>
<value>10485760</value>
<description>
Specifies the maximum bandwidth that each datanode can utilize for the balancing purpose in term of the number of bytes per second.
</description>
</property>
运行以下命令:
/opt/sohuhadoop/hadoop/bin/start-balancer.sh -threshold 3
均衡10个节点,移动400G数据,大概花费了3个小时
The cluster is balanced. Exiting…
Balancing took 2.9950980555555557 hours
我们现有的Hadoop集群已经运行了一段时间了
由于集群中的服务器分布在2个不同的机房,受跨机房带宽的限制
集群中在2个机房之间的数据传输很慢
所以想把另一个机房的3台服务器从Hadoop集群中去掉
Hadoop提供了Decommission的特性,可以按照以下步骤来操作:
1. 在hadoop的conf目录下生成一个excludes的文件,写上需要remove的节点ip
一个节点一行,注意要写ip,不能写Hostname,如:
10.15.10.41
10.15.10.42
10.15.10.43
2. 在hdfs-site.xml中增加配置:
<property>
<name>dfs.hosts.exclude</name>
<value>/opt/sohuhadoop/conf/excludes</value>
<final>true</final>
</property>
3. 复制以上2个文件到集群各节点上
4. 执行hadoop dfsadmin -refreshNodes命令,它会在后台进行Block块的移动
从移出的Nodes上移动到其它的Nodes上面
5. 通过以下2种方式查看Decommission的状态:
hadoop dfsadmin -report
http://10.10.71.220:50070/dfsnodelist.jsp
正在执行Decommission,会显示:
Decommission Status : Decommission in progress
执行完毕后,会显示:
Decommission Status : Decommissioned
基于现有的Hadoop集群,来搭建Hbase的环境
整个过程还是比较简单的
1. 下载Hbase源码,并解压
cp hbase-0.20.6.tar.gz /opt/hadoop/
cd /opt/hadoop/
tar zxvf hbase-0.20.6.tar.gz
ln -s hbase-0.20.6 hbase
2.修改hbase-env.sh,加入java环境,并修改log位置
export JAVA_HOME=/opt/java/jdk
export HBASE_LOG_DIR=/opt/log/hbase
export HBASE_MANAGES_ZK=true
3. 修改hbase-site.xml,配置hbase
<property>
<name>hbase.rootdir</name>
<value>hdfs://zw-hadoop-master:9000/hbase</value>
<description>The directory shared by region servers.</description>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
<description>The mode the cluster will be in. Possible values are
false: standalone and pseudo-distributed setups with managed Zookeeper
true: fully-distributed with unmanaged Zookeeper Quorum (see hbase-env.sh)
</description>
</property>
<property>
<name>hbase.master</name>
<value>hdfs://zw-hadoop-master:60000</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>zw-hadoop-slave225,zw-hadoop-slave226,zw-hadoop-slave227</value>
<description>Comma separated list of servers in the ZooKeeper Quorum. For example, "host1.mydomain.com,host2.mydomain.com,host3.mydomain.com". By default this is set to localhost for local and pseudo-distributed modes of operation. For a fully-distributed setup, this should be set to a full list of ZooKeeper quorum servers. If HBASE_MANAGES_ZK is set in hbase-env.sh this is the list of servers which we will start/stop ZooKeeper on.
</description>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/opt/log/zookeeper</value>
<description>Property from ZooKeeper's config zoo.cfg.
The directory where the snapshot is stored.
</description>
</property>
几个配置的说明:
hbase.rootdir设置hbase在hdfs上的目录,主机名为hdfs的namenode节点所在的主机hbase.cluster.distributed设置为true,表明是完全分布式的hbase集群hbase.master设置hbase的master主机名和端口hbase.zookeeper.quorum设置zookeeper的主机,官方推荐设置为3,5,7比较好4. 编辑regionservers文件,设置regionservers的服务器,和hadoop的slaves一样即可
5. 启动Hbase
/opt/sohuhadoop/hbase/bin/start-hbase.sh
/opt/sohuhadoop/hbase/bin/stop-hbase.sh
Hbase默认只有一个Master,我们可以也启动多个Master:
/opt/sohuhadoop/hbase/bin/hbase-daemon.sh start master
不过,其它的Master并不会工作,只有当主Master down掉后
其它的Master才会选择接管Master的工作
Hbase也有一个简单的web界面,来查看其状态
http://10.10.71.1:60010/master.jsp
http://10.10.71.1:60030/regionserver.jsp
http://10.10.71.1:60010/zk.jsp
Hadoop集群中,NameNode节点存储着HDFS上所有文件和目录的元数据信息
如果NameNode挂了,也就意味着整个Hadoop集群也就完了
所以,NameNode节点的备份很重要,可以从以下2个方面来备份NameNode节点
1. 在hdfs-site.xml中,配置多个name的dir到不同的磁盘分区上:
<property>
<name>dfs.name.dir</name>
<value>/pvdata/hadoopdata/name/,/opt/hadoopdata/name/</value>
</property>
2. 在另外的一台服务器上配置Secondary NameNode:它是NameNode的一个备份
Secondary NameNode会定期合并fsimage和edits日志,将edits日志文件大小控制在一个限度下
合并的时机是由2个配置参数决定的:
fs.checkpoint.period,指定连续两次检查点的最大时间间隔, 默认值是1小时。
fs.checkpoint.size定义了edits日志文件的最大值,一旦超过这个值会导致强制执行检查点(即使没到检查点的最大时间间隔)。默认值是64MB。
Secondary NameNode的配置过程如下:
在conf/masters中指定第二名称节点的主机名 在core-site.xml中指定checkpoint的目录<property>
<name>fs.checkpoint.dir</name>
<value>/opt/hadoopdata/secondname,/pvdata/hadoopdata/secondname</value>
<description>Determines where on the local filesystem the DFS secondary
name node should store the temporary images to merge.
If this is a comma-delimited list of directories then the image is
replicated in all of the directories for redundancy.
</description>
</property>
如果NameNode节点挂了,可以按照如下步骤来从Secondary NameNode来恢复:
在dfs.name.dir指定的位置建立一个空文件夹 从Secondary NameNode上把secondname的目录给scp到新的NameNode机器的fs.checkpoint.dir下 使用hadoop/bin/hadoop namenode -importCheckpoint来启动NameNode,主要不要执行format命令详细的Secondary NameNode细节可参考Hadoop官方文档:
http://hadoop.apache.org/common/docs/r0.20.2/hdfs_user_guide.html#Secondary+NameNode
这两天在操作Hadoop集群时,由于一个误操作,制作了一个天大的悲剧
不小心把Hadoop集群上的所有文件全部删除了,具体情况是这样的:
我用hadoop的超级帐户要建立一个目录,结果发现位置错了
也是,想使用rmr删掉那个目录,可是不小心把命令写成了
hadoop fs -rmr /user
于是,悲剧出现了,所有user目录下的所有目录和文件全都没有了
当时我就慌神了,赶紧从web查看50070的服务
眼看着DFS Used空间从100多G不停的减少
后来才反应过来,赶紧停掉namenode节点,然后上网google办法
后来,从secondname节点重新恢复了一个checkpoint
但绝大部分数据都已经丢失了,只恢复了一小部分数据,已经没啥用了
幸好,原始log我们在其它服务器上还保留的有,只能重新分析再入Hadoop了
总结了一下几点教训:
首先一定要控制好hadoop上各用户的权限,使各user只能操作自己的目录尽量少用hadoop的超级用户进行操作,可以减少误操作hadoop的rm和rmr命令,设计的太BT了,连一个确认提示都没有,直接就删除了。看到有人给官方提了这个建议,但人家回复说:已经有了trash机制了,所以不需要提示,真是无语….hadoop的trash功能:很遗憾,之前没有配置trash,所以就直接给删除了,经过这次误操作,赶紧配置上trash,并设置保留时间为7天。在core-site.xml中增加如下配置,表明rm后会在trash中保留多少分钟:
<property>
<name>fs.trash.interval</name>
<value>10080</value>
<description>
Number of minutes between trash checkpoints. If zero, the trash feature is disabled
</description>
</property>
很遗憾的是,hadoop的这个默认值是0,就是直接删除了,为什么要这么设计呢?郁闷….
经过简单的测试,这个trash功能还是不错的,当rm后,它会move到当前文件夹下的.Trash目录下
如果你删除一个文件或目录多次,则hadoop会自动在name后加上数字序列号
这样,如果你误删除后,就可以有选择的恢复文件了
hadoop fs -mkdir /user/oplog/test
hadoop fs -put *.txt /user/oplog/test
hadoop fs -rmr /user/oplog/test
hadoop fs -ls /user/oplog/.Trash/Current/user/oplog
drwxr-xr-x – oplog oplog 0 2010-11-16 10:44 /user/oplog/.Trash/Current/user/oplog/test
hadoop fs -mv /user/oplog/.Trash/Current/user/oplog/test /user/oplog/
hadoop fs -ls /user/oplog/.Trash/Current/user/oplog
drwxr-xr-x – oplog oplog 0 2010-11-16 10:44 /user/oplog/.Trash/Current/user/oplog/test
drwxr-xr-x – oplog oplog 0 2010-11-16 10:47 /user/oplog/.Trash/Current/user/oplog/test.1
目前,我们邮件的一部分log已经迁移到Hadoop集群上
并由Hive来执行相关的查询
hadoop中默认的mapred.tasktracker.map.tasks.maximum设置是2
也即:每一个tasktracker同时运行的map任务数为2
照此默认设置,查询80天某用户的操作日志,耗时5mins, 45sec
经过测试,发现将mapred.tasktracker.map.tasks.maximum设置为节点的cpu cores数目或者数目减1比较合适
此时的运行效率最高,大概花费3mins, 25sec
我们现在的机器都是8核的,所以最终配置如下:
<property>
<name>mapred.tasktracker.map.tasks.maximum</name>
<value>8</value>
<description>The maximum number of map tasks that will be run
simultaneously by a task tracker.
</description>
</property>
而对于mapred.map.tasks(每个job的map任务数)值,hadoop默认值也为2
可以在执行hive前,通过set mapred.map.tasks=24来设定
但由于使用hive,会操作多个input文件,所以hive默认会把map的任务数设置成输入的文件数目
即使你通过set设置了数目,也不起作用…
如果在集群里面,可以直接使用hadoop的fs shell来操作集群
往上面put/get文件,或者执行一些hive查询
但如果要在hadoop集群外面去操作hadoop和hive,那么可以有2个办法:
1. 写Java程序,调用hadoop提供的一些API去完成
2. 在需要操作的服务器上搭建一个hadoop的环境
目前,我们采用的是第2种办法,这种方式比较简单
只需要将hadoop master上的hadoop相关目录打个包
部署到对应的服务器上,并修改相应的环境变量和hosts就可以了
打包时,可以把一些无用的配置信息和脚本给去掉
但需要保留core-site.xml和hdfs-site.xm和mapred-site.xml这3个配置文件
12345678910111213 | vi /etc/profile export JAVA_HOME=/opt/java/jdk export HADOOP_CONF_DIR=/opt/sohuhadoop/conf export HADOOP_HOME=/opt/sohuhadoop/hadoop export HIVE_HOME=/opt/sohuhadoop/hive vi /etc/hosts 10.10.1.1 hadoop-master. hadoop-master |
如果你的Hadoop集群,是公用的,可能有很多其它部门的文件都存放在上面
那么,就一定要考虑权限问题,给各种数据以不同的权限
Hadoop目前的权限,实现的比较简单,类似于Shell的权限,是通过操作用户名来控制的
它默认的超级用户就是你启动Hadoop时的用户
一般,我们所有的服务器都默认是用root来登录的
因为,安装Hadoop时一定要新建一个用户来安装,不要安装在root下
然后,对于不同的log,再新建不同的用户目录来存放,如:
12345 | hadoop fs -mkdir /user/test hadoop fs -chmod -R 700 /user/test hadoop fs -chown -R test:test /user/test |
这样,只有test这个用户才能操作/user/test目录,其它用户都无权操作
如果,你su pplog,然后执行
1 | hadoop fs -ls /user/test |
你将会看到一个错误提示:
ls: could not get get listing for 'hdfs://zw-hadoop-master:9000/user/test' : org.apache.hadoop.security.AccessControlException: Permission denied: user=pplog, access=READ_EXECUTE, inode="test":test:test:rwx——
对于Hive来说,可以这么来控制权限访问
为不同日志的MetaStore在Mysql建立不同的数据库为不同的用户建立单独的conf目录,如用户test的hive conf目录位于/opt/sohuhadoop/hive/conf/test下在单独的test目录下,修改hive-default.xml文件,配置相应的db启动单独的hiveserver实例,并监听不同的端口:HIVE_PORT=10020 nohup hive –config $HIVE_HOME/conf/test –service hiveserver &在JDBC中连接自己对应的端口,如10020上面的权限控制虽然有一定作用,但却是还很弱,如果其它人知道了你的用户名或者端口号
一样可以去删除你的文件,据说,将来Hadoop会对权限认证做一定改进,期待……
本机的环境如下:
Eclipse 3.6
Hadoop-0.20.2
Hive-0.5.0-dev
1. 安装hadoop-0.20.2-eclipse-plugin的插件。注意:Hadoop目录中的\hadoop-0.20.2\contrib \eclipse-plugin\hadoop-0.20.2-eclipse-plugin.jar在Eclipse3.6下有问题,无法在 Hadoop Server上运行,可以从http://code.google.com/p/hadoop-eclipse-plugin/下载
2. 选择Map/Reduce视图:window -> open pers.. -> other.. -> map/reduce
3. 增加DFS Locations:点击Map/Reduce Locations—> New Hadoop Loaction,填写对应的host和port
12345678910 | Map/Reduce Master: Host: 10.10.xx.xx Port: 9001 DFS Master: Host: 10.10.xx.xx(选中 User M/R Master host即可) Port: 9000 User name: root 更改Advance parameters 中的 hadoop.job.ugi, 默认是 DrWho,Tardis, 改成:root,Tardis。如果看不到选项,则使用Eclipse -clean重启Eclipse 否则,可能会报错org.apache.hadoop.security.AccessControlException |
4. 设置本机的Host:
12345 | 10.10.xx.xx zw-hadoop-master. zw-hadoop-master #注意后面需要还有一个zw-hadoop-master.,否则运行Map/Reduce时会报错: java.lang.IllegalArgumentException: Wrong FS: hdfs://zw-hadoop-master:9000/user/root/oplog/out/_temporary/_attempt_201008051742_0135_m_000007_0, expected: hdfs://zw-hadoop-master.:9000 at org.apache.hadoop.fs.FileSystem.checkPath(FileSystem.java:352) |
5. 新建一个Map/Reduce Project,新建Mapper,Reducer,Driver类,注意,自动生成的代码是基于老版本的Hadoop,自己修改:
123456789101112131415161718192021222324252627282930313233343536373839404142 434445464748495051525354555657585960616263646566676869707172737475767778798081 | packagecom.sohu.hadoop.test; importjava.util.StringTokenizer;importorg.apache.hadoop.io.IntWritable;importorg.apache.hadoop.io.Text;importorg.apache.hadoop.mapreduce.Mapper; publicclass MapperTest extends Mapper<Object, Text, Text, IntWritable>{privatefinalstatic IntWritable one =new IntWritable(1); publicvoid map(Object key, Text value, Context context)throwsIOException, InterruptedException{String userid = value.toString().split("[|]")[2]; context.write(new Text(userid), new IntWritable(1));}} packagecom.sohu.hadoop.test; importjava.io.IOException;importorg.apache.hadoop.io.IntWritable;importorg.apache.hadoop.io.Text;importorg.apache.hadoop.mapreduce.Reducer; publicclass ReducerTest extends Reducer<Text, IntWritable, Text, IntWritable>{ private IntWritable result =new IntWritable(); publicvoid reduce(Text key, Iterable<IntWritable> values, Context context)throwsIOException, InterruptedException{int sum =0;for(IntWritable val : values){ sum += val.get();} result.set(sum); context.write(key, result);}} packagecom.sohu.hadoop.test; importorg.apache.hadoop.conf.Configuration;importorg.apache.hadoop.fs.Path;importorg.apache.hadoop.io.IntWritable;importorg.apache.hadoop.io.Text;importorg.apache.hadoop.io.compress.CompressionCodec;importorg.apache.hadoop.io.compress.GzipCodec;importorg.apache.hadoop.mapreduce.Job;importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;importorg.apache.hadoop.util.GenericOptionsParser; publicclass DriverTest {publicstaticvoid main(String[] args)throwsException{ Configuration conf =new Configuration();String[] otherArgs =new GenericOptionsParser(conf, args) .getRemainingArgs();if(otherArgs.length!=2){System.err.println("Usage: DriverTest <in> <out>");System.exit(2);} Job job =new Job(conf, "Driver Test"); job.setJarByClass(DriverTest.class); job.setMapperClass(MapperTest.class); job.setCombinerClass(ReducerTest.class); job.setReducerClass(ReducerTest.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); conf.setBoolean("mapred.output.compress", true); conf.setClass("mapred.output.compression.codec", GzipCodec.class,CompressionCodec.class); FileInputFormat.addInputPath(job, new Path(otherArgs[0])); FileOutputFormat.setOutputPath(job, new Path(otherArgs[1])); System.exit(job.waitForCompletion(true)?0:1);}} |
6. 在DriverTest上,点击Run As —> Run on Hadoop,选择对应的Hadoop Locaion即可