☰
CentOS 8.1下Hadoop+MapReduce+HBase集群搭建全流程实战
2026/10/17 18:08:27 网站建设 项目流程

最近好多朋友问我在CentOS 8.1上搭Hadoop、MapReduce和HBase的事,干脆把这个项目的搭建全过程整理出来。标题上的“hdoop”其实就是Hadoop,手一快就打错了。这个项目说白了就是三件事:用Hadoop做分布式存储和资源调度,用MapReduce做离线计算,再用HBase承接需要实时读写的列式存储数据。整套环境跑起来之后,既可以练习经典的大数据离线数仓流程,也能给后续做实时类应用打个底。

我会按照真实的动手顺序往下写,从系统初始化到HBase Shell里跑出数据全部覆盖。如果你有一两台机器想练手,这个流程基本可以直接照抄;如果你准备把它放到生产环境,配置参数方面我也把原理讲清楚,方便你按实际资源调整。

写这篇文的路子是我自己踩坑踩出来的。事前最需要想清楚的不是某个命令,而是架构、版本和目录规划,这三样没定好,后面会到处补洞。

1. 项目思维拆解与方案选型

1.1 项目需求与技术选型

这个项目的出发点往往是一个很朴素的需求:有一批结构化和半结构化的数据,既要跑批量统计,又想让后续应用能按行键快速查询。传统关系型数据库在数据量大到一定量级后,单表查询和批量计算都会出现瓶颈,所以需要一个分布式的解决方案。Hadoop负责把文件打散存储在多个节点上,MapReduce负责把这些数据切片并行算,HBase则在HDFS之上建立一张面向海量数据的列式存储表。

选型的时候我明确了两条原则:第一,能用同一种生态体系解决的问题,不要引入太多陌生的框架。Hadoop、MapReduce、HBase本身都是Hadoop生态里的东西,学习路径和问题排查方式都比较成熟。第二,版本必须互相兼容,这是这个项目里最容易翻车的一环。如果选一个太新的Hadoop版本配一个很老的HBase版本,或者反过来,启动时经常会出现一些非常抽象的类加载错误。

我在这个实验项目里选定的组合是:Hadoop 3.2.1、HBase 2.2.4、JDK 1.8。这个组合是很多离线数据实验环境的经典配置,社区讨论多,踩坑记录也容易搜到。JDK没有选更新的版本,因为Hadoop和HBase官方对JDK版本的要求都偏保守,JDK 8在兼容性和稳定性上最稳妥。

1.2 架构设计与资源规划

接下来要考虑的是拓扑。单机伪分布式模式只有一个节点,NameNode、DataNode、ResourceManager、NodeManager都挤在同一台机器上,适合做功能演示。完全分布式模式至少需要三个节点,通常一个主节点跑NameNode和ResourceManager,两个从节点跑DataNode和NodeManager,HBase的两个核心角色HMaster和RegionServer也可以按同样的思路分布上去。

这个项目我选了三节点完全分布式,主机名分别叫node01、node02、node03,对应的内网地址是192.168.10.1、192.168.10.2、192.168.10.3。node01作为主节点,负责跑NameNode、ResourceManager、HMaster;node02和node03作为从节点,跑DataNode、NodeManager、RegionServer。ZooKeeper我选择让HBase自己托管(HBASE_MANAGES_ZK=true),省掉一个额外部署组件的复杂度,等后续需要给其他框架做协调服务时,再单独拆出来。

资源方面,三台机器每台分配2核CPU、4G内存足够了,硬盘给到50G以上。这里有个教训:如果每台机器内存只有2G,Hadoop和HBase的多个JVM进程会争抢内存,启动的时候表面看着一切正常,跑任务时经常卡死在GC上。

2. 环境准备:CentOS 8.1基础配置与JDK安装

2.1 系统初始化与主机名映射

