三节点 Hadoop 集群搭建实操
前两篇都是概念,这篇是动手部分:用三台虚拟机从零搭一个 Hadoop 集群。
搭之前我以为难点在配置文件,搭完发现——真正花时间的是环境准备。配置文件只是照着改参数,而环境里任何一个版本不对,后面全跑不起来。这篇按实际操作的顺序记录下来。
集群规划
三台机器,规划如下:
| 机器 | IP | NameNode | SecondaryNameNode | DataNode | ResourceManager | NodeManager |
|---|---|---|---|---|---|---|
| node01 | 192.168.174.100 | 是 | 是 | 是 | 是 | 是 |
| node02 | 192.168.174.110 | 否 | 否 | 是 | 否 | 是 |
| node03 | 192.168.174.120 | 否 | 否 | 是 | 否 | 是 |
这是最典型的"伪分布式之上、完全分布式之下"的教学配置:主节点 node01 兼任了 NameNode、SecondaryNameNode 和 ResourceManager 三个角色,两台从节点只做 DataNode 和 NodeManager。
把三个主角色都压在一台机器上当然有单点故障,但实验环境无所谓——能看清数据怎么分布到三台机器上,目的就达到了。
第一步:环境准备(这里最容易翻车)
基础环境
虚拟机配置要求:内存 4G 以上,硬盘 40G 以上,操作系统用 64 位的 CentOS 6.9。
64 位这一条不是可选项。Hadoop 的本地库(native library)只有 64 位版本,32 位系统上跑会报各种奇怪的库加载错误。
另外要关掉防火墙和 SELinux——它们会拦截集群各节点之间大量的内部通信:
# 关闭防火墙
service iptables stop
chkconfig iptables off
# 关闭 SELinux
vim /etc/selinux/config
# 把 SELINUX=enforcing 改成 SELINUX=disabledSELinux 那条改完要重启才生效,别改完就急着往下做。
为什么需要"编译" Hadoop
这一步我一开始没理解——官网上不是有现成的安装包吗,为什么还要自己编译?
讲义里给了解释:Apache 官方发布的二进制包没有提供带 C 程序访问的接口。这意味着压缩功能(snappy、bzip2 这些)用不了,因为压缩库是 C 写的,需要通过本地库调用。
所以要用到这些功能,就得拿源码包重新编译一遍。要准备的依赖链不短:
| 组件 | 版本 | 作用 |
|---|---|---|
| JDK | 必须是 1.7 | 编译 hadoop-2.7.5 |
| Maven | 3.0.5 | 构建工具 |
| findbugs | 1.3.9 | 代码静态检查(构建流程需要) |
| protobuf | 2.5.0 | 序列化,需源码编译安装 |
| snappy | 1.1.1 | 压缩库,需源码编译安装 |
最大的坑:JDK 版本
讲义里用加粗专门强调了一条:
注意 hadoop-2.7.5 这个版本的编译,只能使用 jdk1.7,如果使用 jdk1.8 那么就会报错
这是个硬约束。而且 CentOS 6.9 自带一堆 openjdk,得先全部卸干净,否则编译时可能链接到系统自带的版本:
# 查看系统自带的 openjdk
rpm -qa | grep java
# 全部卸载
rpm -e java-1.6.0-openjdk-1.6.0.41-1.13.13.1.el6_8.x86_64 \
tzdata-java-2016j-1.el6.noarch \
java-1.7.0-openjdk-1.7.0.131-2.6.9.0.el6_8.x86_64然后是安装自己的 JDK 并配环境变量:
mkdir -p /export/servers
mkdir -p /export/softwares
cd /export/softwares
tar -zxvf jdk-7u71-linux-x64.tar.gz -C ../servers/
vim /etc/profile
# 加入:
export JAVA_HOME=/export/servers/jdk1.7.0_71
export PATH=:$JAVA_HOME/bin:$PATH
source /etc/profile/export 这个目录不是系统标准目录,是大数据课程约定俗成的安装位置——把软件、数据、临时文件都放在一起,方便统一管理。
Maven 与阿里云镜像
tar -zxvf apache-maven-3.0.5-bin.tar.gz -C ../servers/
vim /etc/profile
export MAVEN_HOME=/export/servers/apache-maven-3.0.5
export MAVEN_OPTS="-Xms4096m -Xmx4096m"
export PATH=:$MAVEN_HOME/bin:$PATH编译 Hadoop 要下载大量依赖,Maven 默认从国外的中央仓库拉,速度很慢。所以要在 settings.xml 里加一个阿里云镜像:
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>编译
cd /export/servers/hadoop-2.7.5
mvn package -DskipTests -Pdist,native -Dtar -Drequire.snappy -e -X参数含义:-DskipTests 跳过测试(编译快很多),-Pdist,native 启用本地库编译,-Drequire.snappy 要求支持 snappy 压缩。编译产物在 hadoop-dist/target 下。
第二步:配置文件
Hadoop 的配置文件都在 $HADOOP_HOME/etc/hadoop/ 下,要改 5 个。逐个说明。
core-site.xml —— 全局核心配置
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://node01:8020</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/export/servers/hadoop-2.7.5/hadoopDatas/tempDatas</value>
</property>
<property>
<name>io.file.buffer.size</name>
<value>4096</value>
</property>
<property>
<name>fs.trash.interval</name>
<value>10080</value>
</property>
</configuration>四项分别:
fs.default.name 指定 HDFS 的默认访问地址。8020 是 NameNode 的 RPC 端口,客户端读写文件时连的就是这里。
hadoop.tmp.dir 是 Hadoop 的临时目录。这个路径非常重要——很多组件的默认数据目录都基于它。如果没配或者配到会被清理的目录(比如 /tmp),集群重启后数据可能就找不回来了。
fs.trash.interval = 10080 开启了回收站,单位是分钟。10080 分钟 = 7 天。开了之后 hdfs dfs -rm 删除的文件会先进回收站,还能捞回来;没开的话是直接删。
hdfs-site.xml —— HDFS 专属配置
这个文件最长,但可以分三组看。
第一组:Web 界面地址
<property>
<name>dfs.namenode.http-address</name>
<value>node01:50070</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>node01:50090</value>
</property>50070 是 NameNode 的 Web UI 端口,搭完集群要打开这个页面验证。
第二组:数据目录
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///export/servers/hadoop-2.7.5/hadoopDatas/namenodeDatas,
file:///export/servers/hadoop-2.7.5/hadoopDatas/namenodeDatas2</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///export/servers/hadoop-2.7.5/hadoopDatas/datanodeDatas,
file:///export/servers/hadoop-2.7.5/hadoopDatas/datanodeDatas2</value>
</property>注意每个都配了两个路径,用逗号分隔。这是 HDFS 的一个特性:同一个目录可以配多份,写的时候同时写进去。讲义里的注释解释了实际用途——
定义 dataNode 数据存储的节点位置,实际工作中,一般先确定磁盘的挂载目录,然后多个目录用逗号分割
也就是说:如果一台机器挂了多块盘,就把每块盘的挂载点都列上,HDFS 会自动把数据分散到不同物理磁盘。既扩大了容量,也避免了单盘故障导致整个 DataNode 不可用。
第三组:块与副本
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>134217728</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>前两个上一篇详细讲过(3 副本、128MB 块)。第三个 dfs.permissions = false 是关掉了 HDFS 的权限检查——这是实验环境图省事的做法,生产环境绝对不能这么配,否则任何用户都能读写别人的数据。
yarn-site.xml
<configuration>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>node01</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>20480</value>
</property>
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>2048</value>
</property>
<property>
<name>yarn.nodemanager.vmem-pmem-ratio</name>
<value>2.1</value>
</property>
</configuration>几项关键的:
yarn.nodemanager.aux-services = mapreduce_shuffle 是必填项。Shuffle 是 MapReduce 里把 map 输出传给 reduce 的过程,YARN 需要这个辅助服务来支持它。漏配的话 MapReduce 任务跑不起来。
yarn.log-aggregation-enable = true 开启日志聚合。默认情况下每个容器(Container)的日志散落在各个 NodeManager 本地磁盘上,任务跑完想查日志得挨个机器找。开启聚合后会统一收集到 HDFS 上,通过 Web 界面就能看。
yarn.nodemanager.resource.memory-mb = 20480 表示这个节点能给 YARN 用的内存总量是 20480MB(20GB)。注意——这是讲义里给生产环境写的值,实验用的虚拟机内存只有 4G,实际得按自己机器的配置改小,否则 NodeManager 会因为"可用内存不足"拒绝启动容器。
hadoop-env.sh 与 mapred-env.sh
两个文件都只要改一行,指定 JAVA_HOME:
export JAVA_HOME=/export/servers/jdk1.8.0_141注意这里出现了版本问题:编译用的是 JDK 1.7,运行用的是 1.8。这个不一致是讲义里就有的——编译源码时 2.7.5 版本只认 1.7,但集群运行起来用 1.8 没问题。
如果只做实验不编译源码,那全程用 1.8 也可以。关键是这两个 env 文件里的路径要和你实际装的 JDK 对上——路径写错是启动失败最常见的原因,而且报错信息通常很含糊,只提示找不到 Java。
slaves
node01
node02
node03这个文件列出所有 DataNode 和 NodeManager 节点。启动脚本会读它,自动登录到这些机器上把进程拉起来。
第三步:分发与启动
建目录
配置里写了那么多数据目录,得先手动创建——Hadoop 不会自动建:
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/tempDatas
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/namenodeDatas
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/namenodeDatas2
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/datanodeDatas
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/datanodeDatas2
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/nn/edits
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/snn/name
mkdir -p /export/servers/hadoop-2.7.5/hadoopDatas/dfs/snn/edits漏建目录的后果是启动时报路径不存在。这类错误很直白,但目录多,容易漏。
分发安装包
cd /export/servers/
scp -r hadoop-2.7.5 node02:$PWD
scp -r hadoop-2.7.5 node03:$PWD因为 node01 上已经配好了所有文件,直接整个目录复制过去,从节点就不用重复配置了。这也是为什么要在配完文件之后才分发——顺序反了就得配三遍。
分发之后,三台机器都要配 Hadoop 的环境变量:
vim /etc/profile
export HADOOP_HOME=/export/servers/hadoop-2.7.5
export PATH=:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
source /etc/profile格式化与启动
首次启动 HDFS 必须先格式化——这时候 HDFS 在物理上还不存在,格式化是做一些初始化的准备工作:
cd /export/servers/hadoop-2.7.5/
bin/hdfs namenode -format
sbin/start-dfs.sh
sbin/start-yarn.sh
sbin/mr-jobhistory-daemon.sh start historyserver关键的一点:格式化只能在首次启动时做一次。如果集群已经跑过并存了数据,再执行 format 会把 NameNode 的元数据全部清空——虽然 DataNode 上的数据块还在,但 NameNode 不认识它们了,等同于数据丢失。这是新手最容易造成"事故"的操作。
第四步:验证
启动后有三个 Web 界面可以检查集群状态:
| 地址 | 看什么 |
|---|---|
http://node01:50070/explorer.html#/ | HDFS 文件系统,能浏览目录和文件 |
http://node01:8088/cluster | YARN 集群状态,看有哪些节点、跑了哪些任务 |
http://node01:19888/jobhistory | 历史任务记录,跑完的 MapReduce 作业在这里查 |
50070 页面上有个 Live Nodes 计数,正常应该是 3。如果显示 2 或者 0,说明有 DataNode 没起来——去对应的机器上看日志。
回头复盘
整个过程走下来,我觉得最值得记的不是那几段 XML,而是三个教训。
环境准备才是真正花时间的地方。卸载 openjdk、装 JDK 1.7、编译 protobuf 和 snappy、配 Maven 镜像——这些听起来都是"前置工作",但它们加起来占了大部分时间。
更麻烦的是报错指不到真正的原因。JDK 版本不对,可能报一个编译到一半的语法错误;目录没建,只报路径不存在却不说该建哪个。这些信息都不指向根因,只能靠一步步回退定位。所以环境越复杂,越要在每一步之后先确认它能用,再往下走。
版本约束看着像玄学,其实都有原因。"hadoop-2.7.5 只能用 jdk1.7 编译"这一条,讲义里特意加粗强调过。我一开始的想法是"1.8 不是更新吗,用新的应该没问题"——但这是版本间的 API 兼容问题,不是新旧问题。遇到这类硬约束,老老实实按文档来。
格式化只能做一次。这是纪律问题。集群里已经有数据时执行 namenode -format,等于把图书馆的索引全烧了而书还在书架上——DataNode 上的数据块还在,但 NameNode 不认识它们了。养成习惯:只在全新集群上格式化。
暂无评论