恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Hadoop完全分布式集群搭建:从伪分布式到多节点实战
首页
资讯中心
/
Hadoop完全分布式集群搭建:从伪分布式到多节点实战
Hadoop完全分布式集群搭建:从伪分布式到多节点实战
发布时间:2026/10/22 2:39:57
上一篇把Hadoop在单机里跑成了伪分布式读写测试倒是通了但那种感觉就像一个人在家玩大富翁棋盘铺了一大片真正的“玩家”却只有你自己。等你想让多个人同时用数据、多个任务并发跑的时候单节点的瓶颈立刻就暴露出来。这篇Hadoop集群搭建2的内容就是来解决这个问题的把上一章的伪分布式升级成真正意义上的多节点集群让HDFS的存储和YARN的计算都落到多台机器上再顺带把我在反复搭建过程中踩过的坑、总结出的配置经验一并写清楚。这篇适合两类人一类是刚把伪分布式跑通、准备往完全分布式迈进的初学者另一类是已经在测试环境里搭过集群但被DataNode不注册、作业OOM、NameNode反复重启这类问题折腾过一遍的运维新手。文章不会讲太多高深原理重点是你照着做能搭起来出了问题知道去哪看、怎么改。1. 为什么第二篇要把集群拆成多节点1.1 伪分布式和完全分布式的体验差异伪分布式最大的特点是NameNode、DataNode、ResourceManager、NodeManager全部跑在同一台Linux上。进程不冲突、配置简单HDFS能读写、MapReduce能跑看起来“麻雀虽小五脏俱全”。但你稍微想一下就会明白这其实是一家店同时充当前台、后厨、仓库和收银台所有活都是一台机器干的数据也只在本地存了一份。完全分布式不一样。NameNode统一管理元数据多个DataNode分散存储真实数据块ResourceManager负责全局资源调度多个NodeManager在各自节点上执行计算任务。数据分块放到不同的节点上计算任务也分发到数据所在的机器上去跑。用一个更生活的类比伪分布式像小卖部完全分布式像连锁店。连锁店的中央仓库管库存各家分店接单干活货不够了从仓库调度某个分店忙不过来其他分店也能顶上。这也解释了为什么第一篇文章之后第二篇必须重新撸一遍集群如果你只是在自己电脑上验证Hadoop能跑伪分布式够用了但一旦涉及多用户共享数据、多个作业并行、数据量上去之后磁盘和内存不够用就必须靠多节点才能扛住。这一篇的核心目标就是三个把HDFS的DataNode数量扩到多个、把YARN的NodeManager数量扩到多个、让一个分布式作业真正跑在多台机器上。1.2 版本选型和节点规划的取舍动手之前先把版本定下来。Apache Hadoop 3.x系列是目前使用最广的版本其中3.3.x算是一个相对稳定的选择。相比2.x3.x在默认端口、配置文件名称、内部通信协议上都有变化比如NameNode Web UI从50070变成了9870slaves文件改名成了workers默认端口也从9000变成了8020。如果你拿网上的老教程硬套3.x很多坑就是这么踩出来的。节点规划方面测试环境最常用的是三节点一台机器作为NameNode ResourceManager另外两台机器作为DataNode NodeManager。三节点既能满足两个DataNode同时工作又能把默认副本数3跑起来规模不大不小排查问题也方便。三个节点的推荐配置可以按下面的表来设计主机名角色推荐配置磁盘规划hd-nnNameNode、ResourceManager、SecondaryNameNode8C16G以上系统盘 一块数据盘hd-dn1DataNode、NodeManager8C16G以上系统盘 一块或两块数据盘hd-dn2DataNode、NodeManager8C16G以上系统盘 一块或两块数据盘至于NameNode和ResourceManager为什么不分开主要是测试环境压缩成本考虑。生产环境不仅要拆开还会做NameNode HA和ResourceManager HA那是之后的内容这篇先把非HA的完整分布式跑通。还有一个很实际的建议每台机器内存别给太抠16G起步。你在配置YARN的时候会体会到内存越紧容器调度越容易出问题排查起来特别费时间。2. 环境准备SSH免密、系统参数、磁盘布局2.1 SSH免密配置与常见坑完全分布式集群里NameNode节点启动时会通过SSH登录到其他机器上拉起DataNode、NodeManager进程。start-dfs.sh和start-yarn.sh之所以能“一键启动”靠的就是SSH免密登录。如果免密没配好脚本会在半路卡住或者直接报Host key verification failed。配置步骤其实很简单ssh-keygen -t rsa -b 2048 ssh-copy-id hd-nn ssh-copy-id hd-dn1 ssh-copy-id hd-dn2第一条命令生成密钥时一路回车即可不要设置passphrase否则启动脚本照样要输密码。配完之后用ssh hd-dn1 date验证能直接返回时间就说明通了。这里有一个几乎所有人都遇到过的问题明明执行了ssh-copy-id也提示成功但再登录还是要求输密码。原因大概率是authorized_keys文件权限太宽SSH为了保证安全会拒绝加载。解决办法是把远端机器上的~/.ssh目录设为700authorized_keys文件设为600chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys另外强烈建议用一个专用的管理用户比如hadoop而不是直接用root跑集群。root虽然省事但Hadoop有些脚本对root用户会有额外的环境处理逻辑后续权限管理也容易混乱。三台机器都创建同一个用户目录结构保持一致会省掉很多不必要的麻烦。2.2 系统参数与磁盘规划多节点集群比单机多了一个隐含要求节点之间的时间必须基本一致。HDFS的租约和YARN的任务调度都依赖时间戳某台机器时钟偏了轻则日志对不上严重时客户端会频繁报“无法续租”甚至导致数据块读写超时。测试环境最简单的做法是所有节点安装chrony指向同一台时间源systemctl enable chronyd --now chronyc sources除了时钟还得调两个经典的资源限制文件句柄数和用户进程数。Hadoop每个节点会打开大量文件DataNode存储上千个block后句柄数很容易达到默认上限。在/etc/security/limits.conf里加上hadoop soft nofile 65535 hadoop hard nofile 65535 hadoop soft nproc 65535 hadoop hard nproc 65535接着是磁盘布局。很多新手把Hadoop数据直接放在系统盘甚至默认路径/tmp下这其实是大忌。Linux系统盘的/tmp可能被系统清理一旦NameNode的元数据目录被清了整个集群等于从零开始。正确做法是给Hadoop单独准备数据目录比如挂载一块独立数据盘到/data0和/data1然后在配置里指向这些目录。如果没有独立数据盘至少单独建目录并确认不会被系统临时清理。最后vm.swappiness建议调低一些默认60太高容易让系统优先使用swap导致性能抖动sysctl -w vm.swappiness10这个值写进/etc/sysctl.conf重启才会保留。这里不推荐测试环境直接关闭防火墙因为生产环境还是要开的测试环境为了省排查时间可以放通集群节点间的常用端口避免DataNode心跳被拦。2.3 JDK、Hadoop安装与目录约定的注意点装JDK这一节看起来基础实际踩坑的人不少。Hadoop 3.3.x对JDK8和JDK11都支持但实测下来JDK8最稳。不要一上来就装最新的JDK17很多老版本的Hadoop在JDK17下会因为依赖包兼容问题直接起不来。安装Hadoop解压后第一个常见报错是运行命令时提示JAVA_HOME is not set。原因很简单Hadoop自带脚本没有自动识别JDK路径。解决办法是在hadoop-env.sh里显式写死export JAVA_HOME/usr/local/jdk1.8.0_202写完执行source或者重新打开终端再用hadoop version验证。目录约定这一块建议初始化出这样的结构/data0/hadoop/tmp /data0/dfs/name /data0/dfs/data /data1/dfs/data其中tmp目录对应hadoop.tmp.dirname目录存放NameNode元数据data目录存放真正的数据块。数据目录必须提前创建并且所有者是hadoop用户mkdir -p /data0/hadoop/tmp /data0/dfs/name /data0/dfs/data chown -R hadoop:hadoop /data0 /data1这里有个很容易忽略的细节你给dfs.namenode.name.dir配置了多个目录时每个目录都必须提前创建好否则NameNode启动时会因为找不到目录直接退出而且报错信息藏在日志里表面上看就像“进程秒退”。3. 五个配置文件逐行拆解3.1 core-site.xml先把“门牌号”定下来core-site.xml是整个Hadoop的全局配置最核心的项就两个默认文件系统地址和临时目录。configuration property namefs.defaultFS/name valuehdfs://hd-nn:8020/value /property property namehadoop.tmp.dir/name value/data0/hadoop/tmp/value /property /configurationfs.defaultFS决定了客户端不指定路径时默认连到哪个NameNode。这里的值要写成hdfs://主机名:8020端口8020是3.x的NameNode RPC端口如果你用的是老版本或者自定义过要以实际为准。全网很多教程用9000端口那基本是2.x时代的写法3.x默认值是8020。hadoop.tmp.dir是一个“父目录”NameNode元数据、DataNode的数据块、以及一些临时文件默认都会挂在它下面。正因为影响面大才特别强调不要放到系统的/tmp否则系统一清临时文件集群就废了。如果你在hdfs-site.xml里已经单独指定了name.dir和data.dir那么这个tmp目录主要影响的是其它临时文件。还有一个容易忽略的点所有节点的core-site.xml内容必须一致。有些新手只在NameNode上改了配置DataNode还是旧的fs.defaultFS结果DataNode起来之后完全不知道该连谁。后面的其它配置文件也一样除个别专属配置外尽量保持集群内一致。3.2 hdfs-site.xml元数据、副本和磁盘hdfs-site.xml是HDFS最重要的配置文件。先说清楚几个关键参数的作用再给你一份可以直接抄的配置。configuration property namedfs.namenode.name.dir/name value/data0/dfs/name/value /property property namedfs.datanode.data.dir/name value/data0/dfs/data,/data1/dfs/data/value /property property namedfs.replication/name value3/value /property property namedfs.namenode.secondary.http.address/name valuehd-nn:9868/value /property /configurationdfs.namenode.name.dirNameNode元数据存放位置。生产上强烈建议配置多个目录并且放在不同磁盘上这样即使一块盘坏掉另一块还能恢复元数据。测试环境至少指定一个目录但目录必须提前建好。dfs.datanode.data.dir每个DataNode的数据存储位置多个目录用逗号分隔。这块是真正存储数据块的地方多块数据盘就多写几个目录数据块会均匀分布到所有目录能有效分散单盘IO压力。dfs.replication副本数。三节点集群保持不变设成3意味着一份数据会同时存到三个DataNode上。如果你把副本数设为1某个节点磁盘坏了数据就找不回来设为3三节点集群偶尔坏一台机器数据还能完整恢复。dfs.namenode.secondary.http.addressSecondaryNameNode的地址和端口。很多人以为SecondaryNameNode是NameNode的热备容易误解。它其实主要做一件事定期从NameNode拉取编辑日志edits跟fsimage合并减轻NameNode重启时的恢复压力。地址就写在NameNode本机IP加端口9868。另外有一个值得了解的调优参数dfs.namenode.handler.count表示NameNode同时处理RPC请求的线程数。默认值是10对并发要求较高的集群可以适当调大比如100。我当时测试过默认值在小集群下问题不大但如果同一时间很多客户端同时发起文件操作就会明显感觉到RPC响应变慢。3.3 yarn-site.xml资源调度与跑任务的通道YARN是MapReduce作业的调度平台配置文件最需要花点心思的是内存相关的参数。先把最基础的配置贴出来configuration property nameyarn.resourcemanager.hostname/name valuehd-nn/value /property property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.log-aggregation-enable/name valuetrue/value /property /configurationyarn.resourcemanager.hostname很好理解ResourceManager跑在哪台机器上所有NodeManager启动后会主动向这个地址注册。yarn.nodemanager.aux-services必须设为mapreduce_shuffle否则Map任务产生的中间结果无法通过网络传输给Reduce任务作业会卡在shuffle阶段表现很诡异任务不报错就是迟迟不完成。yarn.log-aggregation-enable设为true后任务运行完日志会被收集到HDFS上之后用yarn logs -applicationId就能查看历史日志。这个开关在排查历史任务问题时会让你节省大量时间建议打开。内存参数才是YARN配置的重头。默认值对小集群非常不友好可以这样理解NodeManager会把自己这台机器的可用内存划分成若干“容器”每个Map或Reduce任务占用一个容器。如果你不指定YARN会按一种保守策略给容器分很少的内存作业一上来就报OOM。我建议按实际物理内存手动指定property nameyarn.nodemanager.resource.memory-mb/name value12288/value /property property nameyarn.scheduler.minimum-allocation-mb/name value1024/value /property property nameyarn.scheduler.maximum-allocation-mb/name value8192/value /property假设每台NodeManager机器是16G内存上面配置把12G交给YARN调度留出4G给系统进程和Java进程本身的开销。如果你把16G全部交给YARN系统自己内存不足直接触发Linux的OOM Killer反而更不稳定。minimum-allocation-mb是单个容器最少分配的内存maximum-allocation-mb是最大分配内存。容器不能小于最小值也不能超过最大值这个区间决定了并发度12G除以1G最多同时跑12个容器具体作业能申请多大还要看作业自身的mapreduce.map.memory.mb。还有一个新手的经典困惑作业容器运行时总被“杀”掉看一眼NodeManager日志发现“Container killed on request. Exit code is 143”或者“虚拟内存超限”。这是因为YARN默认开启了虚拟内存检查yarn.nodemanager.vmem-check-enabled它会用物理内存加swap的总量去判断容器有没有超限。测试机器内存不大时这个检查经常会误杀容器。一种做法是关掉property nameyarn.nodemanager.vmem-check-enabled/name valuefalse/value /property我更推荐的思路是提高比例而不是直接关闭property nameyarn.nodemanager.vmem-pmem-ratio/name value4/value /property默认比例是2.1也就是说容器申请1G物理内存虚拟内存超过2.1G就会被杀。测试环境下有些Java进程虚拟内存天然偏高把比例调到4到5既能避免误杀又不至于完全关闭安全保护。3.4 mapred-site.xml 与 workers 文件mapred-site.xml是MapReduce专用的配置。最核心的是框架名称configuration property namemapreduce.framework.name/name valueyarn/value /property /configuration这个参数如果不配MapReduce默认在本地跑作业提交到集群后根本不会走YARN你会看到作业状态是local节点信息也是空的。这个被坑过的人太多了所以写成第一条。另外一个可以顺手配上的property namemapreduce.jobhistory.address/name valuehd-nn:10020/value /property property namemapreduce.jobhistory.webapp.address/name valuehd-nn:19888/value /property这个配置对应JobHistory服务用来记录已完成的作业日志。不配也能跑但想在8088的Web页面上看历史作业的运行详情就起不来。配上之后需要额外启动mapred --daemon start historyserver。然后是workers文件。3.x版本里它叫workers2.x叫slaves。路径在$HADOOP_HOME/etc/hadoop/workers内容很简单hd-nn hd-dn1 hd-dn2这个文件的作用是告诉NameNode节点当执行start-dfs.sh时哪些机器要启动DataNode当执行start-yarn.sh时哪些机器要启动NodeManager。很多人格式化NameNode、改配置都做了最后发现只有NameNode自己是DataNode原因就是workers文件里默认只有localhost。别忘了NameNode本身也可以作为DataNode和NodeManager运行所以我把hd-nn也写进去了。4. 集群初始化和启动验证4.1 第一次格式化NameNode配置全部就位之后进入NameNode所在机器执行hdfs namenode -format这一步做了两件事在dfs.namenode.name.dir指定的目录下生成fsimage文件同时生成一个全局唯一的clusterID这个clusterID会被记录在NameNode的VERSION文件中后续每个DataNode连接时都会携带自己的clusterID只有和NameNode一致才能正常注册。格式化只需要执行一次这是一个非常关键的纪律。很多人因为DataNode起不来反手就重新格式化NameNode结果整个集群的元数据全部丢失DataNode上的数据也彻底“失联”等于推倒重来。正确思路是如果是第一次搭集群格式化NameNode没问题如果集群已经运行过并且有数据千万不要因为某个节点故障就天真地重新格式化。还有一个细节格式化时如果提示storage directory does not exist一定要先创建好对应的目录再执行不要忽略警告直接往下走。格式化成功后会输出类似Storage directory /data0/dfs/name has been successfully formatted的日志这一步确认清楚再继续。4.2 启动集群并完成基础验证启动命令分两步执行start-dfs.sh start-yarn.sh不推荐直接用start-all.sh。分开执行最大的好处是出了问题能知道是HDFS没起来还是YARN没起来排查范围缩小一半。启动完成后在hd-nn上执行jps能看到这些进程NameNode SecondaryNameNode ResourceManager在hd-dn1和hd-dn2上执行jps能看到DataNode NodeManager看到进程还不够还要验证HDFS层面和YARN层面的健康状态hdfs dfsadmin -report这个命令会列出所有存活的DataNode、容量信息、以及每台节点的数据块数量。如果显示的DataNode数量比实际少说明集群有问题直接去排查。YARN侧用yarn node -list正常会显示两个NodeManager节点状态为RUNNING。Web界面也要打开看一眼端口对照表存一下很实用服务端口说明NameNode Web UI9870查看HDFS状态、数据块分布ResourceManager Web UI8088查看YARN应用、节点列表DataNode Web UI9864查看单节点数据块情况JobHistory Web UI19888查看历史作业日志我在测试环境见过一种情况jps里进程全都有dfsadmin -report也正常但浏览器访问9870就是打不开。多半是该节点防火墙没放行或者没关先确认端口通不通再怀疑配置。4.3 跑通第一个分布式任务进程全部正常后跑一个最小的MapReduce任务验证算力链路。先准备测试数据目录hdfs dfs -mkdir -p /user/hadoop/input echo hello hadoop /tmp/test.txt hdfs dfs -put /tmp/test.txt /user/hadoop/input/然后用Hadoop自带的示例程序跑Pi任务这个任务短小又经典hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar pi 2 1000命令后面的参数代表map数2、每个map取样次数1000。任务提交后ResourceManager的8088页面里能看到一个RUNNING应用。等它的进度条走完看到Job Finished Successfully说明整个分布式链路已经打通。如果要验证WordCount也可以参考下面的命令hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar wordcount /user/hadoop/input /user/hadoop/output hdfs dfs -cat /user/hadoop/output/part-r-00000这里要注意一个很容易误导人的现象如果作业显示成功但结果文件不在HDFS里而在本地文件系统或者任务根本没进YARN的8088页面说明mapreduce-site.xml里的framework忘了改成yarn作业是在本地模式跑的不是分布式。5. 踩坑实录三个最让我头疼的问题5.1 启动后DataNode失踪这是一个高频问题start-dfs.sh执行完jps看DataNode进程也在但hdfs dfsadmin -report里显示的DataNode数量就是不对。查看DataNode日志常见的报错是Block pool ID needed, but the datanode is running with the old block pool这类问题九成是clusterID不一致造成的。原因是每台机器的DataNode第一次启动时会把NameNode的clusterID写入自己的VERSION文件但如果之前DataNode目录不是干净的或者格式化过一次NameNode而DataNode数据没清两边记录的clusterID就对不上。解决办法按下面的步骤来处理不要一上来就重新格式化停止集群stop-dfs.sh。找到NameNode和DataNode上的VERSION文件。一般位于dfs.namenode.name.dir和dfs.datanode.data.dir对应的current目录下。对比NameNode和DataNode两边VERSION文件里的clusterId字段。不一致时把DataNode侧VERSION文件里的clusterId改成和NameNode一致。重新启动start-dfs.sh再用hdfs dfsadmin -report确认。这个处理方式的本质是让所有节点用同一个身份标识加入集群而不是靠反复格式化来“重新生成身份”保住已有的数据块不被清空。某次我在模拟项目X里处理这个问题时数据目录里已经存了接近200G数据靠统一clusterID的方式直接把集群救回来完全没有重新初始化。5.2 MR任务卡在ACCEPTED或者直接OOM另一种让人摸不着头脑的情况是作业一直显示ACCEPTED但就是分配不到容器。先检查YARN节点的状态yarn node -list如果节点状态不是RUNNING说明NodeManager没有正常向ResourceManager注册。要么是yarn-site.xml里resourcemanager的地址写错要么是NodeManager这台机器上配置的aux-services不对。逐一比对即可。如果节点状态RUNNING但作业还是不动大概率是内存配置问题。日志里显示Application is added to the scheduler and is not yet activated那你需要去ResourceManager的8088页面里看“Scheduler”相关信息。常见的情况是你给了yarn.scheduler.maximum-allocation-mb设置了一个很大的值但NodeManager实际可用内存只有很小的配额或者反过来作业的容器内存申请超过了NodeManager能提供的上限。解决思路是重新核对上一节里的内存参数确保yam.nodemanager.resource.memory-mb 小于等于节点物理内存减去系统预留yarn.scheduler.minimum-allocation-mb 作业申请的容器内存 maximum-allocation-mb不要在同一台机器上把YARN内存和Java堆内存加到一起超过物理内存。还有一类OOM是被虚拟内存检查误杀的现象是任务运行一小会儿就失败NodeManager日志里有Container killed by the ApplicationMaster之类的记录。如果你检查过物理内存足够可以把yarn.nodemanager.vmem-pmem-ratio调大或者临时关闭检查。这个“区分物理内存不足和虚拟内存检查误杀”的经验是我在多次测试环境里总结出来的日志里面关键词都不太一样先看是“physical memory”还是“virtual memory”再决定怎么调。5.3 NameNode怎么都起不来的排查方式NameNode起不来的情况最让人崩溃因为表面看没报什么错进程启动后又立刻消失。其实这类问题都藏在日志里。在hd-nn上找到NameNode的日志$HADOOP_HOME/logs/hadoop-hadoop-namenode-hd-nn.log最常见的三类错误分别是端口被占用NameNode RPC端口8020被别的进程占用了日志会提示Address already in use。用ss -lntp查一下端口占用情况Kill掉占用进程或改端口。目录权限不对dfs.namenode.name.dir指定的目录所有者不是hadoop用户或者目录没创建NameNode启动时会报Cannot create directory之类的错误。检查目录是否存在、属主是否正确目录不存在就先mkdir再chown。元数据目录里没有格式化信息如果你改了dfs.namenode.name.dir但没有重新格式化NameNode找不到fsimage文件日志里会明确提示Storage directory ... is missing。还有一个隐蔽问题配置了多个name.dir其中某个目录存在但另一块磁盘没挂载或路径写错NameNode会以“某目录不存在”为由退出。处理的时候把日志里提示的路径逐一检查一遍比到处乱改配置更有效。把NameNode的排查看成“账房先生对账”账本目录找不到、账本打开不了、记账端口被占用账房先生宁可关门也不开工。日志里的信息通常非常直白静下心看几行就知道问题出在哪。6. 收尾留给后面踩坑的实用建议聊一点个人体会。集群搭建这件事追求“一次成型”是不现实的。我每次搭集群都是先跑一个最小可用版本验证通了再加新特性加高可用、联邦这些之前先把纯多节点集群的配置背熟、排查手段练熟后面再聊HA就不会慌。还有几个小习惯可以提前养成每次改完配置文件之后先用diff对比一下和上一版的差异避免改错参数启动和停止集群尽量走脚本不要手动kill进程否则edits的合并状态可能不一致日志多看一眼比到处问人有效日志里写清楚的报错信息通常在最后三十行里就能找到根源。把这些习惯变成肌肉记忆比记住任何一份配置清单都更管用。