拿到CentOS 8.1系统后,我做的第一步不是安装Hadoop,而是把操作系统层面的信息理顺。首要是修改主机名,主节点叫node01,从节点叫node02、node03,然后统一写入三台机器的/etc/hosts文件。

hostnamectl set-hostname node01 cat >> /etc/hosts <<EOF 192.168.10.1 node01 192.168.10.2 node02 192.168.10.3 node03 EOF

这一步很关键,因为Hadoop和HBase内部大量使用主机名来互相通信。如果主机名解析不了,NameNode能启动,但DataNode进程起不去,日志里会看到一堆UnknownHostException。我见过不少人的问题其实根本不是Hadoop配置错了,而是hosts文件里忘了写映射。

同时建议关闭防火墙或者放行相关端口。实验环境下最简单的做法是直接停掉firewalld:

systemctl stop firewalld systemctl disable firewalld sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

SELinux如果保持enforcing,经常导致HBase创建文件时权限异常,而且错误提示很隐蔽,所以实验环境我直接关掉。生产环境当然不建议这么干,应该通过SELinux策略或者放行特定端口来解决,这点要区分开。

2.2 JDK安装与环境变量配置

Hadoop和HBase都跑在JVM上,JDK是基础。我使用的是OpenJDK 1.8,CentOS 8.1可以直接通过包管理器安装:

yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel

安装完之后要确认Java路径,因为后面要写进hadoop-env.sh和hbase-env.sh。可以这样查:

update-alternatives --config java

或者用readlink -f $(which java)看一下完整路径,常见的路径是/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x.x。我习惯把JAVA_HOME单独写进一个/etc/profile.d/java.sh文件里,避免污染主profile:

cat > /etc/profile.d/java.sh <<EOF export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export PATH=\$PATH:\$JAVA_HOME/bin EOF source /etc/profile.d/java.sh java -version

这里说明一下为什么要单独建文件:Hadoop的start-dfs.sh脚本查找Java时依赖JAVA_HOME环境变量,如果用普通用户执行脚本,用户没有加载到系统全局变量,启动时就会提示找不到JAVA_HOME。把变量放到/etc/profile.d下面是最省心的方式。若还是报错,就把JAVA_HOME直接写死在hadoop-env.sh里,这是唯一能保证被加载到的位置。

2.3 创建专用用户与SSH免密登录

大数据组件不建议用root直接跑,一方面HDFS的一些操作对权限敏感,另一方面安全上也是隐患。我单独创建了一个hadoop用户:

useradd hadoop echo "hadoop123" | passwd --stdin hadoop chown -R hadoop:hadoop /opt

所有组件的安装包、解压目录都放在/opt下面,并将/opt授权给hadoop用户。文件目录归属问题在启动HBase时经常让人抓狂,一次配好后面省事。

SSH免密登录是集群之间通信的基础。主节点node01需要能免密登录到所有节点(包括自己),这样start-dfs.sh才能远程拉起其他节点的进程。生成密钥后分发即可:

su - hadoop ssh-keygen -t rsa -P '' ssh-copy-id node01 ssh-copy-id node02 ssh-copy-id node03

一定要逐节点验证一下ssh nodeXX能不能免密进去。如果分发的时机不对,例如某个节点还没配好hosts文件,免密登录会一直要求输入密码,start-dfs.sh执行到一半就卡住。另外,不要忘记本机也要拷贝一份,否则很多脚本启动时会在本机卡住。

3. Hadoop与MapReduce集群搭建

3.1 安装目录规划与下载部署

Hadoop的运行机制可以用一句话先概括:NameNode管元数据,DataNode管数据块,NodeManager管计算资源。它的所有模块都由配置文件驱动,所以部署的核心不是安装,而是把配置文件改对。

目录规划我参考的是统一目录结构原则。下载好的hadoop-3.2.1.tar.gz放到/opt/apps目录,解压后建立软链,方便以后切换版本:

mkdir -p /opt/apps mkdir -p /opt/data/hdfs/nn mkdir -p /opt/data/hdfs/dn tar -zxvf hadoop-3.2.1.tar.gz -C /opt/apps ln -s /opt/apps/hadoop-3.2.1 /opt/apps/hadoop chown -R hadoop:hadoop /opt/apps /opt/data

这里我把NameNode的元数据目录指定到/opt/data/hdfs/nn,DataNode的数据块目录指定到/opt/data/hdfs/dn。很多默认配置会把元数据放到/tmp目录下,而/tmp在系统重启时可能被清理,一旦清理,NameNode会因找不到元数据而拒绝启动,这是运维里一个非常经典的坑。

3.2 核心配置文件逐项分析

Hadoop的配置集中在$HADOOP_HOME/etc/hadoop目录下,对一次集群初始化来说,必改的文件是core-site.xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml、workers、hadoop-env.sh。

core-site.xml主要告诉客户端和DataNode,NameNode在哪个地址:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node01:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/data/hdfs/tmp</value> </property> </configuration>

fs.defaultFS里的8020是NameNode RPC通信端口,之前的老教程写的多是9000,新版Hadoop里默认配置是8020,你完全可以自定义,但必须保证node01的9000或8020端口没有被占用。hadoop.tmp.dir则是为了把临时文件和元数据从/tmp挪出来。hdfs-site.xml里最主要的是副本数和元数据目录:

<configuration> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/opt/data/hdfs/nn</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/data/hdfs/dn</value> </property> <property> <name>dfs.permissions.enabled</name> <value>false</value> </property> </configuration>

三个节点的集群把副本数设为2,表示一份数据会存两份。如果只有一个节点,这里要改回1。dfs.permissions.enabled设成false在实验环境里可以减少很多权限报错,生产环境绝对不能这样配。

yarn-site.xml负责资源调度。重中之重是aux-services和resourcemanager地址:

<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>node01</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>2048</value> </property> <property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>1</value> </property> </configuration>

mapred-site.xml要指定MapReduce跑在YARN上,同时需要把application classpath配好,否则提交作业时会报找不到Hadoop相关类的错误:

