1. 为什么我劝你亲手搭一遍Hadoop完全分布式集群
刚入行那会儿,我对Hadoop的理解停留在“能跑WordCount就行”的层面。直到有一次帮朋友处理一个日志分析项目,单机模式跑10GB数据直接卡死,我才意识到伪分布式和完全分布式之间的鸿沟不是靠调参能填平的。后来花了整整两个周末,在三台旧笔记本上反复折腾,踩了不下二十个坑,才把完全分布式集群跑通。这段经历让我明白一件事:Hadoop完全分布式集群搭建是每个大数据从业者绕不过去的成人礼,你可以不用,但不能不会。
这篇文章面向的是有一定Linux基础、想从伪分布式进阶到完全分布式的朋友。我会把三台机器从零开始搭建Hadoop集群的完整过程拆开揉碎,包括网络规划、SSH免密、配置文件逐项解读、启动验证、以及那些教程里不会写的坑。整套方案基于Hadoop 3.x稳定版,三节点架构(1个NameNode + 2个DataNode),适合中小规模的数据处理学习和测试。你跟着走一遍,大概率能避开我当年踩过的雷。
提示:本文所有操作基于CentOS 7环境,Hadoop版本为3.3.x。如果你用的是Ubuntu,命令略有差异但思路完全一致。
2. 集群规划与基础环境准备
2.1 三节点架构的角色分配逻辑
很多人一上来就问“我要搭几台机器”,其实这个问题应该反过来想:你的数据量有多大、计算任务有多重、预算有多少。对于学习和小型项目,三台是最低成本的完全分布式方案——少于三台就失去了分布式的意义,多于三台对新手来说管理成本陡增。
我推荐的角色分配是这样的:
| 主机名 | IP地址 | 角色 | 内存建议 |
|---|---|---|---|
| master | 192.168.1.100 | NameNode + ResourceManager + SecondaryNameNode | 4GB+ |
| slave1 | 192.168.1.101 | DataNode + NodeManager | 2GB+ |
| slave2 | 192.168.1.102 | DataNode + NodeManager | 2GB+ |
为什么把SecondaryNameNode放在master上?因为它本身不承担计算任务,只是定期合并fsimage和edits文件,资源消耗不大。放在master上省一台机器,对学习环境来说完全够用。生产环境当然要分开,但那是另一回事。
注意:主机名千万不要用下划线,Hadoop对下划线敏感,某些组件会直接报错。用短横线或者纯字母数字组合最稳妥。
2.2 静态IP配置与主机名映射
虚拟机克隆出来的机器默认是DHCP,IP会变,集群直接崩。所以第一步就是把三台机器的网络固定下来。
在每台机器上编辑网卡配置:
vi /etc/sysconfig/network-scripts/ifcfg-ens33关键参数如下:
BOOTPROTO=static ONBOOT=yes IPADDR=192.168.1.100 NETMASK=255.255.255.0 GATEWAY=192.168.1.2 DNS1=114.114.114.114改完之后重启网络服务:
systemctl restart network然后配置主机名映射,三台机器都要做:
vi /etc/hosts追加内容:
192.168.1.100 master 192.168.1.101 slave1 192.168.1.102 slave2这一步看起来简单,但后面SSH免密、Hadoop各组件通信全靠它。我见过有人图省事只配了IP没配主机名,结果DataNode注册不上,日志里全是“UnknownHostException”,排查半天才发现是这里的问题。
2.3 防火墙与SELinux的关闭策略
Hadoop各组件之间通信端口非常多,NameNode的8020、9870,DataNode的9866,ResourceManager的8032、8088等等。学习环境最省事的做法是直接关掉防火墙:
systemctl stop firewalld systemctl disable firewalldSELinux也要关,否则某些文件权限会莫名其妙被拒绝:
setenforce 0 vi /etc/selinux/config # 将SELINUX=enforcing改为SELINUX=disabled提示:生产环境当然不能这么干,应该按端口逐个放行。但学习阶段,先把集群跑起来比什么都重要,安全策略可以后面再补。
2.4 JDK安装与环境变量配置
Hadoop 3.x要求JDK 8或11,我习惯用JDK 8,兼容性最好。三台机器都要装。
tar -zxvf jdk-8u291-linux-x64.tar.gz -C /opt/module/ vi /etc/profile追加:
export JAVA_HOME=/opt/module/jdk1.8.0_291 export PATH=$JAVA_HOME/bin:$PATH生效并验证:
source /etc/profile java -version看到版本号输出就说明OK了。这里有个细节:JAVA_HOME的路径一定要写绝对路径,不要用~或者相对路径,Hadoop启动脚本里会直接引用这个变量,路径不对直接启动失败。
3. SSH免密登录与集群互信配置
3.1 为什么必须做免密
Hadoop的启动脚本会通过SSH远程登录到各个节点去启动进程。如果不做免密,你每启动一次集群就要输三次密码,脚本根本没法自动化。更关键的是,某些内部通信机制也依赖SSH通道,免密是硬性要求。
3.2 生成密钥对并分发公钥
在master上生成密钥对:
ssh-keygen -t rsa # 一路回车即可然后把公钥复制到三台机器(包括自己):
ssh-copy-id master ssh-copy-id slave1 ssh-copy-id slave2验证免密是否生效:
ssh slave1如果不需要密码直接登录进去,说明配置成功。然后exit退回来,把slave1和slave2也做同样的操作——它们也需要能免密登录到其他节点。
注意:ssh-copy-id第一次执行时会提示输入对方机器的密码,这是正常的。如果提示“Permission denied”,检查一下目标机器的SSH服务是否启动,以及当前用户是否有权限。
3.3 免密配置的常见坑
我遇到过最诡异的问题是:明明ssh-copy-id成功了,但ssh过去还是要密码。排查后发现是目标机器的authorized_keys文件权限不对。SSH对权限要求非常严格:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys还有一个坑是known_hosts文件。如果你重装过系统或者换过IP,旧的指纹会留在known_hosts里,导致SSH连接被拒绝。解决办法是删掉对应的行或者直接清空:
rm -rf ~/.ssh/known_hosts4. Hadoop核心配置文件逐项拆解
4.1 解压安装与目录规划
tar -zxvf hadoop-3.3.1.tar.gz -C /opt/module/ cd /opt/module mv hadoop-3.3.1 hadoop目录结构大概是这样的:
- bin:常用命令脚本
- etc/hadoop:配置文件目录,重点操作区域
- sbin:集群启动停止脚本
- share:jar包和示例
我习惯把Hadoop装在/opt/module下,数据目录放在/data下,日志放在/opt/module/hadoop/logs。目录规划清楚,后面排查问题的时候能省很多时间。
4.2 core-site.xml:集群的全局门户
这个文件定义NameNode的地址和临时目录,是所有配置的基础。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://master:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/module/hadoop/data/tmp</value> </property> </configuration>fs.defaultFS告诉客户端NameNode在哪里,8020是RPC通信端口。hadoop.tmp.dir是Hadoop的临时目录,很多组件的数据都放在这里。这个目录一定要手动创建,Hadoop不会自动建,我第一次搭的时候就是忘了建,NameNode格式化时报了一堆找不到目录的错误。
4.3 hdfs-site.xml:副本与数据目录
<configuration> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/opt/module/hadoop/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/module/hadoop/data/datanode</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>master:9868</value> </property> </configuration>副本数设为3是因为我们正好有三台机器,每个块存三份,任何一台挂了数据都不丢。如果只有两台DataNode,副本数设3会导致部分块永远处于复制中状态,集群会一直报“Under-replicated blocks”警告。
4.4 yarn-site.xml:资源调度核心
<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>master</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>2048</value> </property> <property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>2</value> </property> </configuration>yarn.nodemanager.resource.memory-mb这个参数要根据你机器的实际内存来调。我给的2048MB是保守值,如果你的slave节点有4GB内存,可以调到3072。设太大导致系统内存不足,NodeManager会被系统杀掉;设太小又跑不了大任务。
4.5 mapred-site.xml与workers文件
<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>mapreduce.application.classpath</name> <value>$HADOOP_HOME/share/hadoop/mapreduce/*:$HADOOP_HOME/share/hadoop/mapreduce/lib/*</value> </property> </configuration>workers文件在etc/hadoop目录下,内容就是所有DataNode的主机名:
master slave1 slave2等等,master也要做DataNode吗?在我的规划里master只做NameNode,所以workers文件应该只写:
slave1 slave2这个文件决定了start-dfs.sh会在哪些机器上启动DataNode进程。写错了要么少启动节点,要么在master上多起一个DataNode浪费资源。
4.6 环境变量与分发同步
在hadoop-env.sh里指定JAVA_HOME:
export JAVA_HOME=/opt/module/jdk1.8.0_291配置改完后,把整个Hadoop目录同步到slave1和slave2:
scp -r /opt/module/hadoop slave1:/opt/module/ scp -r /opt/module/hadoop slave2:/opt/module/同时把/etc/profile也分发过去,保证环境变量一致。
5. 集群格式化与启动全流程
5.1 NameNode格式化的正确姿势
第一次启动前必须格式化NameNode:
hdfs namenode -format看到“Storage directory ... has been successfully formatted”就说明成功了。但这里有个大坑:如果你多次格式化,会导致NameNode和DataNode的clusterID不一致,DataNode无法注册。解决办法是删掉所有节点的data目录重新格式化,或者只格式化一次就别再动。
提示:格式化之前确保所有节点的hadoop.tmp.dir目录是空的或者不存在,避免残留数据干扰。
5.2 启动HDFS与YARN
在master上执行:
start-dfs.sh start-yarn.sh启动脚本会通过SSH到各节点拉起对应进程。启动完成后用jps检查:
# master上应该看到 NameNode SecondaryNameNode ResourceManager # slave上应该看到 DataNode NodeManager如果某个进程没起来,去logs目录下看对应的日志文件,通常错误信息都很明确。
5.3 Web UI验证与常见异常
HDFS的Web界面:http://192.168.1.100:9870 YARN的Web界面:http://192.168.1.100:8088
打开HDFS界面,重点看“Datanodes”标签页,应该显示2个活跃节点。如果显示“1 dead”或者节点数为0,说明DataNode没注册上。
我遇到过一次DataNode死活起不来的情况,日志里报“java.net.BindException: Problem binding to ... 9866”。排查后发现是之前启动的残留进程占着端口,用jps看不到但端口确实被占了。解决办法:
netstat -tlnp | grep 9866 kill -9 <pid>然后重新启动即可。
6. 踩坑实录与高频问题速查
6.1 集群启动失败排查思路
| 现象 | 可能原因 | 解决方式 |
|---|---|---|
| NameNode未启动 | 端口被占 / 配置错误 | 检查8020端口,查看namenode日志 |
| DataNode未注册 | clusterID不一致 | 删除data目录重新格式化 |
| ResourceManager未启动 | 内存不足 | 调大yarn.nodemanager.resource.memory-mb |
| SSH连接被拒 | 免密未配好 | 检查authorized_keys权限 |
| Web UI打不开 | 防火墙未关 | 关闭firewalld或放行端口 |
6.2 那些教程不会告诉你的细节
时间同步问题:三台机器的时间差超过30秒,某些组件会拒绝通信。学习环境手动同步一下:
ntpdate ntp.aliyun.comhosts文件优先级:/etc/hosts的解析优先级高于DNS,但如果你在hosts里写错了IP,排查起来非常隐蔽。建议配置完后用ping验证一遍。
日志是最好的老师:Hadoop的日志目录在$HADOOP_HOME/logs下,每个组件都有独立的日志文件。启动失败先看日志,90%的问题日志里都有明确提示。
不要频繁格式化:每次格式化都会生成新的clusterID,DataNode的clusterID对不上就注册不了。如果非要重新格式化,先把所有节点的data目录清空。
6.3 集群停止与重启规范
停止顺序和启动顺序相反:
stop-yarn.sh stop-dfs.sh不要直接kill进程,否则可能丢失元数据。重启的时候先启动HDFS再启动YARN,因为YARN依赖HDFS的某些服务。
7. 集群跑通之后还能做什么
集群搭好只是第一步,接下来可以跑一个WordCount验证:
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.1.jar wordcount /input /output看到output目录下生成结果文件,说明整个链路是通的。然后你可以尝试调整副本数、增加节点、配置机架感知,甚至接入Hive和Spark。完全分布式集群是这些上层组件的基石,把这块吃透,后面学什么都会顺很多。
我个人在实际操作中的体会是:搭建过程中遇到的每一个报错都是学习的机会。第一次搭可能花两天,第二次半天,第三次一小时。这个过程中积累的排查经验,比任何教程都值钱。