1. 环境准备与版本选型,先把屋子收拾好再动工
很多新手一上来就急着下载 Hadoop 压缩包,结果装到一半发现 JDK 版本不兼容、SSH 没配、虚拟机内存不够,各种报错互相叠加,耗时耗力。我装过太多次 Hadoop,踩过足够多的坑,可以负责任地告诉你:把环境基础打好,后面配置环节最多 40 分钟完事;环境一团糟,折腾两三天下不来也正常。
这篇文章以 CentOS 7 为演示系统,安装的是 Hadoop 3.1.3,覆盖两种运行模式:单机模式(local/standalone)和伪分布式模式(pseudo-distributed)。前者主要用于快速验证安装是否成功、跑跑示例程序;后者是真正意义上模拟集群的完整流程,在一台机器上分别启动 NameNode、DataNode、ResourceManager、NodeManager 等进程,为后续学习 MapReduce、HDFS 读写或者过渡到真实多节点集群做准备。
1.1 虚拟机与操作系统的基本要求
建议使用虚拟机软件创建一个 CentOS 7 的虚拟机,不需要图形界面,最小化安装即可。内存给到 2GB 以上,我实际测试下来,内存低于 1.5GB 时,同时跑起 HDFS 和 YARN 会比较吃力,系统容易频繁 swap,导致进程响应极慢甚至直接超时挂掉。CPU 双核起步,硬盘 20GB 足够,因为 Hadoop 安装包解压后大概两三个 GB,再加上运行日志和示例数据,空间完全够用。
一个小建议:虚拟机创建完成后,先做一次系统快照。后面 Hadoop 配置或格式化过程中出现不可逆的破坏(比如配置文件改乱、NameNode 元数据损坏),直接回滚快照,比手动一点点排查省太多时间。这是我个人非常推荐的习惯。
1.2 JDK 版本选择:Hadoop 3.x 必须要 JDK 8
这里需要特别强调一个容易踩的坑:Hadoop 3.1.3 官方要求 Java 8 或 Java 11 都可以,但实际部署中绝大多数公司还是用 JDK 8,原因很简单——生态兼容性最好。如果你装的是 JDK 9、10 这些中间版本,编译或运行阶段会出现意料之外的报错;JDK 17 及以上版本,某些组件直接不支持。
我用的是 OpenJDK 1.8,在 CentOS 上一条命令就能装完:
yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel装完以后验证一下:
java -version这个步骤不复杂,但很多教程只会写“请安装 JDK 8”,从不告诉你为什么要用 8。实际上 Hadoop 的底层很多模块依赖 JDK 自带的一些工具和内部 API,比如 com.sun.management 相关的监控接口,在新版 JDK 中被移除或限制了访问权限,导致启动服务时出现模块访问错误。JDK 8 是经过 Hadoop 生态长期验证的版本,不折腾。
再看一下环境变量配置,编辑 /etc/profile 文件,追加以下内容:
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export PATH=$PATH:$JAVA_HOME/bin然后执行 source /etc/profile 使其生效。JAVA_HOME 的路径可以在命令行里这样确认:
readlink -f $(which java)输出类似 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.xxx/bin/java,去掉末尾的 /bin/java 就是你的 JAVA_HOME 路径。
1.3 SSH 免密登录:伪分布式集群的“通行证”
即使是在单机上做伪分布式,Hadoop 也会通过 SSH 协议在本机启动和停止各个守护进程。如果没有配置免密登录,每次启动集群时都会要求输入密码,而且在自动化脚本场景下会直接失败。
CentOS 最小化安装通常自带 openssh-server,如果没有就安装一下:
yum install -y openssh-server openssh-clients先生成密钥对:
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa这里 -P '' 表示空口令,省去每次输入密钥密码的麻烦。然后把公钥写入 authorized_keys:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys验证方法:
ssh localhost如果直接进入 shell 而不提示输入密码,说明配置成功。这里有个细节:Hadoop 配置文件中指定的主机名是什么,就需要对哪个主机名配置免密。如果你在 core-site.xml 里配置 fs.defaultFS 为 hdfs://node01:9000,那么一定要保证 ssh node01 也能免密登录,否则启动时同样会卡住。所以稳妥做法是同时对 localhost 和本机 hostname 都做一遍免密。
1.4 下载 Hadoop 3.1.3 并规划目录结构
可以从 Apache 官方镜像站或清华/华为等国内镜像下载 hadoop-3.1.3.tar.gz。这里我多说一句版本选择问题:为什么选 3.1.3 而不是最新的 3.3.x 或 3.4.x?因为 3.1.3 是 3.x 系列里非常稳定、资料最丰富的一个版本,网上大量教程、源码分析、企业案例都基于这个版本或同系列的 3.1.x。用新版本没问题,但遇到问题时你能搜到的解决方案会少很多。初学阶段,稳定可查远比功能多更重要。
下载完成后解压到 /usr/local 目录,并创建软链接,方便后续版本升级和路径维护:
tar -zxvf hadoop-3.1.3.tar.gz -C /usr/local/ ln -s /usr/local/hadoop-3.1.3 /usr/local/hadoop接着配置 HADOOP_HOME,同样写在 /etc/profile 里:
export HADOOP_HOME=/usr/local/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop最后验证环境变量:
hadoop version能正常输出版本信息,说明 JDK 和 Hadoop 基础环境已经打通。此时先别急着改配置文件,先把两种运行模式的区别搞清楚。
2. 先搞懂单机模式和伪分布式模式的区别,再动手配
很多教程把单机模式和伪分布式模式混在一起讲,导致读者配完了自己都不知道当前在跑哪种模式。这两个概念搞不清楚,后面排查问题会非常痛苦。
2.1 单机模式(Standalone Mode):Hadoop 的“出厂设置”
在默认情况下,Hadoop 的四个核心配置文件都是空配置,它就以单机模式运行。这种模式下:
- 没有启动任何守护进程,没有 NameNode、DataNode、ResourceManager
- 整个 Hadoop 以单个 Java 进程形式运行,主要用于本地调试 MapReduce 程序
- 使用本地文件系统(file:///)作为存储介质,而不是 HDFS
- 所有组件共享同一个 JVM,不存在网络通信
你可以把单机模式理解成一个“纯 CPU 计算模式”,它只是借用了 Hadoop 的 MapReduce 框架来跑程序,输入和输出都在本地磁盘上,不涉及分布式存储和资源调度。这种模式的优点是零配置、启动快,适合验证 Hadoop 安装是否正确、跑跑自带示例。缺点是完全没有体现出“分布式”的含义,文件块、副本机制、数据节点这些概念完全没有涉及。
验证单机模式最简单的例子是运行官方自带的 WordCount 程序。在 $HADOOP_HOME 目录下随便创建一个输入文件夹,放几个文本文件,然后执行:
mkdir input echo "hello hadoop hello world" > input/hello.txt hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount input output跑完后查看 output 目录下的结果:
cat output/part-r-00000你会得到每个单词出现的次数。这里有个细节:output 目录不能事先存在,否则程序会报错提示输出路径已存在。这是 Hadoop 的机制设计——它不允许输出目录被覆盖,目的是防止误删之前的计算结果。如果第二次运行同一个任务,需要先手动删除 output。
2.2 伪分布式模式(Pseudo-Distributed Mode):一台机器模拟完整集群
伪分布式模式才是这篇文章的重头戏。所谓“伪分布式”,就是在一台物理节点上,用多个 Java 进程分别模拟集群中的不同角色,形成“一个节点上的集群”。
在这种模式下:
- NameNode 和 DataNode 分别以独立进程启动,它们之间通过 HDFS 协议通信
- ResourceManager 和 NodeManager 分别以独立进程启动,它们之间通过 YARN 协议通信
- 默认使用 HDFS(hdfs://)协议进行存储,文件会被真正地切块、分散存储
- 它会创建真实的元数据目录和数据存储目录,使用 HDFS 客户端执行文件读写操作
为什么叫“伪”?因为这个“集群”的每个角色虽然互相独立通信,但所有进程都在同一台机器上,没有实现跨节点的数据分布。它和真正集群的差别在于:真实集群中 NameNode 和 DataNode 分布在多台机器上,数据块也物理分散;伪分布式把这些全部塞进了一台机器,用于学习原理、开发调试、体验完整流程。
我之前带过不少人装大数据环境,发现一个常见心态:既然要做大数据,直接装个三节点集群不就行了,何必搞伪分布式?这种想法可以理解但不推荐。伪分布式阶段能让你在最小成本下理解每个组件的功能、每个配置文件的作用,出现问题也只需排查单机环境,定位逻辑更清晰。等这套流程烂熟于心,再扩展到多节点集群,无非是改几个 IP、复制配置文件、互相配好 SSH,水到渠成。
2.3 两种模式的切换机制
其实单机模式和伪分布式模式的切换非常容易,就是改配置文件的问题。Hadoop 的 bin 目录中有多个脚本,但何时加载哪些配置,完全由 etc/hadoop 下的 XML 文件控制。当 core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml 里都为空或不存在时,Hadoop 以单机模式运行;一旦这四个文件配置了具体参数,重启相关进程后,Hadoop 就会以伪分布式模式运行。
从这一点能看出来,Hadoop 的设计哲学之一就是“配置驱动运行”。改动配置文件之后,不是像普通软件那样重启就能生效,HDFS 相关配置往往需要重新格式化 NameNode,这是一个很多新手忽略的重点。格式化相当于初始化整个 HDFS 的元数据状态,之后文件系统的 NameSpace、块信息、集群 ID 都会重新生成,之前 HDFS 中的数据会全部丢失。所以一旦集群已经有重要数据,切不可随意格式化。
3. 四份核心配置文件的逐项拆解与实操配置
现在进入重头戏:配置伪分布式模式的四个核心 XML 文件。它们都位于 $HADOOP_HOME/etc/hadoop 目录下。我见过不少人直接复制别人的配置,也不管每项参数什么意思,出了问题完全不知道从哪里排查。这一节我会把每个配置项逐项解释清楚,并说明为什么要这样配、改动以后影响什么。
3.1 core-site.xml:集群的“全局地址簿”
core-site.xml 是整个 Hadoop 集群的核心配置,它决定了 HDFS NameNode 的地址、临时目录位置等全局参数。伪分布式最少需要配置两项。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/usr/local/hadoop/tmp</value> </property> </configuration>fs.defaultFS 指定了 HDFS 的文件系统访问入口。当你在命令行输入诸如 hadoop fs -ls / 之类的命令时,Hadoop 会去连接这个地址。这里 hostname 需要和 SSH 免密的配置保持一致,如果写 hdfs://node01:9000,就必须保证本机 hostname 设置成了 node01,并且 ssh node01 免密可用。否则启动时会出现连接超时或拒绝连接。
hadoop.tmp.dir 是 Hadoop 的临时文件根目录,它决定了 NameNode 和 DataNode 元数据存放的根路径,默认值是 /tmp/hadoop-${user.name}。这个默认值非常坑,因为系统重启后会清空 /tmp 目录,导致元数据丢失。所以生产环境中几乎所有团队都会把 hadoop.tmp.dir 改到持久化磁盘路径下,这也是我特别强调这项配置的原因。
3.2 hdfs-site.xml:决定数据怎么存、副本几份
hdfs-site.xml 负责 HDFS 层面的配置,伪分布式模式需要配置副本数和 NameNode 的 Web 服务端口。
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.http-address</name> <value>localhost:9870</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///usr/local/hadoop/tmp/dfs/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file:///usr/local/hadoop/tmp/dfs/data</value> </property> </configuration>dfs.replication 设置数据块的副本数。在真实集群中默认是 3,即每个数据块会在三个不同节点上各存一份,用来容灾。但在伪分布式模式下,只有一个 DataNode,所以副本数必须设成 1。如果设成 3,DataNode 会尝试复制数据块到另外两个节点,但集群里根本没有那么多节点,结果会不断报复制不足的警告,虽然任务可能能完成,但日志里一大堆报错信息,影响排查。
dfs.namenode.http-address 是 NameNode 的 Web UI 地址。Hadoop 3.x 版本把这个端口从 50070 改成了 9870,这也是很多老教程在 3.x 上失效的原因之一。通过浏览器访问这个地址,可以查看 HDFS 的文件系统状态、数据节点列表、集群容量等。
dfs.namenode.name.dir 和 dfs.datanode.data.dir 分别指定 NameNode 的元数据目录和 DataNode 的数据块目录。虽然不配置这两个选项,系统也会根据 hadoop.tmp.dir 自动推导,但显式配置的好处是:目录路径一目了然,不会因为默认路径变化导致后续排查困难。
3.3 mapred-site.xml:告诉 MapReduce 用哪种调度框架
mapred-site.xml 在 Hadoop 2.x 之后主要用来指定 MapReduce 程序运行的框架。Hadoop 3.x 中默认支持 YARN,所以配置很简单:
<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>mapreduce.framework.name 的值设置为 yarn,含义是让 MapReduce 作业提交到 YARN 框架上运行。如果不设置这个参数,默认值是 local,作业会在提交它的节点上本地运行,不会利用 YARN 的资源管理和调度能力。伪分布式下我们要跑完整的“提交作业 -> ResourceManager 分配资源 -> NodeManager 执行任务”链路,所以必须设置成 yarn。
mapreduce.application.classpath 是 Hadoop 3.x 中比较特殊的一个设置。从某个版本开始,YARN 的 Application classpath 不再自动包含 Hadoop 安装目录下的所有 jar 包,导致作业提交后,NodeManager 在启动 Container 时找不到 MapReduce 框架相关的类,直接抛出 ClassNotFoundException。这个配置就是为了把 MapReduce 相关的 jar 包显式加入 classpath,清扫掉这类问题。
3.4 yarn-site.xml:资源管理器的核心参数
yarn-site.xml 负责 YARN 的配置,至少需要设置 ResourceManager 的地址和辅助服务。
<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>localhost</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.env-whitelist</name> <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value> </property> </configuration>yarn.resourcemanager.hostname 指定 ResourceManager 所在的主机,伪分布式下写 localhost 即可。ResourceManager 负责全局的资源分配和作业调度,相当于整个集群的“总调度室”。
yarn.nodemanager.aux-services 非常关键。MapReduce 的 Shuffle 阶段需要 NodeManager 提供一个辅助服务来协助传输 map 输出的中间数据,这个服务的名字就叫 mapreduce_shuffle。如果不配置这一项,作业运行时会出现 NoSuchAuxService: mapreduce_shuffle 的错误,导致任务无法完成。你可以把它理解为:Map 阶段产出的数据需要有人帮忙搬运给 Reduce 阶段,而这个搬运工就是 aux-service。
yarn.nodemanager.env-whitelist 是 Hadoop 3.x 中新增的配置项,用于控制哪些环境变量可以被传递到 Container 中。在较老版本中,NodeManager 默认继承全部环境变量,但新版做出了限制。如果不把 HADOOP_HOME 相关的环境变量加进白名单,Container 内找不到 Hadoop 命令和依赖库,作业依然会失败。
3.5 workers/slaves 文件:告诉集群谁是从节点
在 Hadoop 3.x 中,管理 DataNode 和 NodeManager 的节点列表文件名是 workers(在 Hadoop 2.x 中叫 slaves)。文件位于 $HADOOP_HOME/etc/hadoop/workers,内容很简单:
localhost这个文件的作用是:当执行 start-dfs.sh 或 start-yarn.sh 时,脚本会逐行读取该文件,通过 SSH 登录到每个节点并启动相应的守护进程。伪分布式下只有本机一个节点,所以写入 localhost 即可。
如果未来扩展成多节点集群,需要把这个文件里的 localhost 替换成各台数据节点的主机名,每行一个节点。这也是从伪分布式过渡到真集群比较自然的衔接点。
4. 完整实操流程:从格式化到启动,再到跑通 WordCount
配置做完,接下来是一整套从初始化到验证的流程。这部分我按实际操作顺序来写,每一段命令都有执行后应有的效果,方便你对照检查。
4.1 配置环境变量与系统参数
先确保之前 JDK 和 Hadoop 的环境变量已经配置并生效。使用 echo 验证:
echo $JAVA_HOME echo $HADOOP_HOME确保返回的不是空行。另外建议把 HDFS 所需的用户目录先准备好,虽然大多数情况下 Hadoop 进程会自动创建,但提前手动创建可以避免权限问题。
这里补充一个与虚拟机内存相关的现实问题:YARN 分配内存不足导致 Container 被杀掉。默认情况下,NodeManager 会根据物理内存做资源估算,但如果虚拟机内存只有 2GB,而默认参数下每个 Container 动辄需要 1~2GB,会出现 Container 被 NodeManager 直接 kill 的报错。如果遇到这种情况,可以在 yarn-site.xml 中调整这些参数:
<property> <name>yarn.nodemanager.resource.memory-mb</name> <value>1024</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>768</value> </property> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>128</value> </property>把总资源调低,单容器最大内存也调低,才能让任务在有限内存下跑完。
4.2 格式化 NameNode:HDFS 的初始化动作
这一步可能是整个流程中最容易让新手困惑的操作。格式化 NameNode 的命令是:
hdfs namenode -format执行后你会看到大量日志输出,最后的关键信息包含 successfully formatted 字样。这行日志意味着文件系统初始化成功,NameNode 的元数据目录已经创建完毕。
我解释一下为什么必须格式化:NameNode 是整个 HDFS 的“大脑”,它需要维护完整的文件系统目录树、文件与数据块映射关系。在首次启动之前,这个“大脑”还是一片空白,格式化操作就是为它创建底层的文件系统结构。之后每次 NameNode 启动时,都会把这个结构加载到内存中。
这个命令有以下几个重要注意事项:
- 格式化命令只需要在所有配置改完之后执行一次。以后正常启动、关闭集群,不需要也不应该反复格式化。
- 如果在已经运行过 HDFS 的机器上再次格式化,会生成新的集群 ID,与 DataNode 上记录的集群 ID 不一致,导致 DataNode 无法正常注册到 NameNode。
- 如果二次格式化前没有清空 dfs.datanode.data.dir,DataNode 会因集群 ID 不匹配而启动失败。
我踩过最典型的一次坑就是:启动集群后 Web UI 里 DataNode 列表为空,查日志才发现 DataNode 报 clusterId 不匹配。原因就是我改了配置文件后没有清空原来的数据目录就重新格式化了。解决方法是删除 tmp 目录下的 dfs/data 和 dfs/name,再做一次格式化。
4.3 启动 HDFS 和 YARN 守护进程
格式化完成后,依次启动 HDFS 和 YARN。先启动 HDFS:
start-dfs.sh执行这段脚本后,正常情况下会启动三个进程:NameNode、DataNode、SecondaryNameNode。用 jps 命令验证(jps 是 JDK 自带的 Java 进程查看工具):
jps输出应该包括以下三行:
- NameNode
- DataNode
- SecondaryNameNode
任何一个进程缺失,都说明 HDFS 启动不完整。之后启动 YARN:
start-yarn.sh再次用 jps 验证,此时应该额外出现:
- ResourceManager
- NodeManager
五个进程齐全,说明伪分布式集群已经完整运行。如果要一次性启动所有服务,也可以直接执行 start-all.sh,其效果等同于先执行 start-dfs.sh 再执行 start-yarn.sh。不过我习惯分步执行,这样出问题时能更快速定位是 HDFS 组件还是 YARN 组件的问题。
4.4 Web UI 验证:直观确认集群状态
集群启动后,可以通过浏览器访问两个 Web 界面来验证:
- HDFS 管理界面:http://localhost:9870
- YARN 资源调度界面:http://localhost:8088
HDFS 界面上,左侧文件系统菜单可以看到 HDFS 中的目录结构,主页 Overview 会显示集群容量信息,DataNodes 菜单里应该能看到一个活跃节点,状态显示为 Active,容量是你分配的磁盘空间。YARN 界面上,可以看到集群的 Active Nodes 数量为 1,并显示可用内存和虚拟核数。
这两个 Web UI 页面在日常运维、监控、排查问题时特别实用。比如查看任务日志、检查某个文件在各个节点的块分布情况、观察集群资源使用率,都比敲命令直观得多。
4.5 创建 HDFS 目录并执行真正的 WordCount
伪分布式模式下,文件需要先放入 HDFS 中,MapReduce 作业再从 HDFS 读取数据。首先在 HDFS 上创建目录:
hdfs dfs -mkdir -p /user/hadoop/input解释一下这条命令:在 HDFS 根目录下创建 user/hadoop/input 多级目录。-p 参数和 Linux mkdir 的 -p 一样,父目录不存在时自动创建。然后创建本地测试文件,上传到 HDFS:
echo "hello hadoop hello world hdfs" > test.txt hdfs dfs -put test.txt /user/hadoop/input/查看上传结果:
hdfs dfs -ls /user/hadoop/input应该能看到一个 test.txt 文件。此时可以用一条命令直接查看 HDFS 上的文件内容:
hdfs dfs -cat /user/hadoop/input/test.txt接下来执行 WordCount 任务:
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount /user/hadoop/input /user/hadoop/output执行过程中控制台会不断输出 MapReduce 任务进度,包括 map 阶段进度、reduce 阶段进度,以及最终完成信息。完成后查看结果:
hdfs dfs -cat /user/hadoop/output/part-r-00000输出结果类似:
hello 2 hadoop 2 world 1 hdfs 1这表示 WordCount 任务成功跑通了完整的伪分布式流程:文件存储在 HDFS 上,MapReduce 任务通过 YARN 调度执行,结果写回 HDFS。走到这一步,你的伪分布式集群已经完全可用。
4.6 关闭集群的正确姿势
准备关闭集群时,有两个命令需要记住:
stop-yarn.sh stop-dfs.sh执行完以后再用 jps 检查,应该只剩下 jps 本身。这里有个很多人忽略的细节:NameNode 在关闭时会进入安全模式并主动保存一份新的元数据镜像到磁盘。如果直接 kill 进程,很可能导致下一次启动时元数据恢复耗时变长,极端情况下可能损坏镜像文件。所以始终建议使用官方脚本优雅关闭。
5. 常见问题与排查技巧实录
这部分是我从反复安装、配置、带人入门过程中总结的最常见问题,按出现频率从高到低排列。每个问题都给出典型错误表现、原因分析和排查思路。
5.1 NameNode 启动失败,日志报 Java.net.BindException:Address already in use
这个问题在我带过的学员中出现频率极高,常见于多次启动未正确关闭或端口被其他程序占用。NameNode 默认使用 9000 端口做 RPC 通信、9870 端口做 Web UI。当端口被占用时,NameNode 进程会直接退出。
排查方法:
netstat -tlnp | grep 9000 lsof -i:9870如果看到端口被占用,确认占用进程是否为另一个残留的 NameNode。如果是残留进程,执行 kill -9 清理后重启。如果不是 Java 进程,则需要检查是否与其他软件端口冲突,可以修改 core-site.xml 中 fs.defaultFS 的端口号换个端口。
5.2 DataNode 启动失败,报 org.apache.hadoop.hdfs.server.datanode.DataNode:Datanode denied communication with namenode
这个问题的典型诱因就是我前面提到的重复格式化导致集群 ID 不一致。DataNode 启动时会带上本地已有的 clusterId 去访问 NameNode,如果发现不一致,会拒绝注册。
解决方案简单粗暴:
rm -rf /usr/local/hadoop/tmp/dfs/data rm -rf /usr/local/hadoop/tmp/dfs/name hdfs namenode -format然后重新启动 start-dfs.sh。同时强调一遍:以后不要随意格式化。
5.3 执行 WordCount 时作业卡在 ACCEPTED 状态,NodeManager 日志报 OutOfMemory
这种问题本质是 YARN 的资源分配与虚拟机物理内存不匹配。伪分布式下 NodeManager 会根据机器的物理内存估算总可用资源,但 YARN 计算得到的可用内存往往大于虚拟机实际能够分配给容器进程的内存,导致容器启动后内存溢出被 kill。
解决方法是在 yarn-site.xml 中显式限制资源:
<property> <name>yarn.nodemanager.resource.memory-mb</name> <value>1024</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>512</value> </property>同时,为了减小每个任务的内存开销,可以把 mapred-site.xml 中加入:
<property> <name>mapreduce.map.memory.mb</name> <value>256</value> </property> <property> <name>mapreduce.reduce.memory.mb</name> <value>256</value> </property>调整后重启 YARN 和相关服务。这个经验非常实用,内存小的机器上跑大数据组件,第一课就是学会给组件“限食”。
5.4 浏览器打不开 9870 或 8088 端口
如果在虚拟机的浏览器里能打开,但宿主机浏览器无法访问,优先排查两个问题:虚拟机防火墙是否放行端口,以及网络模式是否为 NAT + 端口转发。CentOS 默认防火墙可能拦截外部访问,可以临时关闭:
systemctl stop firewalld systemctl disable firewalld或者只放行端口也可以,但作为学习环境,直接关掉省事。宿主机的 hosts 文件里也最好加上虚拟机的 IP 和主机名的映射。
5.5 常见问题速查表
| 现象 | 常见原因 | 排查命令/方案 |
|---|---|---|
| jps 缺少 NameNode | 端口被占用、配置出错 | netstat -tlnp 查看 9000/9870 端口,查看日志 |
| jps 缺少 DataNode | 集群 ID 不匹配 | 清空数据目录重新格式化 |
| 作业运行失败,报 shuffle 异常 | yarn-site.xml 缺少 aux-service | 检查 mapreduce_shuffle 配置 |
| 浏览器无法访问 9870 | 防火墙未关闭、网络模式问题 | systemctl stop firewalld |
| Hadoop 命令找不到 | 环境变量未生效 | 执行 source /etc/profile,检查 PATH |
| SSH 连接卡住 | 免密登录未配置 | ssh localhost 验证,重新配置密钥 |
5.6 日志文件:排查问题的第一现场
Hadoop 的日志是排查问题的金矿。HDFS 和 YARN 的日志都位于 $HADOOP_HOME/logs 目录下。命名规则很容易辨识:每类进程都有独立的日志文件,比如 hadoop-hadoop-namenode-localhost.log、hadoop-hadoop-datanode-localhost.log。YARN 的日志在 logs/userlogs 目录下,每次提交的 MapReduce 作业都会生成一个新的子目录,里面记录了每个任务的标准输出、标准错误和系统日志。
遇到进程启动失败,第一件事不是乱猜原因,而是用命令查看日志尾部:
tail -50 $HADOOP_HOME/logs/hadoop-hadoop-namenode-localhost.log日志里通常直接包含导致启动失败的根本原因(Root Cause),比如端口被占用、权限不匹配、配置缺失等。养成看日志的习惯,解决 Hadoop 问题的效率会提升一大截。
6. 一些绕开麻烦的实操习惯与扩展方向
安装和配置 Hadoop 是一个流程标准、试错路径清晰的过程。走完一遍后我建议大家养成这几个习惯,能给后续使用省下不少麻烦。
第一个习惯是启动集群前先清理僵尸进程。如果上次关闭不干净,历史进程还占着端口或内存资源,这次启动会出现各种难以定位的怪异现象。每次重新启动前执行一下 jps,确认只剩 jps 本身,再启动集群。
第二个习惯是合理使用快照。虚拟机做一次干净的环境快照,然后把 Hadoop 安装配置完以后再做一个快照。这样以后无论是学习实验还是排查问题,都能快速恢复环境。这个习惯对我个人帮助极大,特别是在反复尝试不同参数、不同实验时,快照就是后悔药。
第三个习惯是留意文件权限。以 root 用户安装 Hadoop,但运行时创建 HDFS 目录、操作文件时,建议统一切换到同一个固定用户。Hadoop 对文件权限有一套完整的模拟 POSIX 权限体系,如果一会儿用 root 一会儿用其他用户,会出现 Permission denied 的报错。统一用户身份是最省心的方式。
第四个建议是从伪分布式平滑过渡到完全分布式。这次配置中的 workers 文件、core-site.xml、hdfs-site.xml、yarn-site.xml 已经完美呈现了分布式集群配置的骨架。扩展时,准备三台或多台虚拟机,把配置复制过去,修改 fs.defaultFS 里的主机名为 NameNode 所在节点的主机名,修改 workers 为所有 DataNode 节点,配置节点间 SSH 免密,重复一次格式化和启动流程,完全分布式集群就起来了。
我在实际带大家的经验中发现,能把伪分布式理解透彻的人,转到真实集群时几乎不会遇到认知层面的障碍。因为所有核心概念——NameNode 管元数据、DataNode 存数据块、ResourceManager 管资源、NodeManager 执行计算——都在伪分布式环境中展示得淋漓尽致。区别只是从一台机器变成了多台机器。
另外提一句版本升级的问题。学会了 3.1.3 的安装流程后,换到 3.3.x 甚至更高版本,步骤基本一致,唯一要注意的是个别端口变化和新增参数。比如 Hadoop 3.2 之后对 S3A 客户端、云存储相关的配置更完善,但对常规安装而言并不会有颠覆性变化。所以打好 3.1.3 这套基础,后续升级只是查阅官方文档核实参数变动而已,不需要重头学起。