<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>mapreduce.application.classpath</name> <value>/opt/apps/hadoop/etc/hadoop:/opt/apps/hadoop/share/hadoop/common/*:/opt/apps/hadoop/share/hadoop/common/lib/*:/opt/apps/hadoop/share/hadoop/hdfs/*:/opt/apps/hadoop/share/hadoop/hdfs/lib/*:/opt/apps/hadoop/share/hadoop/mapreduce/*:/opt/apps/hadoop/share/hadoop/mapreduce/lib/*:/opt/apps/hadoop/share/hadoop/yarn/*:/opt/apps/hadoop/share/hadoop/yarn/lib/*</value> </property> </configuration>

这份配置是从Hadoop 3.0开始才特别需要的。如果不写,YARN运行MapReduce作业时,AppMaster经常会抛ClassNotFoundException,起初很多人以为是自己代码问题,实际上只差这么一段路径。

workers文件则列出所有DataNode和NodeManager节点,Hadoop 3.x里这个文件名从slaves改成了workers:

cat > /opt/apps/hadoop/etc/hadoop/workers <<EOF node02 node03 EOF

最后补上hadoop-env.sh里的JAVA_HOME:

echo 'export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk' >> /opt/apps/hadoop/etc/hadoop/hadoop-env.sh

3.3 HDFS与YARN的启动和验证

配置文件修改完成后,要把整个etc/hadoop目录同步到node02和node03,然后回到node01,第一次启动前必须格式化NameNode。格式化的本质是创建NameNode的元数据目录,并生成一个空的文件系统镜像。如果集群版本或核心配置在格式化之后有大的变化,通常需要重新格式化,但要注意,直接重复格式化会把之前写入HDFS的数据全部清空,所以生产环境格式化的时机要非常谨慎。

su - hadoop hdfs namenode -format start-dfs.sh start-yarn.sh

启动后每个节点用jps看一眼进程,node01上应该有NameNode、ResourceManager、SecondaryNameNode,node02和node03上应该有DataNode和NodeManager。这里有个容易混淆的点:SecondaryNameNode和NameNode是两回事,它不会成为NameNode的备份,只是负责定期合并编辑日志和镜像文件,没有它集群也能跑,但元数据恢复会变慢很多。

浏览器访问node01的9870端口可以看到HDFS的Web UI,访问8088端口能看到YARN的资源调度界面。这是两个非常直观的集群健康指标。如果9870访问不了,多半是hosts解析问题或防火墙问题;如果8088访问不了,多半是ResourceManager进程没起来。

3.4 跑通第一个MapReduce作业

集群跑起来之后,我习惯先用自带的WordCount例子验证整个计算链路。先在HDFS上创建一个测试目录,上传几个文本文件:

hdfs dfs -mkdir -p /input echo "hadoop mapreduce hbase" > test1.txt echo "centos hdoop bigdata" > test2.txt hdfs dfs -put test1.txt test2.txt /input

然后提交经典示例作业:

hadoop jar /opt/apps/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.2.1.jar wordcount /input /output hdfs dfs -cat /output/part-r-00000

在YARN的Web界面里可以看到这个作业从Submitted到Succeeded的完整流转过程。第一次等待的时间可能偏长,因为每个容器启动时要加载大量类库。如果作业失败,不要急着改代码,先把日志找出来看。YARN日志在node01的/opt/apps/hadoop/logs/userlogs下,按job id分目录,map或reduce的每个任务都有单独的日志文件,这种定位效率远高于看着Web UI猜。

跑通WordCount,意味着HDFS的分布式存储是通的,YARN的资源调度是通的,MapReduce的整个执行框架也是通的,HBase就可以放心接上来了。

4. HBase列式存储的集成与实操

4.1 HBase版本兼容性与部署位置

HBase的定位可以简单理解成运行在HDFS之上的分布式列存数据库。它会把自己的数据文件写到HDFS里,但表结构、Region的状态这类运行期元数据由ZooKeeper管理。因此部署HBase前,必须先确认HDFS是健康的,这是几乎所有HBase启动异常的总根源。

版本搭配上,HBase 2.2.4官方支持的Hadoop版本范围覆盖了Hadoop 2.x和3.x,和Hadoop 3.2.1配起来没有问题。要下载hbase-2.2.4-bin.tar.gz,解压之后同样做软链:

tar -zxvf hbase-2.2.4-bin.tar.gz -C /opt/apps ln -s /opt/apps/hbase-2.2.4 /opt/apps/hbase chown -R hadoop:hadoop /opt/apps/hbase

HBase的目录结构里,/opt/apps/hbase/conf存放配置文件,/opt/apps/hbase/regionserver存放RegionServer的进程文件。如果多次启动失败,常见原因是regionserver目录里残留了一些临时状态,把它删掉再重启通常会恢复正常。

4.2 hbase-site.xml关键参数分析

HBase的配置同样集中在conf目录,核心是hbase-site.xml。以下几项必须有,而且必须和Hadoop、ZooKeeper的实际情况对齐:

<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://node01:8020/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>node01,node02,node03</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/opt/data/zookeeper</value> </property> <property> <name>hbase.master.info.port</name> <value>16010</value> </property> </configuration>

hbase.rootdir必须和core-site.xml里的fs.defaultFS保持一致,端口都要用8020,否则HBase会把数据写到本地文件系统而不是HDFS,报错的样式非常奇葩。hbase.zookeeper.quorum里填的必须和hosts映射完全一致,不能写IP和主机名混用。hbase.zookeeper.property.dataDir是让HBase自带的ZooKeeper持久化数据,这个目录也要从/tmp挪走,否则系统重启后ZooKeeper状态全丢,HBase表就找不回来了。

conf目录下还需要一个regionservers文件,写法跟Hadoop的workers完全类似:

cat > /opt/apps/hbase/conf/regionservers <<EOF node02 node03 EOF

另外别忘了hbase-env.sh,至少要设置JAVA_HOME。如果集群的内存比较紧张,可以像下面这样调整HBase的堆大小:

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export HBASE_HEAPSIZE=1G

HBASE_HEAPSIZE这个参数是HMaster和RegionServer各自的最大堆内存。机器总共只有4G的情况下,我建议给到1G左右,给Hadoop的NodeManager留一点余地,否则两个框架一起抢内存,系统会频繁触发GC。

4.3 HBase启动、验证与Shell操作

启动HBase之前,先确认HDFS没有处于安全模式,然后使用start-hbase.sh启动。观察启动顺序会发现HBase自带ZooKeeper启动在先,然后才是HMaster和RegionServer。启动完成后,用jps在node01上应能看到HMaster进程,node02和node03上应能看到HRegionServer进程。访问16010端口可以登录HBase的Web UI,看到表数量、Region分布和RegionServer状态。

接下来进入Shell验证读写:

hbase shell

创建一张测试表,列族定义为cf,然后写入几行数据:

create 'user_info', 'cf' put 'user_info', 'row1', 'cf:name', 'zhangsan' put 'user_info', 'row2', 'cf:age', '25' scan 'user_info' get 'user_info', 'row1'

看到能scan出整表数据后,说明一级链路通:客户端 → HMaster → ZooKeeper → RegionServer → HDFS。如果scan卡住,十有八九是RegionServer没有正常注册,先去日志目录看HRegionServer启动日志,而不是反复重启集群。

Shell里几个高频命令也可以顺手记一下:disable和drop用来下线并删除一张表,alter用来修改列族属性,describe用来查看表结构。删除表前必须先disable,否则会报错,这是HBase和老朋友关系型数据库在操作习惯上一个很明显的差异。

4.4 MapReduce读写HBase的典型用法

HBase本来就可以和MapReduce配合使用。官方自带的例子能让你快速确认这一层联通性。比如统计HBase表行数:

hbase org.apache.hadoop.hbase.mapreduce.RowCounter user_info

这个命令内部会启动一个MapReduce作业,通过TableInputFormat读取表数据,统计完成后输出总行数。如果你能正常跑出统计结果,说明MapReduce框架已经从HDFS阶段扩展到了HBase阶段。

实际项目中更常见的是自己写一个Mapper或Reducer,读取HBase表做ETL清洗,再写回另一张表。总体思路是:用TableInputFormat把表里的数据逐行喂给Mapper,输出给Reducer或直接写回HBase。这里只提醒一点,自定义HBase MapReduce作业的jar包路径要配置到HADOOP_CLASSPATH里,因为HBase客户端类需要在YARN容器里被加载:

export HBASE_HOME=/opt/apps/hbase export HADOOP_CLASSPATH=$HBASE_HOME/lib/*:$HADOOP_CLASSPATH

很多人在这一步会卡在ClassNotFoundException,原因就是没有把HBASE_HOME/lib加载到HADOOP_CLASSPATH。配置完环境变量后要重新提交作业才会生效。

5. 常见问题与排查技巧实录

5.1 HDFS安全模式一直退不出怎么办

NameNode刚启动时会进入安全模式,这个模式可以理解成文件系统的只读保护状态,用来确认DataNode上报的数据块情况。如果集群的DataNode注册数量不够,或者上报的数据块数和元数据里记录的数量差异太大,安全模式就会迟迟不退出。

排查时先看有几个DataNode在线。合理的情况是node02和node03都正常注册了。有一个隐蔽原因经常被忽略:DataNode进程是起来了,但由于hostname和hosts不匹配,它注册到NameNode时显示的地址是一个奇怪的随机主机名,NameNode一直等不到它。解决办法就是把/etc/hosts统一改好,删除DataNode运行目录下的dfs.dn.uid残留文件,然后重启DataNode。

如果只是想强制离开安全模式,可以用:

hdfs dfsadmin -safemode leave

这只能解决一时的问题。如果每次启动都卡在安全模式,还是要从副本数、健康节点数去排查,强制退出只是饮鸩止渴。这里补充一下,HDFS刚格式化后第一次启动也会短暂停留在安全模式,等一小段时间会自动退出,这是正常现象。

5.2 DataNode或RegionServer进程起不来

这个现象我也反复遇到过。进程起不来,最直接的排查路径是看日志,而不是反复执行启动脚本。Hadoop在挂掉之后,日志会记录异常线程栈;HBase同理,也会在启动日志里写明失败原因。

实际案例中有两个高频原因。第一个是磁盘目录权限不对,DataNode在/opt/data/hdfs/dn目录下创建块文件失败,错误信息里会出现Permission denied。解决方法是确定这个目录归属于hadoop用户,并保证从节点目录路径和配置完全一致。第二个是端口被占用,NameNode的8020端口如果已经被某些进程占用,DataNode连接不上真正的NameNode,表现为DataNode起来了又马上退出。

排查端口占用可以这样:

ss -lntup | grep 8020

一旦发现可疑进程,不要武断地杀掉,先确认是不是其他服务占用。集群服务器上常见的错误就是有人把自己跑的Java服务占了8020端口,结果Hadoop怎么起都不对。

5.3 内存溢出与资源争抢的调优

三台4G内存的机器,Hadoop和HBase同时跑,内存压力是比较大的。我的经验是先给系统留1G,剩下的再分配给各进程。YARN的NodeManager如果分配了过多的container内存,同时HMaster和RegionServer又把堆撑满,机器整体就会卡成PPT。

几个可以落地的调优点:

  • 适当降低HBase的HBASE_HEAPSIZE,从默认的几个G调成1G或2G。
  • 修改yarn-site.xml里的yarn.nodemanager.resource.memory-mb,不要让NodeManager把内存全部承诺给container。
  • 检查mapred-site.xml里的mapreduce.map.memory.mb和mapreduce.reduce.memory.mb,默认值通常是1G左右,如果不够需要调大,如果集群资源紧张就要调小。

另外补充一个很多人容易忽略的点:swap分区的使用。在大数据集群节点上,内存不够时会大量使用swap,JVM挂起和GC停顿现象会非常明显。如果实验环境允许,建议关掉swap,或者在调优时优先保证关键进程的堆内内存。

5.4 ZooKeeper与系统层面的坑

HBase自带的ZooKeeper虽然方便,但也引入了几个易踩的坑。最典型的是数据目录写在/tmp下,重启后丢失。我把hbase.zookeeper.property.dataDir改到/opt/data/zookeeper后,这类问题基本绝迹。

另外时间同步问题也很关键。NameNode、DataNode和RegionServer之间对时间偏差的容忍度有限。如果三台机器各差几十秒,HBase的ZooKeeper会话会频繁超时,用着用着RegionServer就掉线。安排一个简单的NTP时间同步服务,或者至少启动前手动date -s校准一下,能省下很多排查时间。

从整体来看,搭建这套环境的成功率不高,很多时候不是因为你不会配置,而是因为某个基础项没有打牢。我自己的习惯是先做最小化验证,再叠加服务。先把单机伪分布式跑通,再扩成多节点,最后接HBase,每增加一层组件,都先确认上一层是健康的。这套思路在后面排查问题时帮了不少忙。

最后再分享一个小技巧。整套环境搭好后,把启动顺序固定下来:先启动HDFS,再启动YARN,确认HDFS健康后启动HBase。关机或维护时反过来,先停HBase,再停YARN,最后停HDFS。别小看这个顺序,HBase如果先于HDFS退出,下一次启动时在HDFS上留下的临时文件很可能让RegionServer恢复异常。按照这套顺序操作,我整个实验期间几乎没有再遇到环境级故障。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询