接上篇,上回我们把CentOS7装好,网络改成固定IP,配了JDK8,Hadoop3也解压到了/opt/hadoop-3.3.6,还专门建了hadoop用户来跑服务。如果你是从零开始,建议先把环境准备那篇看完,因为后面所有命令都基于那个基础。如果已经准备好了,这篇就直接进入正题:改配置、格式化NameNode、启动HDFS和Yarn、跑通WordCount、遇到问题怎么排查。这篇文章我按实际操作的顺序来写,不是把官方文档翻译一遍,而是把每一步背后的原因和踩过的坑一起说出来。
这里我采用的方案是伪分布式,也就是一台CentOS7机器模拟整个Hadoop集群。NameNode、DataNode、ResourceManager、NodeManager都跑在同一台机器上,这个方案最适合新手理解运行机制,也足够应付后续学习MapReduce和HDFS API。生产环境的多节点集群,我会在最后聊一下和伪分布式的差异,但配置思路是相通的。
1. 动手前的环境确认与配置思路
1.1 先把上篇的成果检查一遍
很多人照着教程装完Hadoop,到了下篇发现跑不起来,绝大部分原因是前面某个环节没做干净。所以我建议不要急着打开xml配置文件,先花两分钟确认这些内容是否就绪。
bash复制# 确认hadoop用户存在
id hadoop
# 确认JDK版本
java -version
# 确认Hadoop解压目录
ls -ld /opt/hadoop-3.3.6
# 确认hadoop命令能被找到
su - hadoop
echo $HADOOP_HOME
hadoop version
我自己习惯用hadoop用户操作,不用root直接跑服务。原因很简单:Hadoop的DataNode和NodeManager在运行期会写大量数据,如果数据目录归属root,后面排查权限问题会特别痛苦。伪分布式虽然不会像集群那么严格,但从一开始养成好习惯,以后接触真实集群少踩坑。
如果你发现HADOOP_HOME是空的,说明上篇的环境变量没配好,先回去补上。需要注意,环境变量一般配在~/.bash_profile里,有些教程让配在/etc/profile,也都没问题,关键是当前登录用户能读到。用hadoop用户执行hadoop version前,记得重新source一下,或者直接重新登录一次。
1.2 伪分布的目录规划和内存预估
Hadoop启动后会涉及两类目录:一类是NameNode和DataNode的元数据及数据目录,另一类是运行日志目录。默认情况下,Hadoop会把数据写到/tmp下,这是个隐藏坑,因为系统重启后/tmp可能被清理,而且/tmp空间也可能不够。所以上篇我建议你规划一个独立的数据盘或者分区,如果机器是虚拟机演示用,至少把数据目录放到/opt/hadoop_data这样不容易被误清的地方。
内存方面,伪分布式不像集群那么吃紧,但也不是随便一台机器都能跑。NameNode、SecondaryNameNode、ResourceManager、DataNode、NodeManager这五个Java进程加在一起,初始堆内存大约2GB左右。所以虚拟机内存建议不小于4GB,如果是2GB内存,光把服务启动起来就会很勉强,跑WordCount时很容易整机卡死。我给测试机的配置是4核4GB,磁盘空间预留20GB以上,装完CentOS7系统后再装Hadoop,剩余空间基本够用。
Hadoop3的进程统一由hdfs和yarn脚本管理,很多新手会把Hadoop2时代的start-all.sh习惯带过来。实际上Hadoop3还在用start-dfs.sh和start-yarn.sh,但更推荐分别启动,方便定位是HDFS出问题还是Yarn出问题。判断一个服务启动慢还是启动失败,需要学会看日志,而不是反复重启试运气。
1.3 Hadoop3默认端口和防火墙的关系
CentOS7默认防火墙是firewalld,很多教程直接让新手systemctl stop firewalld,测试阶段没毛病,但我想多解释一句:如果只是本机跑,不用特意关防火墙,但一旦要通过其他机器访问管理界面,就必须放行对应端口。Hadoop3各组件默认端口如下:
| 组件 | 默认端口 | 用途 |
|---|---|---|
| NameNode Web UI | 9870 | HDFS管理界面,Hadoop3改成了9870,不是Hadoop2的50070 |
| DataNode通信端口 | 9866 | DataNode与NameNode的数据传输 |
| DataNode Web UI | 9864 | 查看单个DataNode状态 |
| ResourceManager Web UI | 8088 | Yarn任务管理界面 |
| NodeManager通信端口 | 8042 | NodeManager与ResourceManager通信 |
伪分布式测试阶段,如果不想关闭防火墙,可以执行这些命令把常用端口放出来:
bash复制sudo firewall-cmd --permanent --add-port=9870/tcp
sudo firewall-cmd --permanent --add-port=9866/tcp
sudo firewall-cmd --permanent --add-port=9864/tcp
sudo firewall-cmd --permanent --add-port=8088/tcp
sudo firewall-cmd --permanent --add-port=8042/tcp
sudo firewall-cmd --reload
如果你用的是云服务器,除了系统防火墙,安全组规则也要一起放开。我曾经遇到过本地页面全部打不开,排查到最后发现是云控制台的安全组没放行端口,这类问题外表看像Hadoop启动失败,实际和服务本身毫无关系。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 四个核心配置文件的修改逻辑与实操
2.1 core-site.xml:先告诉Hadoop谁是老大
Hadoop启动时首先会读取core-site.xml。这个文件里最关键的是fs.defaultFS,它决定了文件系统的访问地址,也就是NameNode的地址。我们改成hdfs://localhost:9000,表示使用本机9000端口作为HDFS入口。
先切到配置目录,然后用vim编辑:
bash复制su - hadoop
cd /opt/hadoop-3.3.6/etc/hadoop
vim core-site.xml
修改后的内容如下:
xml复制<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/opt/hadoop_data/tmp</value>
</property>
</configuration>
大部分人只配了fs.defaultFS,没配hadoop.tmp.dir,导致NameNode数据落在系统的/tmp目录。我这里随手指定到/opt/hadoop_data/tmp,虽然暂时不深入HDFS元数据的存储结构,但提前把数据目录规划好,以后调整磁盘位置不用重来。这个目录要先创建好,并保证hadoop用户有写权限。
bash复制sudo mkdir -p /opt/hadoop_data/tmp
sudo chown -R hadoop:hadoop /opt/hadoop_data
配置fs.defaultFS时的地址要和后面访问的NameNode地址完全一致。伪分布式里写成localhost没问题,但如果以后想从外面的电脑访问HDFS,这个地址就要改成机器的主机名或IP,不然客户端不知道去连接谁。
2.2 hdfs-site.xml:副本数与数据目录
HDFS默认会把每个数据块复制三份,这在生产集群里是标准做法,但伪分布式只有一台机器、一个DataNode,保留三份副本没有意义,反而浪费磁盘。因此hdfs-site.xml里必须把dfs.replication改成1,否则后续启动DataNode时,会一直看到副本数不足的告警,虽然不致命,但会影响测试任务的执行效率。
xml复制<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///opt/hadoop_data/namenode</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///opt/hadoop_data/datanode</value>
</property>
</configuration>
这里我额外设置了NameNode和DataNode的目录。NameNode的name.dir保存的是整个文件系统的元数据,包括目录树、文件和数据块映射,这个目录一旦损坏,整个HDFS就废了。DataNode的data.dir才是真正存数据块的目录。很多教程只让改hadoop.tmp.dir,不是不对,而是不够明确,因为Hadoop会基于tmp目录衍生出这两个子目录。显式写清楚的好处是,定位问题时一眼就知道数据在什么地方。
在伪分布式上,dfs.replication设为1能避免很多误告警。如果你以后搭建三节点集群,一定要改回2或3,否则某个节点宕机后会发现数据块复制任务一直处于等待状态。
2.3 mapred-site.xml和yarn-site.xml:计算框架与资源调度
MapReduce的配置在Hadoop3里仍然通过mapred-site.xml定义,但默认只有一个模板文件叫mapred-site.xml.template,你需要先复制一份再修改。这里最重要的一项是mapreduce.framework.name,需要设为yarn,意思是让MapReduce运行在Yarn之上,由Yarn负责资源分配和任务调度。如果不配置,MapReduce可能会跑在local模式下,任务看似成功,实际上并没有验证HDFS和Yarn的协同工作。
bash复制cp mapred-site.xml.template mapred-site.xml
vim mapred-site.xml
mapred-site.xml修改后内容:
xml复制<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>
接下来是yarn-site.xml,这个文件的作用是给ResourceManager指明身份,同时配置NodeManager的资源管理方式。在伪分布式环境,我们通常把yarn.resourcemanager.hostname设为本机hostname或localhost,再把数据传输方式和辅助服务打开。
xml复制<configuration>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>localhost</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
</configuration>
yarn.nodemanager.aux-services这个参数非常关键,如果漏掉或者写错,MapReduce作业提交后会在Shuffle阶段反复失败。它的作用就是让NodeManager为MapReduce的Shuffle过程提供一个辅助服务,mapreduce_shuffle是固定值,不能随便改名。
2.4 workers文件:确定哪些节点当DataNode和NodeManager
Hadoop3中节点列表文件叫workers,不再叫slaves。它定义的是:当你执行start-dfs.sh时,哪些机器会启动DataNode;执行start-yarn.sh时,哪些机器会启动NodeManager。伪分布式环境一行localhost就够了。
bash复制vim workers
把文件内容改成:
code复制localhost
这里容易踩一个坑:很多人的机器hostname不叫localhost,而是类似hadoop-node1。如果workers文件里写的是localhost,而NameNode使用机器hostname做解析时可能会找不到对应关系。稳妥做法是把workers里的内容和core-site.xml里的fs.defaultFS地址统一。如果你在core-site.xml写localhost,那么workers写localhost就没问题;如果你写node1,那workers里也要写node1,而且/etc/hosts里要有node1到本机IP的映射。
3. 免密登录、格式化和启动服务
3.1 配置SSH免密登录的细节
Hadoop的start-dfs.sh脚本会通过SSH远程登录到workers列表里的每一台机器,去执行启动脚本。即使在伪分布式模式,它也会尝试SSH连接localhost,所以必须要配好免密登录,否则启动时会卡住,一直提示输入密码。
用hadoop用户执行以下命令:
bash复制ssh-keygen -t rsa
一路回车生成默认密钥即可,然后执行:
bash复制ssh-copy-id hadoop@localhost
执行过程中会要求输入一次密码,这是正常现象。配好后验证一下:
bash复制ssh localhost
如果直接弹出上次登录的时间信息,说明免密生效了。这一步失败最常见的两个原因:一是你用的是root用户的密钥,但服务跑在hadoop用户下,两者不通用;二是sshd配置禁用了公钥认证。我建议全程用hadoop用户操作,就不会混淆身份。
3.2 格式化NameNode与启动HDFS
在首次启动HDFS之前,必须格式化NameNode。格式化就是初始化元数据目录,让NameNode生成一张干净的文件系统镜像。如果忘记格式化,启动NameNode时会直接报错,提示找不到VERSION文件。
格式化命令:
bash复制hdfs namenode -format
这里有个非常重要的提醒:格式化会清空NameNode里的所有元数据。如果是第一次安装,格式化没问题;但如果集群已经运行过,里面存了数据,千万不要手贱再执行一次,否则数据等于被删除。很多人都栽在这个操作上,我在生产环境见过有人误格式化之后恢复数据,代价非常大。
格式化成功后,日志里能看到类似Storage directory /opt/hadoop_data/namenode has been successfully formatted的提示。不要看到一堆日志刷屏就关掉,注意观察最后几行,有没有ERROR字样。
接下来启动HDFS:
bash复制start-dfs.sh
启动完成后,用jps命令确认当前Java进程:
bash复制jps
正常情况下应该看到NameNode、DataNode、SecondaryNameNode三个进程。如果缺某一个,马上到对应日志里看原因。
3.3 启动Yarn并检查进程
HDFS起来后,再启动Yarn:
bash复制start-yarn.sh
启动完再次执行jps,正常情况下会增加ResourceManager和NodeManager两个进程。这样一台机器上共有五个Java进程,分别对应HDFS和Yarn的核心组件。
我习惯用下面这种简短的检查方式,把所有Java进程和关键端口一起看出来:
bash复制jps
ss -lntp | grep -E '9000|9870|8088'
如果看到端口已经监听,说明服务启动得比较顺利。有些时候jps里进程都在,但页面仍然打不开,多半就是防火墙或端口绑定问题,所以我更习惯先用ss命令确认端口监听状态,再决定是不是要翻防火墙。
4. 跑通WordCount与Web界面验证
4.1 准备测试数据并提交第一个作业
Hadoop装好之后,光看五个Java进程还很抽象。真正说明安装成功的是跑通一个MapReduce作业,最简单实用的就是官方自带的WordCount示例。它会统计一组文本里每个单词出现多少次,整个过程会经历Map、Shuffle、Reduce三个阶段,对熟悉Hadoop工作原理非常有帮助。
先在本地创建一个测试文件目录,准备两个文本文件:
bash复制mkdir -p /opt/hadoop_data/wordcount_input
echo "hello hadoop hello world" > /opt/hadoop_data/wordcount_input/file1.txt
echo "hello hdfs hello mapreduce" > /opt/hadoop_data/wordcount_input/file2.txt
把这些文件上传到HDFS上:
bash复制hdfs dfs -mkdir -p /wordcount/input
hdfs dfs -put /opt/hadoop_data/wordcount_input/*.txt /wordcount/input/
hdfs dfs -ls /wordcount/input/
如果hdfs命令能正常执行,说明HDFS客户端和服务端通信没问题。接下来提交WordCount作业,jar包在Hadoop发行版的share/hadoop/mapreduce目录下:
bash复制hadoop jar /opt/hadoop-3.3.6/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /wordcount/input /wordcount/output
执行过程中屏幕上会刷出Map和Reduce进度。我第一次跑的时候见到进度条一直在动,还以为卡死了,其实它在正常工作。等出现类似“Map 100% Reduce 100%”的信息,再执行:
bash复制hdfs dfs -cat /wordcount/output/part-r-00000
看到每个单词出现次数,说明整套环境已经通了。如果只想快速验证而不关心计算逻辑,可以只检查输出目录是否存在以及部分结果文件是否生成。
4.2 通过Web界面观察HDFS和Yarn状态
HDFS的NameNode界面地址是http://localhost:9870,Yarn的ResourceManager界面是http://localhost:8088。如果是在虚拟机上跑CentOS7,可以把这两个端口做端口转发或直接用浏览器访问虚拟机的IP。
NameNode页面里最值得关注的地方是HDFS容量、DataNode列表、DataNode存活数量。伪分布式正常情况下Live Nodes应该是1,如果显示0,多半是DataNode没起来或者DataNode连不上NameNode。另外,页面顶部会显示集群的整体存储量,可以通过它确认HDFS真正占用了多少空间。
Yarn的ResourceManager页面可以看到所有NodeManager是否在线、当前有没有队列中的作业、正在运行的Application。跑WordCount时,进8088页面能看到这个作业的详细链路,包括Map任务个数、Reduce任务个数、开始时间、结束状态。以后你调试复杂作业,这个页面比命令行直观得多。
4.3 用命令行检查文件块分布
Web界面适合日常观察,但排查问题更多还是靠命令行。比如我想知道某个文件被拆成几个块、每个块放在哪个DataNode上,可以执行:
bash复制hdfs fsck /wordcount/input/file1.txt -files -blocks -locations
如果显示副本数为1且块状态健康,说明存储正常。如果提示“Missing replica”,就去确认DataNode进程和磁盘空间。这个命令用在生产环境能快速定位坏块问题,建议学习阶段就养成习惯。
5. 高频故障排查:从起不来服务到跑不动任务
5.1 NameNode启动失败或反复退出
如果start-dfs.sh之后,jps里看不到NameNode,绝大多数是下面几个原因。
第一,格式化失败或格式化的目录与配置文件不一致。配置文件里指定的namenode目录是/opt/hadoop_data/namenode,但你没有提前创建好,或者创建后权限不是hadoop用户。Hadoop格式化时如果无法写入目录,会直接失败。解决方法是先确认目录存在且归属正确,然后重新格式化。
第二,端口被占用。NameNode默认用9870端口做Web UI,用9000端口做RPC通信,如果这两个端口已经被其他进程占用,进程会直接退出。排查命令:
bash复制ss -lntp | grep -E '9870|9000'
这里有个小技巧,启动日志是排查问题的第一手资料。NameNode日志位于当前用户的logs目录,也就是/opt/hadoop-3.3.6/logs/hadoop-hadoop-namenode-xxx.log。启动失败后不要反复重启,先打开这个日志看最后的异常栈,往往直接能定位问题。
第三,元数据目录损坏。如果上一次运行没正常退出,比如直接关掉了虚拟机,NameNode的edits日志和fsimage可能不一致,启动时会进入安全模式或直接报错。这时可以尝试用hdfs namenode -recover恢复,但操作有风险,新手还是优先考虑恢复最原始的格式化方案。
5.2 DataNode起不来或启动后很快消失
DataNode启动失败通常和集群ID不一致有关。每个HDFS集群在格式化NameNode时会生成一个clusterID,DataNode第一次连接NameNode后会把对应ID记录在本地。如果格式化过NameNode,而DataNode还保留着旧ID,就会报“Incompatible clusterIDs”错误。
遇到这种情况,最简单的办法是清空DataNode数据目录,让它重新注册:
bash复制sudo rm -rf /opt/hadoop_data/datanode/*
然后重新启动DataNode:
bash复制hdfs --daemon start datanode
这里注意,只清DataNode目录,不要动NameNode目录,否则又要重新格式化,数据照样丢失。DataNode反复消失还有一个常见原因:磁盘可用空间不足,或者dfs.datanode.data.dir指定到了一个不存在的路径。DataNode启动失败一般会在日志里明确写清楚,排查效率最高的手段就是查看log目录下的datanode日志。
5.3 内存不足导致进程被系统杀掉
伪分布式各进程默认堆内存配置可能不适合小内存机器。Hadoop安装包里的hadoop-env.sh中有HADOOP_HEAPSIZE之类的参数,如果没改过,NameNode可能直接分配1GB堆内存,ResourceManager又是1GB,加上其他进程,2GB内存的虚拟机很容易被撑爆。
解决办法是调整Hadoop内存参数。打开hadoop-env.sh:
bash复制vim /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh
找到类似下面的行修改为合适大小:
bash复制export HADOOP_HEAPSIZE=512
如果希望单独控制NameNode、ResourceManager等进程内存,可以用HDFS_NAMENODE_OPTS和YARN_RESOURCEMANAGER_OPTS,这里不展开,思路是给每个组件分配合理的堆内存,避免相互争抢。系统本身没有swap的话,也可以在CentOS上增加交换分区,但这个只是权宜之计,真正做任务调优还是要把内存管好。
5.4 MapReduce作业一直卡住或失败
作业提交后一直显示ACCEPTED,App Master却起不来,这种情况在内存不足时很常见。Yarn的NodeManager会为每个容器分配内存,如果宿主机实际内存不够,Container会被反复Kill。此时去Yarn日志或NodeManager日志里找关键词Kill或OutOfMemory。
另一个常见原因是Shuffle阶段失败,十有八九是yarn-site.xml里没配置yarn.nodemanager.aux-services为mapreduce_shuffle。这个问题在上文提过,但值得再强调一遍,因为日志里的报错非常有迷惑性,有时候只提示“Error: org.apache.hadoop.mapreduce.task.reduce.Shuffle$ShuffleError”。
如果作业本身输出目录已存在,也会导致失败。MapReduce的输出目录设计要求是“不存在”,因为框架担心覆盖掉之前的结果。再跑一次前,先删除旧输出目录:
bash复制hdfs dfs -rm -r /wordcount/output
5.5 快速排查清单
| 现象 | 可能原因 | 处理动作 |
|---|---|---|
| jps缺少NameNode | 未格式化/端口占用 | 查看namenode日志,检查端口 |
| jps缺少DataNode | clusterID不一致/目录权限 | 清空datanode目录并重启 |
| 9870页面打不开 | 防火墙或安全组未放行 | 放行端口或临时关闭firewalld |
| 8088打不开 | ResourceManager进程未启动 | 查看resourcemanager日志 |
| WordCount一直等待 | 内存不足或aux-services缺失 | 检查nodemanager日志与配置 |
| 输出目录已存在 | 未删除旧目录 | 执行hdfs dfs -rm -r |
6. 从伪分布式到多节点集群的思路
6.1 节点规划和配置差异
伪分布式跑通后,你可能会遇到一个疑虑:这和生产环境差距大吗?答案是大,但配置文件的底层逻辑完全一致。真实集群至少需要三台机器,典型角色划分是:一台跑NameNode和ResourceManager,另外若干台跑DataNode和NodeManager。如果数据量不大,可以把SecondaryNameNode放在另一台辅助节点上,避免NameNode负载过高。
CentOS7环境与这里不同的地方主要是主机名和IP映射。每台机器都要在/etc/hosts里写全所有节点的IP和主机名,例如:
code复制192.168.1.101 hadoop-master
192.168.1.102 hadoop-node1
192.168.1.103 hadoop-node2
core-site.xml中的fs.defaultFS改成hdfs://hadoop-master:9000,hdfs-site.xml中把dfs.replication改为2或3,workers文件里不再是localhost,而是hadoop-node1和hadoop-node2。除此之外,整个流程和单机伪分布式一模一样。
6.2 配置分发与启动顺序
多节点安装时,不要在每台机器上都重新编辑一遍xml文件,那样很容易出现某台机器少写一个参数。我习惯先在主节点把全部配置改好,然后用scp分发到其他节点:
bash复制scp -r /opt/hadoop-3.3.6/etc/hadoop hadoop-node1:/opt/hadoop-3.3.6/etc/
scp -r /opt/hadoop-3.3.6/etc/hadoop hadoop-node2:/opt/hadoop-3.3.6/etc/
分发之前确认所有节点JDK路径一致,Hadoop解压目录一致,环境变量也一致。之后只在主节点上执行start-dfs.sh和start-yarn.sh,脚本会通过SSH免密登录到workers文件里记录的节点去启动DataNode和NodeManager。格式化只需要在主节点执行一次,千万不要在每台节点上都格式化NameNode,否则会生成不同的clusterID,导致整个集群无法相互识别。
从我个人的实际经验来看,先在单机伪分布式上把所有组件跑通,再扩展到集群,这个学习路径是最高效的。伪分布式阶段暴露的问题基本都属于配置或环境类问题,解决起来成本低;如果你一上来就搭三节点集群,遇到问题后很难判断是哪台机器配置写错了,排查范围一下子扩大好几倍。
最后再分享一个小习惯:每次修改完xml配置文件,一定要用xmllint或者打开文件检查标签是否闭合,因为Hadoop的配置解析器对格式要求严格,一个漏掉的会让整个配置读不出来。执行hdfs namenode -format前,也再看一眼namenode目录里有没有旧数据,避免为了图省事把已经跑通的集群清空重来。这套安装流程我反复做过多次,把常见的坑都放在前面了,你只要每一步都对照日志确认再继续,基本能一路顺利跑完。
