
1. 为什么要在虚拟机里搭这套三件套很多人第一次接触大数据集群都是从在一台物理机上用虚拟机模拟多台服务器开始的。原因很直接真实的多节点集群需要多台机器、交换机、网络配置成本高、门槛高而用虚拟机可以在自己的一台电脑上跑出三台甚至五台逻辑服务器把分布式系统的核心机制完整地演练一遍。Zookeeper Hadoop HBase 这个组合是大数据生态里最经典的入门三件套Hadoop 提供分布式存储HDFS和分布式计算MapReduce/YARNHBase 建立在 HDFS 之上提供列式存储和随机读写能力而 Zookeeper 则是整个集群的协调中枢负责选主、配置管理、分布式锁等元数据一致性工作。这套组合的价值在于它几乎覆盖了分布式系统所有核心概念主从架构、数据分片、副本机制、一致性协议、故障转移。你在虚拟机里踩过的每一个坑——时间不同步、主机名解析失败、端口占用、防火墙拦截——在真实生产环境里同样会遇到只是规模不同。所以别小看这个玩具集群它是理解分布式系统最扎实的路径。这篇文章面向的是有一定 Linux 基础、想动手搭一套完整集群的开发者。我会从虚拟机规划讲起一路走到 HBase 建表验证把每一步的意图、参数含义、常见报错都讲清楚。整套流程我在不同机器上复现过多次下面这些配置和顺序是验证过能跑通的。需要提前说明的是本文基于 Ubuntu 18.04 这个长期支持版本组件版本选择的是 Hadoop 3.x、HBase 2.x 和 Zookeeper 3.4.x 这一套互相兼容的组合。版本兼容性是这套集群最容易翻车的地方后面会专门讲。2. 虚拟机规划与系统准备2.1 三节点角色分配与资源估算集群规划是第一步也是最容易被忽视的一步。很多人上来就装装到一半发现内存不够、角色冲突。我的建议是用三台虚拟机角色分配如下主机名IP 示例角色内存建议master192.168.56.101NameNode、ResourceManager、HMaster、ZK4GBslave1192.168.56.102DataNode、NodeManager、HRegionServer、ZK2GBslave2192.168.56.103DataNode、NodeManager、HRegionServer、ZK2GBZookeeper 采用三节点部署是因为它需要奇数个节点来构成仲裁Quorum。三节点允许挂掉一台集群仍能正常工作如果只部署两台挂一台就失去多数派整个 ZK 不可用。这是 ZK 部署的铁律不是随便定的。内存方面Hadoop 的 NameNode 和 HBase 的 HMaster 都是吃内存的角色master 节点给 4GB 比较稳妥两个 slave 各 2GB。如果你宿主机内存只有 8GB建议把 slave 降到 1.5GB 并关闭不必要的服务但体验会比较卡。磁盘每台给 20GB 起步HDFS 副本默认 3 份空间要留够。2.2 网络模式选择与静态 IP 配置虚拟机网络模式有三种桥接、NAT、仅主机。搭集群我强烈推荐仅主机模式Host-Only或者NAT 固定 IP。原因是集群节点之间必须能通过固定 IP 互相通信如果用 DHCP 动态分配每次重启 IP 变了所有配置文件都要改非常痛苦。配置静态 IP 在 Ubuntu 18.04 上和旧版本不一样它用的是 netplan。编辑/etc/netplan/下的 yaml 文件network: version: 2 ethernets: ens33: dhcp4: no addresses: [192.168.56.101/24] gateway4: 192.168.56.1 nameservers: addresses: [8.8.8.8]改完执行sudo netplan apply。这里有个坑网卡名不一定是 ens33用ip addr先确认。另外 yaml 对缩进极其敏感必须用空格不能用 Tab缩进错了 netplan 会直接报错。2.3 主机名与 hosts 解析三台机器分别设置主机名sudo hostnamectl set-hostname master然后在每台机器的/etc/hosts里加上三行映射192.168.56.101 master 192.168.56.102 slave1 192.168.56.103 slave2这一步绝对不能省。Hadoop 和 HBase 的配置文件里到处用主机名互相引用如果解析不了启动时会报UnknownHostException。我见过太多人卡在这里明明 IP 能 ping 通但服务就是起不来最后发现是 hosts 没配。2.4 时间同步最隐蔽的杀手分布式集群对时间一致性要求极高。Zookeeper 的选举、HBase 的版本控制都依赖时间戳。如果节点之间时间差超过阈值会出现各种诡异问题比如 RegionServer 被踢出集群、ZK 会话超时。Ubuntu 18.04 自带 systemd-timesyncd可以统一指向一个内网时间源或者干脆让 slave 同步 master 的时间。简单做法是安装 ntpsudo apt install ntp -y在 slave 上编辑/etc/ntp.conf把 server 指向 master。不过更省事的办法是直接用date -s手动对齐一次测试环境够用了。生产环境必须上正经的时间同步服务。2.5 创建统一用户与 SSH 免密不要用 root 跑这些服务创建一个专用用户sudo useradd -m -s /bin/bash hadoop sudo passwd hadoop然后给这个用户配置 sudo 权限并配置三台机器之间的 SSH 免密登录。免密的原理是把公钥追加到对方的~/.ssh/authorized_keys。先在 master 上生成密钥ssh-keygen -t rsa一路回车然后把公钥分发到三台机器包括自己ssh-copy-id hadoopmaster ssh-copy-id hadoopslave1 ssh-copy-id hadoopslave2免密的意义在于Hadoop 的启动脚本会通过 SSH 去各节点拉起进程如果每次都要输密码脚本根本没法自动化。配置完记得ssh slave1测试一下能直接进去才算成功。3. JDK 与组件版本兼容性把关3.1 JDK 安装与 JAVA_HOME 设置Hadoop 3.x 和 HBase 2.x 都要求 JDK 8。Ubuntu 18.04 仓库里的 openjdk-8 可以直接用sudo apt install openjdk-8-jdk -y装完确认路径通常在/usr/lib/jvm/java-8-openjdk-amd64。然后配置环境变量编辑~/.bashrc或/etc/profileexport JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 export PATH$JAVA_HOME/bin:$PATHsource之后用java -version验证。这里要注意Hadoop 的配置文件里也会引用 JAVA_HOME如果环境变量和配置文件不一致会报找不到 Java 的错误。3.2 版本兼容性矩阵别让版本毁掉一切这是整套集群最关键的隐性知识。三个组件的版本必须互相兼容否则会出现各种莫名其妙的类冲突。我验证过的稳定组合是组件版本说明JDK1.8三件套共同要求Hadoop3.2.1支持 JDK8稳定HBase2.2.4兼容 Hadoop 3.xZookeeper3.4.14HBase 内置版本一致特别提醒HBase 对 Hadoop 版本很敏感。HBase 2.x 官方明确支持 Hadoop 3.x但如果你用 HBase 1.x 配 Hadoop 3.x大概率会在启动时抛NoSuchMethodError。另外 HBase 自带一个 Zookeeper但生产上我们通常用独立部署的 ZK所以要在 HBase 配置里关掉它自带的后面讲。3.3 目录规划与权限统一规划一个安装根目录比如/opt/bigdata把三个组件都解压进去sudo mkdir -p /opt/bigdata sudo chown -R hadoop:hadoop /opt/bigdata数据目录单独规划比如/data/hadoop、/data/hbase、/data/zookeeper和安装目录分开。这样做的好处是数据盘可以独立挂载、独立扩容也方便备份。所有目录的属主都设成 hadoop 用户避免权限问题。4. Zookeeper 集群搭建与验证4.1 解压配置与 myid 机制把 Zookeeper 解压到/opt/bigdata/zookeeper进入 conf 目录复制一份配置模板cp zoo_sample.cfg zoo.cfg编辑 zoo.cfg核心配置如下tickTime2000 initLimit10 syncLimit5 dataDir/data/zookeeper clientPort2181 server.1master:2888:3888 server.2slave1:2888:3888 server.3slave2:2888:3888这里解释几个关键参数。tickTime是 ZK 的基本时间单元2000 毫秒。initLimit表示 follower 启动时与 leader 同步数据允许的最大心跳数10 个 tick 就是 20 秒。syncLimit是运行时 follower 与 leader 之间允许的最大延迟5 个 tick 即 10 秒。这两个值如果设太小网络稍慢就会导致节点被踢出。server.N里的 N 就是节点的 myid。需要在每台机器的 dataDir 下创建一个名为myid的文件内容就是对应的数字。master 写 1slave1 写 2slave2 写 3。这个 myid 必须和 zoo.cfg 里的编号一一对应写错了会导致选举混乱。4.2 启动顺序与状态检查三台机器都配置好后在每台上分别启动/opt/bigdata/zookeeper/bin/zkServer.sh start启动后检查状态zkServer.sh status正常情况下一台显示leader另外两台显示follower。如果三台都显示standalone或者报错说明集群没组起来。常见原因是防火墙没关、myid 不对、或者 hosts 解析有问题。提示Ubuntu 默认可能开了 ufw 防火墙测试环境建议直接sudo ufw disable或者放行 2181、2888、3888 端口。4.3 用 zkCli 做一次真实读写光看状态不够用客户端连上去实际操作一下zkCli.sh -server master:2181进去之后创建节点、读取节点create /test hello get /test如果能在 master 上创建在 slave 上也能读到说明集群数据同步正常。这一步验证通过Zookeeper 就算搭好了。ZK 是整个集群的地基它不稳后面 Hadoop 和 HBase 都会跟着出问题所以务必确认无误再往下走。5. Hadoop 集群配置与启动5.1 核心配置文件逐个拆解Hadoop 的配置集中在etc/hadoop目录下需要改的文件有五个。先看core-site.xmlconfiguration property namefs.defaultFS/name valuehdfs://master:9000/value /property property namehadoop.tmp.dir/name value/data/hadoop/tmp/value /property /configurationfs.defaultFS指定 HDFS 的访问入口客户端靠它找到 NameNode。hadoop.tmp.dir是临时目录默认在 /tmp 下重启系统会被清空导致集群数据丢失所以必须改到持久化目录。hdfs-site.xml配置副本数和数据目录configuration property namedfs.replication/name value3/value /property property namedfs.namenode.name.dir/name value/data/hadoop/name/value /property property namedfs.datanode.data.dir/name value/data/hadoop/data/value /property /configuration副本数设 3因为我们正好有三个 DataNode每个节点存一份容错性最好。如果只有两个 DataNode 却设 3会一直有副本处于欠复制状态。yarn-site.xml配置资源调度configuration property nameyarn.resourcemanager.hostname/name valuemaster/value /property property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property /configurationmapred-site.xml指定计算框架用 YARNconfiguration property namemapreduce.framework.name/name valueyarn/value /property /configuration最后是workers文件旧版本叫 slaves列出所有 DataNode 主机名slave1 slave2注意 master 不参与数据存储只做 NameNode这样角色更清晰。5.2 格式化与启动的先后逻辑配置分发到三台机器后第一次启动前必须格式化 NameNodehdfs namenode -format格式化的本质是初始化 NameNode 的元数据目录生成 fsimage。这一步只能做一次重复格式化会导致 DataNode 的 clusterID 和 NameNode 不一致集群起不来。如果确实要重新格式化必须把所有节点的数据目录清空。格式化完成后启动 HDFSstart-dfs.sh再启动 YARNstart-yarn.sh启动脚本会根据 workers 文件自动 SSH 到各节点拉起进程。启动后用jps检查master 上应该有 NameNode、ResourceManagerslave 上应该有 DataNode、NodeManager。5.3 Web UI 与常见启动故障Hadoop 提供了 Web 界面NameNode 在http://master:9870Hadoop 3.x 端口从 50070 改成了 9870YARN 在http://master:8088。打开能看到集群概况、节点列表、存储使用率。启动失败最常见的原因有三个一是 SSH 免密没配好脚本卡在输密码二是 JAVA_HOME 在hadoop-env.sh里没设对三是数据目录权限不对。排查时先看日志日志在logs目录下NameNode 的日志会明确告诉你哪里出错。注意如果多次格式化过 NameNodeDataNode 启动后会报Incompatible clusterIDs解决办法是删掉所有节点的 data 目录重新格式化或者手动把 DataNode 的 VERSION 文件里的 clusterID 改成和 NameNode 一致。6. HBase 集群部署与联调6.1 让 HBase 使用外部 ZookeeperHBase 默认会自己拉起一个 ZK但我们已经独立部署了 ZK 集群所以要告诉 HBase 用外部的。编辑conf/hbase-site.xmlconfiguration property namehbase.rootdir/name valuehdfs://master:9000/hbase/value /property property namehbase.cluster.distributed/name valuetrue/value /property property namehbase.zookeeper.quorum/name valuemaster,slave1,slave2/value /property property namehbase.zookeeper.property.dataDir/name value/data/zookeeper/value /property /configurationhbase.rootdir指向 HDFS 上的存储路径HBase 的所有数据最终都落在 HDFS。hbase.cluster.distributed设为 true 表示分布式模式。hbase.zookeeper.quorum列出 ZK 集群地址。关键点不要设置hbase.zookeeper.property.clientPort之外的 ZK 相关配置去启动内置 ZK只要 quorum 指向外部HBase 就会用外部的。6.2 regionservers 与启动脚本编辑conf/regionservers写入slave1 slave2master 作为 HMaster 不存数据。启动 HBasestart-hbase.sh启动后 master 上会有 HMaster 进程slave 上会有 HRegionServer。用jps确认。如果 HRegionServer 起不来八成是 HDFS 没启动或者 rootdir 路径不对。6.3 建表验证与数据落盘确认进入 HBase shellhbase shell创建一个测试表create test_table, cf1然后插入数据、查询put test_table, row1, cf1:name, zhangsan get test_table, row1能查到数据说明 HBase 读写正常。再回到 HDFS 上看数据是否真的落盘hdfs dfs -ls /hbase/data/default/test_table能看到目录结构说明 HBase 的数据确实写到了 HDFS。这一步把三个组件串起来了HBase 通过 ZK 协调把数据存到 HDFS。整个链路打通集群就算真正搭成了。7. 那些让我熬夜的坑与排查思路7.1 端口占用与进程残留虚拟机里反复启停服务最容易遇到端口被占用。比如上次没停干净ZK 的 2181 还被占着新进程起不来。排查用netstat -tlnp | grep 2181找到 PID 后 kill 掉。更彻底的办法是写个停止脚本按顺序停先停 HBase再停 YARN/HDFS最后停 ZK。启动顺序反过来。顺序错了会导致数据不一致。7.2 时间不同步引发的诡异故障前面提过时间同步这里说个真实场景我有一次 slave2 的时间比 master 慢了 5 分钟结果 HBase 的 RegionServer 频繁掉线日志里全是ClockOutOfSyncException。因为 HBase 判断 RegionServer 是否存活依赖时间戳时间差太大就认为节点失联。对齐时间后立刻恢复正常。这个坑非常隐蔽因为服务能启动只是运行中随机出问题。7.3 内存不足导致的 OOM虚拟机内存给少了跑着跑着进程被系统 OOM Killer 干掉。表现是某个节点进程突然消失日志里没有明显错误。用dmesg | grep -i kill能看到 OOM 记录。解决办法是给足内存或者调小各组件的堆内存。比如在hbase-env.sh里设置export HBASE_HEAPSIZE512在hadoop-env.sh里设置export HDFS_NAMENODE_OPTS-Xmx1024m。7.4 主机名解析与防火墙这两个是新手最常踩的。主机名解析问题表现为UnknownHostException防火墙问题表现为连接超时。测试环境直接关防火墙最省事生产环境则要精确放行端口。hosts 文件三台机器必须完全一致改了一台别忘了另外两台。8. 集群跑起来之后还能做什么集群搭好只是起点。接下来可以做的验证和扩展有很多跑一个 WordCount 任务验证 MapReduce用 HBase 的 Java API 写个简单的增删改查程序给 HDFS 上传大文件测试吞吐甚至模拟节点故障手动 kill 掉一个 DataNode 观察副本自动恢复的过程。这些操作能让你真正理解分布式系统的容错机制而不是停留在能启动的层面。我个人在实际操作中的体会是搭集群这件事看十遍教程不如自己动手踩一遍坑。配置文件里的每一个参数背后都有设计意图只有当你改错它、看到报错、再改回来才算真正理解。虚拟机的好处就是可以随便折腾搞坏了删掉重来成本几乎为零。把这套三件套跑通你对大数据的理解就上了一个台阶。