☰
Hadoop集群搭建实战:从单机到多节点部署与避坑指南
2026/10/15 9:08:33 网站建设 项目流程

1. 为什么单机跑得好好的,一上集群就各种报错

很多人第一次接触Hadoop,都是在单台机器上装个伪分布式,跑个WordCount,看到结果输出就觉得自己会了。然后领导说"咱们搭个真集群吧",三台机器一摆,配置文件一改,启动脚本一敲,接下来就是无尽的报错——NameNode起不来、DataNode连不上、ResourceManager拒绝连接、节点列表里少了一台……折腾两天,集群还是半死不活。

这个场景我见过太多次了。问题不在于Hadoop本身有多难,而在于单机模式和集群模式之间存在一道隐形的鸿沟:网络通信、主机名解析、SSH互信、目录权限、配置文件的一致性,这些东西在单机上要么不存在,要么被自动处理了,一到集群环境全部暴露出来。而大多数教程只告诉你"改这个文件、填那个IP",却不告诉你为什么要这么做,一旦环境稍有不同,你就不知道该怎么排查。

这篇内容面向的是有一定Linux基础、准备在真实多节点环境上搭建Hadoop集群的开发者或运维人员。我会从集群规划开始,把每一步的操作意图、参数含义、容易踩的坑都讲清楚。你跟着走一遍,应该能搭出一个稳定可用的Hadoop集群,更重要的是,下次遇到类似问题你知道该往哪个方向查。

提示:本文基于Hadoop 3.x版本编写,具体操作在主流Linux发行版上均可复现。不同发行版的包管理命令略有差异,我会在涉及的地方标注出来。

2. 集群规划:三台机器怎么分配角色才合理

2.1 节点角色划分的基本逻辑

Hadoop集群的核心角色分为两类:主节点(Master)和从节点(Slave)。主节点上跑的是NameNode(管理HDFS元数据)和ResourceManager(管理YARN资源调度),从节点上跑的是DataNode(存实际数据)和NodeManager(执行计算任务)。

最小的可用集群需要几台机器?理论上两台就够——一台主一台从。但实际生产中,三台是最低推荐配置,原因有两个:一是主节点上的NameNode和ResourceManager对内存消耗较大,分开部署会更稳;二是三副本存储策略下,三台机器刚好能保证每个数据块有三个副本分布在不同节点上。

我建议的规划方案是这样的:

主机名IP示例角色分配建议内存
master192.168.1.100NameNode, ResourceManager, SecondaryNameNode4GB+
slave1192.168.1.101DataNode, NodeManager2GB+
slave2192.168.1.102DataNode, NodeManager2GB+

SecondaryNameNode放在master上是因为它本身不承担太多计算压力,主要工作是定期合并fsimage和edits文件,对内存要求不高。如果机器充裕,把它单独放一台更好,但三台机器的场景下没必要。

2.2 主机名和IP映射:别小看这一步

Hadoop节点之间通信大量依赖主机名,而不是IP。如果你在配置文件里写IP,短期能跑,但后期扩容或迁移时会非常痛苦。所以第一步就是把主机名规划好,并且在每台机器的/etc/hosts文件里做好映射。

三台机器分别执行:

# 设置主机名(以master为例) hostnamectl set-hostname master # 编辑hosts文件 vim /etc/hosts

在每台机器的/etc/hosts中加入以下内容(三台机器保持一致):

192.168.1.100 master 192.168.1.101 slave1 192.168.1.102 slave2

改完之后用ping master、ping slave1验证一下,确保三台机器之间都能通过主机名互相ping通。这一步看起来简单,但我踩过的坑是:改了主机名但没重启网络服务,导致hostname命令显示新名字但系统实际解析还是旧名字。稳妥的做法是改完后执行systemctl restart network(CentOS系)或systemctl restart systemd-networkd(Ubuntu系),然后重新登录终端确认。

2.3 时间同步:一个容易被忽略的隐形杀手

Hadoop集群对时间敏感度很高。如果节点之间时间差超过默认的30秒,DataNode会被NameNode拒绝连接,日志里报"Clock skew"相关的错误。虚拟机环境尤其容易出这个问题——挂起再恢复后时间就偏了。

