我觉得我,以及我的BLOG陷入了一个怪圈。
那就是我在这里的每一篇文开头都必然是:最近工作好忙,又断更很久了……Anyway,这也不能成为偷懒的理由。我可能对记录技术有些固执的误解,总认为是要待到整理出一个专题一样自成体系的技术点才来更新,然大多数情况下,工作之余不会有太多时间来干这些耗时耗力的玩意。于是我尝试做一些改变,只要产生了想法抑或有些许收获,就或多或少地记录下来。但愿这能奏效。
做推荐系统有一段时日了,从一无所有到搭出一套可以基本运转的推荐系统,说难不难,但个中滋味也需体味过才知不易。嗯,关于从头搭建推荐系统我也有计划后续整理一篇出来。从最初的小水管单机离线接口,到现在全网接入,亿级用户画像,每天数十G的行为数据,光凭初期的Python并发脚本已经完全无法应付了,用户画像每天增量更新时动辄五六个小时,连拖延症如我也看不下去了。可是处在郊区小部门,没有现成的集群可以给我用,只有手头寥寥数台机器,还能怎么办呢?自己搭个Hadoop集群吧!
Hadoop这个东西网上资料太多,就不详细介绍它的背景原理啥的了。简单说来它就是Apache基金会的一个分布式计算项目,核心由分布式文件系统HDFS和分布式计算编程框架MapReduce两部分组成。
Hadoop的分布式文件系统,全称Hadoop Distributed File System。它最大的贡献就是为TB级别的大文件存储提供了分布式方案,它的出现让我们的计算框架至少可以不用考虑以下问题:海量数据存储的单机磁盘容量限制问题、分布式作业时文件的传输问题、数据的容错和备份等等。HDFS的结构如图1所示:
图1 HDFS结构图
HDFS系统中每一台机器是一个Node。其中一台机器作为Namenode,系统会将所有存储文件按大小(如每份64M)切分为Block,分散存储在不同的Datanode上,由Namenode记录文件、目录、分块信息等;其他机器作为Datanode,用以存储各个Block。
MapReduce是一种编程模型,在Hadoop中,它把HDFS的文件作为输入源,主要思路就是通过将计算任务切分成片,并行计算,最后再将分片结果合并后拼到一起。系统中有两个角色:负责调度任务的JobTracker,与负责执行任务的TaskTracker。整个MapReduce的过程大致为:input->split->map->combine->shuffle->reduce->output,有张图对这个过程解释得一目了然,在此就不多做赘述:
图2 MapReduce过程
此时我们有3台机器(我的机器系统是SuSE,CentOS等大同小异),假设IP为10.1.1.1,10.1.1.2,10.1.1.3,其中一台作为Master(运行Namenode和JobTracker),其他两台作为Slaves(运行Datanode和TaskTracker),要把Hadoop集群运行起来,接下来我们至少要完成这些事:
让我们一步步来。当然,是用最简单的语言描述(……呼)。
我们需要为三台机器分别配置hostname,让彼此互相认识。首先,把自己的hostname持久化。如在slave-1机器上输入如下命令:
hostname slave-1
大坑预警1:hostname不能包含下划线(_)、冒号(:)等特殊符号,不然只会配置hdfs路径时会出错
然后修改/etc/hosts
文件,绑定hostname解析:
10.1.1.2 slave-1 localhost localhost.localdomain
10.1.1.1 master
10.1.1.3 slave-2
大坑预警2:localhost、localhost.localdomain等hostname最好不要绑定到127.0.0.1/127.0.1.1一类本地IP地址。Hadoop启动时会首先连接localhost,而此时很多策略下本地IP地址不允许监听,容易出错。具体要求参见https://wiki.apache.org/hadoop/ConnectionRefused
配置完后可ping一下hostname验证。
由于在Master启动的Namenode进程需要SSH到Slaves上进行操作,因此配置他们之间的SSH免密登录是必要的。
首先,我们在三台机器上都创建一个账号,用户名、密码需要保持一致,用以在Hadoop集群间传输文件。
useradd -m hadoop -d /home/hadoop
passwd hadoop
然后登入到Master,这里根据机器的SSH版本分两种方式:
如果是SSH1,则先生成密钥和公钥对:
ssh-keygen -t rsa -P ''
接着将公钥追加到授权的keys里:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
记得检查/etc/ssh/sshd_config文件的以下属性是否配置正确:
RSAAuthentication yes # 启用 RSA 认证
PubkeyAuthentication yes # 启用公钥私钥配对认证方式
AuthorizedKeysFile .ssh/authorized_keys # 公钥文件路径
若不一致,修改后重启服务service sshd restart
即可。然后将公钥文件id_rsa.pub scp到两台Slave机器上。再分别登录两台机器,重复如下操作:
mkdir ~/.ssh
cat id_rsa.pub >> ~/.ssh/authorized_keys
配置完成。在Master上分别ssh到两台Slave机器上,即可验证无密码登录。
如果是SSH2,则改为如下步骤。首先生成密钥对:
ssh-keygen2 -t rsa -P ''
然后在~/.ssh2目录下建立文件identification,通过它来指定私钥。
echo 'IdKey id_rsa' > ~/.ssh2/identification
将公钥文件id_rsa.pub scp到两台Slave机器上。再分别登录两台机器,重复如下操作:
cat 'Key id_rsa.pub' > ~/.ssh2/authorization
配置完成。
这个……实在懒得说了,网上教程一大把,没什么好说的。注意配好环境变量即可,最好三台机器保证目录一致,便于管理。
我们把Hadoop安装在了/data/hadoop
目录下,这时我们进入/data/hadoop/etc/hadoop
目录下,需要修改如下几个配置文件:core-site.xml
、hdfs-site.xml
、mapred-site.xml
、'yarn-site.xml'。
配置一些Hadoop系统的核心参数。必须配置Master的地址作为HDFS的默认目录,其他参数的意义和默认值详见http://hadoop.apache.org/docs/r2.7.3/hadoop-project-dist/hadoop-common/core-default.xml
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://master:9000</value> # master
</property>
</configuration>
大坑预警3:在复写hadoop.tmp.dir的路径时注意,这个配置项的值表示HDFS上的路径,而不是本地文件系统。之前不知看来哪里的配置设了一个file://tmpdir,造成之后配置mongodb连接时临时文件写入出错。注意。
这里注意rpc-address和secondary.http-address的hostname保持与defaultFS一致。
<configuration>
<property>
<name>dfs.namenode.rpc-address</name>
<value>master:9000</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>master:50090</value>
</property>
<property>
<name>dfs.replication</name> # 副本数
<value>2</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/data/hadoop/dfs/name</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/data/hadoop/dfs/data</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
<property>
<name>dfs.hosts.exclude</name> # 指定节点黑名单便于之后不重启动态下线节点
<value>/data/hadoop/etc/hadoop/excludes</value>
</property>
</configuration>
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobhistory.address</name>
<value>master:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>master:19888</value>
</property>
<property>
<name>mapred.hosts.exclude</name> # 节点黑名单同上设置
<value>/data/hadoop/etc/hadoop/excludes</value>
<final>true</final>
</property>
</configuration>
<configuration>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>master</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
</configuration>
最后在slaves
文件中输入所有Datenode的hostname(Master也可以作为一个Datanode):
vim slaves
master
slave-1
slave-2
所有配置完成后,可直接把/etc/hadoop
目录打包传输到所有Slave节点,解压覆盖相同目录文件即可。
在Master机器上输入命令:
hadoop namenode -format
Format动作只需初始化时执行一次,之后增删节点或是Hadoop重启都无需再执行。注意:如果在Hadoop运行之后又重新执行了format,则需要在Slave节点上清空所有dfs.name文件夹下的内容,否则会出错。
在Master机器上输入如下命令:
/data/hadoop/sbin/start-dfs.sh
/data/hadoop/sbin/start-yarn.sh
/data/hadoop/sbin/mr-jobhistory-daemon.sh start historyserver
然后输入jps
查看进程启动情况,正常状态下需看到下列所有进程(如果Master没有作为Datanode,则可以没有Datenode和NodeManager进程):
NameNode
JobHistoryServer
SecondaryNameNode
ResourceManager
DataNode
NodeManager
然后我们登录到Slave机器上输入jps
,如果出现下列进程,则该节点运行正常。
DataNode
NodeManager
我们可以调用hdfs dfsadmin -report
命令来查看当前活跃节点,若数量一致则启动成功。命令运行部分结果如下所示:
Configured Capacity: 3355338473472 (3.05 TB)
Present Capacity: 2413589479424 (2.20 TB)
DFS Remaining: 2340718829568 (2.13 TB)
DFS Used: 72870649856 (67.87 GB)
DFS Used%: 3.02%
Under replicated blocks: 6
Blocks with corrupt replicas: 0
Missing blocks: 0
Missing blocks (with replication factor 1): 0
-------------------------------------------------
Live datanodes (3):
如果你看到这个,那么,Congratulations! 可以开始愉快地玩耍Hadoop啦!
具体的MapReduce示例什么的,推荐去官网教程学习,并且一定要确认与自己的Hadoop版本一致。现在的MapReduce编程接口有v1和v2两版,虽然有的废弃属性还会兼容,但难以保证以后的支持。详见:http://hadoop.apache.org/docs
搭建起了Hadoop集群只是个开始,计划之后对于一些扩展,参数的调优甚至是基于HDFS的Spark应用也会做一些随笔记录。