1. 为什么我要在本地从零搭一套 Hadoop + HBase 集群
先把结论摆在前面:如果你只想“跑个 Demo”,单机伪分布式就够了;但如果你想真正理解大数据底座是怎么运转的,想搞清楚一张表的数据到底落在哪台机器、哪个目录、哪个 Region 上,那必须老老实实搭一套多节点的 Hadoop + HBase 集群。我自己前前后后搭过不下十遍,从最早被 SSH 免密卡一整天,到后来被 HBase 的时钟漂移坑到怀疑人生,踩过的坑基本能写一本小册子。这篇就把整套流程、背后的原理、以及那些文档里不会写的细节,一次性讲透。
Hadoop 集群解决的是“海量数据怎么存、怎么算”的问题,核心是 HDFS(分布式文件系统)和 YARN(资源调度);HBase 则建立在 HDFS 之上,解决的是“海量数据怎么随机实时读写”的问题,它是一个分布式的列式数据库。两者组合起来,就是很多公司早期大数据平台的标配底座。这套东西适合谁看?适合刚接触大数据的在校同学、需要自己搭环境做验证的后端或数据开发、以及想搞明白“底层到底发生了什么”的运维同学。哪怕你之前只会在云上点几下买个托管服务,跟着走一遍,你对数据分布、副本、Region 分裂这些概念的理解会完全不一样。
我这次搭建的目标很明确:三台机器组成的最小可用集群,一台做 Master(NameNode + HMaster),两台做 Slave(DataNode + RegionServer),全部用普通用户操作,不碰 root,方便后续迁移和权限管理。下面所有内容都围绕这个目标展开,参数、目录、配置项我都会给出具体值和选择理由,你可以直接抄,也可以按自己机器情况微调。
2. 集群规划与前置准备:别急着装,先把地基打牢
2.1 节点角色划分与硬件资源估算
很多人一上来就tar -zxvf解压安装包,结果装到一半发现内存不够、端口冲突、主机名解析不了。我习惯先把规划表列出来,心里有数再动手。三节点规划如下:
| 主机名 | 角色 | 建议内存 | 建议磁盘 | 说明 |
|---|---|---|---|---|
| node1 | NameNode、DataNode、HMaster、RegionServer | 8G 起 | 100G | 主节点也兼做工作节点,省机器 |
| node2 | DataNode、RegionServer、NodeManager | 8G 起 | 100G | 纯工作节点 |
| node3 | DataNode、RegionServer、NodeManager | 8G 起 | 100G | 纯工作节点 |
为什么主节点也兼做 DataNode?因为三台机器的小集群,如果 Master 只做管理不存数据,资源浪费太严重。生产环境当然要分离,但学习和验证阶段,合并部署完全没问题,Hadoop 本身也支持这种角色叠加。内存方面,NameNode 大概吃 2G 到 4G,HMaster 吃 1G 到 2G,剩下的留给 DataNode 和 RegionServer。如果你机器只有 4G 内存,建议把副本数降到 1,并且关掉 YARN,只玩 HDFS + HBase。
磁盘规划上有个经验:不要把数据目录放在系统盘。我一般会挂一块独立数据盘,挂载到/data,然后所有 Hadoop 和 HBase 的数据目录都指向/data/hadoop和/data/hbase。这样做的好处是系统盘满了不影响数据,迁移的时候直接摘盘就行。如果你只有一块盘,那至少把数据目录和安装目录分开,别混在一起。
2.2 系统层面的必做项:主机名、hosts、防火墙、时钟
这几项是“不做必出事”的典型。我按重要性排个序。
第一是主机名和 hosts 解析。三台机器分别执行hostnamectl set-hostname node1(以此类推),然后编辑每台机器的/etc/hosts,把三台机器的 IP 和主机名对应写进去。为什么必须做?因为 Hadoop 和 HBase 的配置文件里到处是主机名,如果解析不了,节点之间握手直接失败,报错还特别隐晦,经常是“Connection refused”或者“Unknown host”,新手很容易以为是服务没起来。
第二是关闭防火墙或者放行端口。学习环境我建议直接systemctl stop firewalld && systemctl disable firewalld,省得一个个端口去开。生产环境当然要用白名单,但那是另一回事。Hadoop 用到的端口很多,NameNode 的 8020、9870,DataNode 的 9866,YARN 的 8088、8032,HBase 的 16000、16010、16020、16030,全都要通。
第三是时钟同步。这一条是 HBase 的命门。HBase 对集群内各节点的时间差非常敏感,默认超过 30 秒就会导致 RegionServer 启动失败或者 Region 分配异常。我一般用chronyd或者ntpd让三台机器指向同一个时间源。如果你在纯内网环境,可以选一台机器做时间服务器,其他两台同步它。命令大概是chronyc sources查看同步状态,确认^*标记出现才算成功。
第四是SSH 免密登录。在 node1 上生成密钥对ssh-keygen -t rsa,然后ssh-copy-id node1、ssh-copy-id node2、ssh-copy-id node3,把公钥分发到三台机器。为什么要免密?因为 Hadoop 的启动脚本start-dfs.sh会通过 SSH 去各节点拉起进程,如果每次都要输密码,脚本根本跑不下去。这里有个细节:node1 要能免密登录自己,很多人只配了到 node2、node3 的免密,忘了自己,结果启动时报“Permission denied”。
2.3 JDK 安装与 JAVA_HOME 的坑
Hadoop 3.x 和 HBase 2.x 都要求 JDK 8 或 JDK 11。我实测下来 JDK 8 最稳,兼容性最好。安装方式我用的是解压版,不用包管理器,因为路径可控。解压到/opt/module/jdk1.8,然后配置环境变量。
这里有个高频坑点:JAVA_HOME的配置要写在多个地方。除了/etc/profile里的全局变量,Hadoop 的hadoop-env.sh、yarn-env.sh、mapred-env.sh,以及 HBase 的hbase-env.sh,都要显式指定JAVA_HOME。为什么?因为这些脚本执行时不一定加载了/etc/profile,尤其是通过 SSH 非交互式登录启动的进程。我踩过的坑就是:手动执行hadoop version正常,但start-dfs.sh启动的进程报“JAVA_HOME is not set”。解决办法就是在每个 env 脚本里写死绝对路径。
提示:配置完环境变量后,用
source /etc/profile生效,然后echo $JAVA_HOME确认。三台机器都要做,别只做一台。
3. Hadoop 集群搭建:从配置到启动的完整链路
3.1 核心配置文件逐个拆解
Hadoop 的配置集中在$HADOOP_HOME/etc/hadoop目录下。我把需要改的文件和关键参数列出来,每个都说明为什么这么设。
core-site.xml是全局核心配置,主要设两个东西:默认文件系统和临时目录。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node1:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> </configuration>fs.defaultFS指向 NameNode 的地址,所有客户端默认往这里写。hadoop.tmp.dir是很多临时文件的根目录,默认在/tmp下,系统重启就没了,所以必须改到数据盘。
hdfs-site.xml管 HDFS 的具体行为。
<configuration> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/data/hadoop/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/data/hadoop/datanode</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>node2:9868</value> </property> </configuration>副本数设 2 是因为三节点集群,设 3 会导致每个块都要全量复制,写入变慢,学习环境没必要。name.dir和data.dir分开,方便排查问题。SecondaryNameNode 我放在 node2,避免和 NameNode 抢资源。
yarn-site.xml管资源调度。
<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>node1</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>4096</value> </property> </configuration>aux-services必须设成mapreduce_shuffle,否则 MapReduce 任务跑不起来。resource.memory-mb是每个 NodeManager 能用的内存,我设 4G,留一半给系统和其他进程。
workers 文件(老版本叫 slaves)列出所有 DataNode 节点,每行一个主机名:node1、node2、node3。这个文件决定了start-dfs.sh会去哪些机器启动 DataNode。
3.2 格式化与启动:顺序错了就重来
配置分发到三台机器后(我用scp或者rsync),第一件事是格式化 NameNode:在 node1 上执行hdfs namenode -format。注意,这个命令只能执行一次,重复执行会导致集群 ID 不一致,DataNode 全部起不来。如果你不小心格式化了两次,最简单的办法是把所有节点的name.dir和data.dir清空,重新格式化。
启动顺序是:先start-dfs.sh,再start-yarn.sh。启动脚本在$HADOOP_HOME/sbin下。执行完用jps检查进程。node1 上应该看到 NameNode、DataNode、ResourceManager、NodeManager;node2 和 node3 上应该看到 DataNode、NodeManager。如果某个进程没起来,去对应的日志目录$HADOOP_HOME/logs下看.log文件,错误信息一般都在最后几十行。
注意:
jps看不到进程不代表进程没起,有可能是权限问题导致jps读不到。用ps -ef | grep java再确认一遍。
3.3 验证 HDFS 是否真的可用
启动成功后,别急着装 HBase,先把 HDFS 验证透。我一般做三件事:
第一,浏览器打开http://node1:9870,看 NameNode 的 Web UI,确认三个 DataNode 都在线,容量正常。
第二,命令行建目录、传文件、读文件:
hdfs dfs -mkdir -p /test/input hdfs dfs -put /etc/profile /test/input/ hdfs dfs -cat /test/input/profile第三,也是最关键的,去看数据到底落在哪。在 node2 上执行ls /data/hadoop/datanode/current/BP-xxx/current/finalized/subdir0/subdir0/,你能看到一个个blk_开头的文件。这就是你的数据块。用hdfs fsck /test/input/profile -files -blocks -locations可以看到这个文件被切成了几个块、每个块在哪几个节点上。这一步做完,你对“分布式存储”的理解就从抽象变具体了。
4. HBase 集群搭建:建立在 HDFS 之上的实时数据库
4.1 HBase 与 Hadoop 的依赖关系梳理
HBase 的架构里,HMaster 负责管理表的元数据和 Region 的分配,HRegionServer 负责实际的数据读写,而数据最终是存在 HDFS 上的。所以 HBase 的搭建必须建立在已经跑通的 HDFS 之上。这里有个关键选择:HBase 用自带的 ZooKeeper 还是独立部署 ZooKeeper。
我强烈建议独立部署 ZooKeeper,原因有三:一是自带 ZooKeeper 在集群模式下容易出问题,二是独立部署方便后续其他组件复用,三是出故障时排查边界清晰。ZooKeeper 我部署在 node1、node2、node3 三台机器上,组成一个三节点的仲裁集群。ZooKeeper 的myid文件、zoo.cfg里的server.x配置,这些基础操作这里不展开,核心是保证zkServer.sh status能看到一个 Leader 两个 Follower。
4.2 hbase-site.xml 的关键参数与选择理由
HBase 的配置核心在conf/hbase-site.xml。我把关键项列出来:
<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://node1:8020/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>node1,node2,node3</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/data/zookeeper/data</value> </property> <property> <name>hbase.unsafe.stream.capability.enforce</name> <value>false</value> </property> </configuration>hbase.rootdir指向 HDFS 上的/hbase目录,HBase 的所有数据最终都写在这里。cluster.distributed必须为 true,否则就是单机模式。zookeeper.quorum列出 ZooKeeper 集群地址。最后那个unsafe.stream.capability.enforce设 false,是为了兼容某些文件系统不支持 hflush 的情况,学习环境设上能避免一些启动报错。
还有一个文件是regionservers,列出所有 HRegionServer 节点:node1、node2、node3。这个文件的作用和 Hadoop 的 workers 类似。
4.3 启动 HBase 与验证读写
启动命令是start-hbase.sh,在 node1 上执行。启动后jps应该能看到 HMaster(node1)和 HRegionServer(三台都有)。Web UI 在http://node1:16010。
验证环节我习惯用 HBase Shell 走一遍完整流程:
hbase shell create 'test_table', 'cf1' put 'test_table', 'row1', 'cf1:name', 'zhangsan' get 'test_table', 'row1' scan 'test_table'建表、写入、读取、扫描,四步都通了,说明集群基本可用。这时候你可以再去 HDFS 上看/hbase/data/default/test_table目录,能看到 Region 的目录结构,里面有cf1列族的目录,再里面就是 HFile 文件。这就是 HBase 数据在 HDFS 上的真实形态。
提示:HBase Shell 里
list命令能看到所有表,describe 'test_table'能看到表的列族和 Region 信息。如果建表时报PleaseHoldException,别慌,这是 HMaster 还在初始化,等几十秒再试。
5. 踩坑实录:那些让我熬夜的典型问题与排查思路
5.1 启动失败类问题的排查路径
问题一:DataNode 起不来,日志报“Incompatible clusterIDs”。这是格式化 NameNode 后,DataNode 的集群 ID 和 NameNode 不一致导致的。解决办法是删除 DataNode 的数据目录,重新启动。根本原因是格式化操作会生成新的集群 ID,而老的 DataNode 还记着旧 ID。
问题二:HRegionServer 启动后马上挂掉,日志报“Clock skew too great”。这就是前面强调的时钟同步问题。HBase 默认允许的时间差是 30 秒,超过就拒绝启动。用date命令对比三台机器的时间,差得多就赶紧同步。
问题三:HMaster 起不来,报“Connection refused”连 ZooKeeper。先确认 ZooKeeper 三台都起来了吗?zkServer.sh status看状态。再确认hbase-site.xml里的quorum写对了吗?主机名和 hosts 对得上吗?我遇到过 hosts 里写错一个字母,排查了两小时。
5.2 运行过程中的性能与稳定性问题
问题四:写入越来越慢,Region 频繁分裂。这是 HBase 的常见现象。默认情况下,一个 Region 超过 10G 就会分裂。如果你的 RowKey 设计是单调递增的,所有写入都会集中在一个 Region 上,导致热点。解决办法是 RowKey 加盐或者哈希前缀,把写入打散到多个 Region。这个不是搭建问题,但搭完集群一定要知道。
问题五:HDFS 空间满了,HBase 写入失败。三节点小集群磁盘很容易满。我一般会设置 HDFS 的回收站保留时间短一点,定期清理。另外,HBase 的 WAL 日志和 HFile 都会占空间,hbase.hregion.memstore.flush.size这个参数控制 MemStore 多大时刷写,默认 128M,调小一点能减少内存压力,但会增加 HFile 数量。
问题六:YARN 和 HBase 抢内存。如果 HBase 和 YARN 部署在同一批机器上,NodeManager 的内存设置太大,会导致 RegionServer 内存不足被系统杀掉。我的经验是给 NodeManager 留 4G,RegionServer 留 4G,系统留 2G,这样 8G 内存的机器刚好够用。
5.3 常见问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方向 |
|---|---|---|---|
| DataNode 未启动 | 集群 ID 不一致 | 查看 DataNode 日志 | 清空数据目录重启 |
| RegionServer 闪退 | 时钟不同步 | date对比各节点 | 配置时间同步 |
| HMaster 连不上 ZK | ZK 未启动或配置错 | zkServer.sh status | 检查 quorum 配置 |
| 写入报错 | HDFS 空间满 | hdfs dfsadmin -report | 清理或扩容 |
| 启动脚本卡住 | SSH 免密未配好 | ssh node2测试 | 重新分发公钥 |
| Web UI 打不开 | 防火墙未关 | telnet node1 9870 | 关闭防火墙或放行端口 |
6. 搭完之后还能做什么:从能跑到好用的进阶方向
集群搭起来只是第一步,真正让它“好用”还有很多事可以做。我分享几个我后续会做的优化方向,你可以按需选择。
第一是配置高可用。目前 NameNode 和 HMaster 都是单点,挂了就全挂。生产环境要配 NameNode HA 和 HMaster HA,需要额外的 JournalNode 和 ZooKeeper。三节点集群做 HA 有点勉强,五节点比较合适。
第二是调优 JVM 和 GC。HBase 的 RegionServer 默认堆内存可能不够用,我一般会改hbase-env.sh里的HBASE_HEAPSIZE,设成 4G 左右,并且用 G1 GC 替代 CMS,减少 Full GC 停顿。
第三是集成 Hive 或 Spark。HBase 本身查询能力有限,通过 Hive 外部表或者 Spark 的 HBase Connector,可以用 SQL 或 DataFrame 的方式分析 HBase 数据,实用性大大提升。
第四是监控告警。用 Prometheus + Grafana 采集 HBase 和 Hadoop 的 JMX 指标,能看到 Region 数量、请求延迟、队列长度这些关键数据。没有监控的集群就是盲跑,出了问题只能靠猜。
我个人在实际操作中的体会是:搭建过程本身不难,难的是理解每个配置项背后的“为什么”。你把dfs.replication为什么设 2、hbase.rootdir为什么指向 HDFS、时钟为什么必须同步这几个问题搞明白了,再遇到报错就不会慌,因为你知道该往哪个方向查。最后再分享一个小技巧:每次改完配置,先在一台机器上重启验证,没问题再分发到全部节点,这样能把问题范围缩小到单机,排查效率高很多。