最省事的方案是配置NTP同步。以master作为时间服务器,slave节点从master同步:

# 在master上安装并启动NTP服务 yum install -y ntp # CentOS # 或 apt install -y ntp # Ubuntu systemctl start ntpd systemctl enable ntpd # 在slave节点上同步master的时间 ntpdate master

如果环境不允许搭NTP服务,至少写个定时任务,每隔几分钟同步一次。别等到集群跑了一周突然挂掉才想起来查时间。

3. 环境准备:JDK、SSH互信和防火墙处理

3.1 JDK安装:版本选择有讲究

Hadoop 3.x要求JDK 8或JDK 11。我建议用JDK 8,兼容性最好,社区里绝大多数问题都是基于JDK 8解决的。JDK 11虽然也支持,但某些Hadoop组件在JDK 11下会有模块化相关的警告,新手容易被吓到。

三台机器都要装JDK,步骤一致:

# 下载JDK 8(以某开源发行版为例) tar -zxvf jdk-8uXXX-linux-x64.tar.gz -C /usr/local/ mv /usr/local/jdk1.8.0_XXX /usr/local/java # 配置环境变量 vim /etc/profile

在/etc/profile末尾添加:

export JAVA_HOME=/usr/local/java export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

执行source /etc/profile生效,然后用java -version验证。三台机器都要做,别偷懒只做一台。

注意:JAVA_HOME的路径一定要记清楚,后面配置Hadoop的时候要反复用到。如果路径写错,Hadoop启动时会报"JAVA_HOME is not set"或者更隐蔽的类加载错误。

3.2 SSH免密登录:Hadoop启动脚本的硬性依赖

Hadoop的启动脚本(start-dfs.sh、start-yarn.sh)需要在master上通过SSH免密登录到所有节点,去启动对应的守护进程。如果没配免密,每启动一次集群就要输好几次密码,自动化脚本根本没法跑。

配置步骤:

# 在master上生成密钥对 ssh-keygen -t rsa # 一路回车,不设密码 # 把公钥复制到本机和所有slave节点 ssh-copy-id master ssh-copy-id slave1 ssh-copy-id slave2

完成后用ssh slave1测试,如果能不输密码直接登录就说明配置成功。这里有个细节:第一次SSH连接会提示确认主机指纹,需要输入yes。如果你在脚本里自动执行,这个交互会卡住。解决办法是提前在~/.ssh/config里加上StrictHostKeyChecking no,或者手动先连一遍把指纹存下来。

另外,slave节点之间也需要互相免密吗?严格来说,如果你只在master上启动集群,slave之间不需要。但实际运维中经常需要从任意节点跳转到其他节点排查问题,所以我建议所有节点之间都配好双向免密,省得后面麻烦。

3.3 防火墙和SELinux:该关就关,别犹豫

Hadoop集群内部通信会用到大量端口——NameNode的8020、9870,DataNode的9866,ResourceManager的8032、8088等等。如果防火墙开着,这些端口默认都是被封的,节点之间根本连不上。

测试环境最省事的做法是直接关掉防火墙和SELinux:

# 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 关闭SELinux(临时) setenforce 0 # 永久关闭SELinux vim /etc/selinux/config # 把 SELINUX=enforcing 改为 SELINUX=disabled

生产环境当然不能这么粗暴,需要按端口逐个放行。但如果你是在自己的实验环境里搭集群,先把防火墙关了能排除掉一大类"连不上"的问题。等集群跑通了,再回头研究需要开放哪些端口。

4. Hadoop安装与核心配置文件的逐项拆解

4.1 安装包解压与目录规划

把Hadoop安装包解压到/usr/local/下,重命名为hadoop:

tar -zxvf hadoop-3.x.x.tar.gz -C /usr/local/ mv /usr/local/hadoop-3.x.x /usr/local/hadoop

接下来配置Hadoop的环境变量,在/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_CONF_DIR这个变量很多人不配,但某些工具和脚本会依赖它来定位配置文件,配上没坏处。

4.2 需要改动的配置文件清单

Hadoop的配置文件都在$HADOOP_HOME/etc/hadoop/目录下。需要修改的有以下几个:

  • hadoop-env.sh:指定JAVA_HOME
  • core-site.xml:核心配置,指定HDFS的默认文件系统和临时目录
  • hdfs-site.xml:HDFS相关配置,副本数、NameNode和DataNode的数据目录
  • mapred-site.xml:MapReduce运行框架指定
  • yarn-site.xml:YARN相关配置
  • workers:指定所有从节点的主机名

下面逐个拆解。

4.3 core-site.xml:集群的"身份证"

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://master:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/usr/local/hadoop/tmp</value> </property> </configuration>

fs.defaultFS告诉客户端"默认的HDFS入口在哪",写的是master的主机名和NameNode的RPC端口。8020是Hadoop 3.x的默认端口,Hadoop 2.x用的是9000,别搞混了。

hadoop.tmp.dir是Hadoop的临时目录,很多运行时产生的文件都放这里。这个目录一定要手动创建,Hadoop不会自动帮你建。而且不要用/tmp,因为系统重启时/tmp可能被清空,导致集群数据丢失。

4.4 hdfs-site.xml:副本数和数据目录

<configuration> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/usr/local/hadoop/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/usr/local/hadoop/data/datanode</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>master:9868</value> </property> </configuration>

dfs.replication设为3,表示每个数据块存三份。如果你只有两台DataNode,设3会导致部分块无法达到副本数,NameNode会一直报"under-replicated blocks"的警告。所以副本数不要超过DataNode的数量。

dfs.namenode.name.dir和dfs.datanode.data.dir分别指定NameNode和DataNode的数据存储路径。这些目录也要手动创建,并且确保运行Hadoop的用户有写权限。

4.5 mapred-site.xml和yarn-site.xml:计算框架的配置

mapred-site.xml:

<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上。第二个属性是Hadoop 3.x新增的,不配的话跑MapReduce任务时会报ClassNotFoundException。

yarn-site.xml:

<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>master</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.nodemanager.aux-services必须设为mapreduce_shuffle,这是MapReduce任务在YARN上运行的必要配置。env-whitelist是Hadoop 3.x的要求,不配的话NodeManager可能找不到JAVA_HOME。

4.6 workers文件:从节点清单

workers文件(Hadoop 2.x叫slaves)里列出所有DataNode和NodeManager所在的主机名:

slave1 slave2

每行一个主机名,不要有多余的空格或空行。master本身要不要加进去?如果你想让master也作为DataNode参与存储,可以加。但一般建议master专注做管理,不存数据。

4.7 配置文件的分发:一致性是关键

以上配置文件在master上改好后,需要同步到所有slave节点。用scp批量复制:

scp -r /usr/local/hadoop/etc/hadoop/ slave1:/usr/local/hadoop/etc/hadoop/ scp -r /usr/local/hadoop/etc/hadoop/ slave2:/usr/local/hadoop/etc/hadoop/

所有节点的配置文件必须完全一致,除了hadoop-env.sh里的JAVA_HOME路径可能因安装位置不同而有差异。配置文件不一致是集群启动失败的最常见原因之一,排查起来还很费劲,因为日志里的报错往往指向别的地方。

5. 格式化与启动:顺序错了就白干

5.1 NameNode格式化:只能做一次

在master上执行:

hdfs namenode -format

这个命令会初始化NameNode的元数据目录。看到"Storage directory ... has been successfully formatted"就说明成功了。

注意:格式化只能执行一次。如果你格式化了多次,会导致NameNode的clusterID和DataNode的clusterID不一致,DataNode会拒绝启动。如果确实需要重新格式化,必须先把所有节点上的data目录清空,再重新格式化。

5.2 启动HDFS和YARN

# 启动HDFS start-dfs.sh # 启动YARN start-yarn.sh

启动脚本会通过SSH到workers文件中列出的节点上启动对应进程。执行完后用jps命令检查各节点的进程:

  • master上应该有:NameNode、SecondaryNameNode、ResourceManager
  • slave上应该有:DataNode、NodeManager

如果某个进程没起来,去对应节点的$HADOOP_HOME/logs/目录下看日志,日志文件名一般包含进程名和用户名。

5.3 Web UI验证:最直观的检查方式

Hadoop 3.x的Web UI端口和2.x不同,别记错了:

服务端口访问地址
HDFS NameNode9870http://master:9870
YARN ResourceManager8088http://master:8088
SecondaryNameNode9868http://master:9868

打开NameNode的页面,在"DataNodes"一栏应该能看到所有slave节点都是"In Service"状态。如果少了某个节点,检查该节点的DataNode日志,常见原因是网络不通、防火墙没关、或者clusterID不匹配。

6. 踩坑实录:那些让我熬夜的报错和排查过程

6.1 DataNode启动后立刻退出

现象:jps看到DataNode进程一闪而过,日志里报"java.net.BindException: Problem binding to ... 9866"或者"Directory is not writable"。

排查思路:先看日志里的具体报错。如果是端口被占用,用netstat -tlnp | grep 9866查一下谁占用了。如果是目录权限问题,检查dfs.datanode.data.dir指定的目录是否存在、当前用户是否有写权限。我遇到过一次是因为之前用root格式化过,后来用普通用户启动,目录归属不对,chown -R改一下就好了。

6.2 NameNode和DataNode的clusterID不一致

现象:DataNode日志报"java.io.IOException: Incompatible clusterIDs"。

原因:NameNode格式化多次,或者DataNode的data目录是从别的集群拷贝过来的。

解决:找到NameNode的dfs.namenode.name.dir/current/VERSION文件,里面的clusterID值复制出来,替换到每个DataNode的dfs.datanode.data.dir/current/VERSION文件中。改完后重启DataNode。

6.3 ResourceManager启动后NodeManager连不上

现象:ResourceManager的Web UI里"Active Nodes"显示为0。

排查:先确认NodeManager进程是否在slave上运行。如果进程在但连不上,检查yarn-site.xml里yarn.resourcemanager.hostname配置是否正确,以及slave节点能否ping通master。还有一个隐蔽的原因:master的hostname解析问题。如果master的/etc/hosts里把自己的主机名映射到了127.0.0.1而不是实际IP,ResourceManager会绑定到回环地址,外部节点自然连不上。

6.4 启动脚本报"Permission denied"

现象:执行start-dfs.sh时提示某节点的SSH连接被拒绝。

排查:确认免密登录是否配置成功。手动ssh slave1试一下,如果需要输密码说明公钥没复制过去。另外检查~/.ssh目录和authorized_keys文件的权限——SSH对权限要求很严格,~/.ssh必须是700,authorized_keys必须是600,权限不对SSH会直接拒绝。

7. 集群跑通之后,这几件事值得再做一遍

集群能启动、Web UI能看到所有节点,这只是第一步。要让它稳定运行,还有几件事值得花时间做:

第一,配置日志聚合。默认情况下,YARN的容器日志散落在各个NodeManager的本地目录里,任务失败后想查日志得挨个节点去找。开启日志聚合后,日志会统一上传到HDFS上,通过ResourceManager的Web UI就能直接查看。在yarn-site.xml中加上yarn.log-aggregation-enable设为true即可。

第二,设置合理的内存参数。YARN默认给每个NodeManager分配8GB内存和8个vCPU,如果你的机器没这么大,NodeManager启动时会报"Invalid resource request"之类的错误。在yarn-site.xml中根据实际硬件配置yarn.nodemanager.resource.memory-mb和yarn.nodemanager.resource.cpu-vcores。

第三,写一个集群启停脚本。每次手动敲start-dfs.sh和start-yarn.sh太麻烦,而且有时候需要按特定顺序启动。写个简单的shell脚本,把启动、停止、状态检查都封装进去,日常运维会轻松很多。

第四,定期检查磁盘空间。HDFS的数据目录会随着使用不断增长,如果磁盘满了,DataNode会进入不健康状态。配个监控或者定时任务,在磁盘使用率超过阈值时发告警。

我在实际使用中体会最深的一点是:Hadoop集群的绝大多数问题都能在日志里找到答案,关键是要知道去哪个节点、哪个目录、看哪个日志文件。把日志路径和常见报错的关键词记熟,排查效率会高很多。另外,配置文件改完后一定要同步到所有节点,我因为忘了同步导致集群行为诡异的情况不止一次两次了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询