很多人看到“完全分布式”这四个字就有点发怵觉得要比伪分布式难很多。其实你只要把一个核心观念转过来——分布式不是多装几台机器的事而是要让多台机器“商量着干活”Hadoop 完全分布式的搭建就是把这个商量过程手动配一遍。这篇博文从零开始用最直白的话把整个搭建流程拆开讲适合刚学会 Linux 基本命令、准备入门大数据的读者参考。我会把每一步背后的想法也写出来这样你以后排查问题就有思路而不是只会照着命令敲。1. 动手之前先想清楚“完全分布式”到底长什么样1.1 一台 NameNode 加两台 DataNode最朴素的集群模型完全分布式和伪分布式最大的区别在于角色的“分家”。伪分布式里官方给了一个非常“作弊”的玩法让一个 Java 进程同时扮演所有角色看着像集群实际上还是一台机器。完全分布式就要诚实很多每台机器各司其职。最简单的完全分布式集群至少需要三台机器角色分配可以这样主机名角色必须启动的进程建议内存node01NameNode / ResourceManagerNameNode, ResourceManager至少 2GBnode02DataNode / NodeManagerDataNode, NodeManager至少 2GBnode03DataNode / NodeManagerDataNode, NodeManager至少 2GBNameNode 管的是“目录”也就是元数据。DataNode 管的是“文件内容”也就是真正的数据块。ResourceManager 管的是计算资源的分配NodeManager 负责在各自机器上执行计算任务。你把它们想成一家公司的三层结构NameNode 和 ResourceManager 是管理层DataNode 和 NodeManager 是业务层。管理层负责记账和派活业务层负责真正搬砖。1.2 为什么不能再用一台电脑模拟有的读者会问我电脑配置一般能不能开三台虚拟机能但你要知道资源开销在哪。Hadoop 集群里的每个进程都是独立的 JVM哪怕数据量很小JVM 本身也要吃内存。三台虚拟机每台分 2GB物理机至少要有 8GB 内存才跑得舒服。如果你的电脑只有 8GB 内存建议先升级到 16GB或者退一步先把伪分布式研究透等理解了进程之间如何通信再上多机。这里还有一个容易忽略的点虚拟机网络模式。搭建集群时不要用 NAT直接用“仅主机模式”或者“自定义 VMnet”让三台虚拟机处于同一个局域网互相能 ping 通。用 NAT 的话虚拟机之间通常也能通信但容易和宿主机网络混在一起调试端口时干扰因素更多。对于学习环境我倾向于使用仅主机模式配好静态 IP简单干净。1.3 我建议你这么做节点规划初学者最容易犯的错误是给三台机器起名 node01、node02、node03但 IP 随便配或者干脆用 DHCP 自动获取。这不叫规划这叫随缘。集群是需要长期稳定的东西DHCP 一重启地址就变了所有配置立刻失效。节点规划至少要定三件事内网静态 IP、主机名、机器用途。我自己常用的约定是IP 段用 192.168.56.x 这种局域网网段主机名和 IP 的对应关系统一写进每台机器的 /etc/hosts。比如192.168.56.10 node01 192.168.56.11 node02 192.168.56.12 node03为什么要写 /etc/hosts因为 Hadoop 的配置里可以用主机名代替 IP写主机名可读性强而且后续如果换 IP只需要改 hosts 文件不用改一堆 XML 配置。这是一个很实用的习惯。2. 零基础最容易翻车的准备阶段从虚拟机到免密登录2.1 Linux、JDK 和 Hadoop 版本怎么搭配如果你完全是零基础我建议操作系统直接用 CentOS 7 或者 Rocky Linux 8教程多踩坑后容易搜到结果。Ubuntu 也可以但配置方式略有差异初学者别把精力耗在系统差异上。版本搭配上Hadoop 3.3.x 配 Java 8 是最稳妥的组合。JDK 8 是老而弥坚的版本Hadoop 框架本身没有用太多 Java 11 的新特性使用 JDK 11 反而可能出现一些兼容性警告。记住学习阶段选择版本组合稳定比追新更重要。安装 JDK 的过程我就不再展开说了只强调一点装完一定要确认java -version能用并且设置 JAVA_HOME。很多初学者在 Hadoop 配置里写了一大堆参数结果 Hadoop 启动脚本找不到 Java报错信息还很绕——其实查一下 java 命令是否全局可用一分钟就能排除。2.2 固定 IP、hosts 和主机名一个都不能少给三台机器分别执行修改主机名的操作例如hostnamectl set-hostname node01然后编辑 /etc/hosts三台机器都要加上所有节点的解析。这里有个坑有些教程让你把 hosts 里原来的主机名映射删掉如果你不删有些命令会解析到奇怪的名字上。实际操作中保留默认的 localhost 解析再追加三行映射即可。为什么三台机器的 hosts 必须保持一致因为 Hadoop 的进程会通过主机名互相通信。如果你只改了一台另外两台解析不到 node01启动时就会报 UnknownHost。这类错误排查起来很烦为了避免它我在三台机器上全部做同样的 hosts 配置。IP 的配置也很关键。如果你用 CentOS可以编辑/etc/sysconfig/network-scripts/ifcfg-ens33网卡名可能不同把 BOOTPROTO 改成 static然后添加 IPADDR、NETMASK、GATEWAY 三项。改完执行systemctl restart network再用ip addr验证。这一步看着不起眼却是整个集群稳定性的地基。2.3 SSH 免密登录哪些机器需要为什么必须做这个环节很多教程都让做但说得不够清楚。我把逻辑给你捋一下启动集群时你通常在 node01 上执行 start-dfs.sh脚本会通过 SSH 连到其他节点远程启动 DataNode 或 SecondaryNameNode。如果 SSH 需要输密码脚本执行过程中就会卡住集群无法自动拉起。所以免密登录其实是为了“机器取代人工输入密码”。需要配置免密的路径是node01 到 node01、node01 到 node02、node01 到 node03。注意node01 连自己也要免密否则本地节点可能会因为需要输入密码而启动失败。操作命令如下在 node01 执行ssh-keygen -t rsa -P -f ~/.ssh/id_rsa ssh-copy-id node01 ssh-copy-id node02 ssh-copy-id node03然后逐个测试ssh node02是否直接进入不需要密码。这里有一个零基础常见的报错执行 ssh-copy-id 后要求输入密码但输入又提示不正确。大多数原因是当前用户的家目录权限有问题。检查一下~目录、~/.ssh目录的权限家目录不能是 777.ssh目录建议是 700authorized_keys文件建议是 600。权限太宽松SSH 会拒绝信任。2.4 关闭防火墙和 SELinux学习环境必须做的妥协网络通信全部正常之后分布式搭建前的准备工作就剩这一件事。学习阶段的集群建议把所有节点的 firewalld 直接停掉并禁用开机自启systemctl stop firewalld systemctl disable firewalld然后临时关闭 SELinuxsetenforce 0永久关闭则编辑/etc/selinux/config把SELINUXenforcing改成SELINUXdisabled。注意要重启后才完全生效或者你也可以保持临时关闭先跑通再说。我知道有经验的工程师会强调生产环境不能这么干没错。防火墙和 SELinux 在真实生产环境中一定要按规则放行但那是安全工程师的精细活零基础阶段先把通信障碍全部清除把 Hadoop 本身跑明白更重要。等你把集群搞熟了再回来研究哪些端口需要放行——HDFS 的 9870、9820YARN 的 8088、8042这些都可以查官方文档来配置。2.5 用专用用户运行集群而不是 root这个习惯我建议一开始就养好。创建一个 Hadoop 用户useradd hadoop passwd hadoop然后把解压好的 Hadoop 安装目录和之后的数据目录都授权给这个用户。为什么要用专用用户而不是 root因为 Hadoop 的进程在 root 下运行一旦脚本里出现相对路径的错误风险会放大很多而且 HDFS 文件系统的权限管理和 Linux 用户权限是联动的用 root 时所有文件都是超管身份后面学权限控制会很困惑。Hadoop 官方文档也明确不推荐用 root 运行集群。3. 五个配置文件逐个拆解想明白再复制粘贴3.1 先定目录再谈配置Hadoop 解压后你会看到 etc/hadoop 目录下有一堆 XML 模板。这些配置的核心语义只有一个告诉各个进程“我是谁”“我的伙伴在哪”“数据往哪存”。零基础阶段至少要改五个文件core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml、workers。在改之前建议先创建一个专门的数据目录比如/data/hadoop并把 Hadoop 安装目录和数据目录分开放。为什么要分开因为 Hadoop 解压包可能在升级时被整个替换如果配置文件和真正的数据都堆在安装目录里升级迁移时会很痛苦。把 name、data、tmp 这些状态数据放外面升级时只要换安装包路径即可。修改完成后记得把数据目录授权给 hadoop 用户mkdir -p /data/hadoop/{tmp,name,data} chown -R hadoop:hadoop /data/hadoop3.2 core-site.xml 与 hdfs-site.xmlHDFS 的“总部”在哪里core-site.xml 的核心配置是fs.defaultFS它决定客户端和集群通信时默认连接哪个 NameNode。我一般写成configuration property namefs.defaultFS/name valuehdfs://node01:9000/value /property property namehadoop.tmp.dir/name value/data/hadoop/tmp/value /property /configurationhadoop.tmp.dir是很多初学者忽略的配置。不配置的话Hadoop 会默认使用系统的 /tmp 目录而 Linux 系统重启后可能自动清理 /tmp 下的文件结果就是 NameNode 的元数据“莫名其妙”丢失。这是一个典型的“默认值坑”提前用专门目录能避开。hdfs-site.xml 里要设的是数据存储路径和副本数。三台集群的配置参考如下configuration property namedfs.namenode.name.dir/name value/data/hadoop/name/value /property property namedfs.datanode.data.dir/name value/data/hadoop/data/value /property property namedfs.replication/name value2/value /property property namedfs.namenode.secondary.http-address/name valuenode02:9868/value /property /configuration这里有两个点要讲透第一dfs.replication设成 2 是因为你只有两台 DataNode副本数不能超过节点数否则文件会一直处于 under-replicated 状态看着像没备份好。如果你有 3 台 DataNode设 3 比较合理。第二SecondaryNameNode 不是 NameNode 的备份它只是定期合并编辑日志减轻 NameNode 的负担。所以它放在 node02 比较合适别让这台机器同时背太多活但也不用单独占用一整台机器。3.3 yarn-site.xml 与 mapred-site.xml让计算资源也能调度HDFS 管存储YARN 管计算。yarn-site.xml 里有两个必须的配置configuration property nameyarn.resourcemanager.hostname/name valuenode01/value /property property nameyarn.nodemanager.aux-service/name valuemapreduce_shuffle/value /property property nameyarn.nodemanager.resource.memory-mb/name value1024/value /property /configurationyarn.resourcemanager.hostname告诉 NodeManager 去哪里找 ResourceManager。yarn.nodemanager.aux-service是 MapReduce 计算框架和 YARN 通信的桥梁漏掉它提交任务时会出现“shuffle 失败”之类的错误。yarn.nodemanager.resource.memory-mb要按虚拟机实际内存来写我建议先设 1024别贪多。你给虚拟机分了 2GB如果这里写 2048系统内存就不够用NodeManager 启动后可能直接挂掉。mapred-site.xml 原本叫 mapred-site.xml.template需要先重命名再编辑。核心配置是计算框架用 YARNconfiguration property namemapreduce.framework.name/name valueyarn/value /property property namemapreduce.application.classpath/name value$HADOOP_HOME/share/hadoop/mapreduce/*:$HADOOP_HOME/share/hadoop/mapreduce/lib/*/value /property /configuration第二个配置在 Hadoop 3.x 里很关键。新版 Hadoop 不会自动把 MapReduce 的依赖库放进 classpath如果你不配置运行 WordCount 时会报ClassNotFoundException: org.apache.hadoop.mapreduce.v2.app.MRAppMaster。这个问题我在刚开始用 Hadoop 3 的时候也遇到过看着像是程序代码的问题实际是 classpath 没设好。3.4 workers 文件和 hadoop-env.sh确认“谁出力”在 Hadoop 3.x 里slaves 文件已经改名为 workers。这个文件写的是哪些节点承担 DataNode 和 NodeManager 角色。注意不要写 NameNode 和 ResourceManager它只管“工人”node02 node03另外在 etc/hadoop 目录下找到 hadoop-env.sh在里面设置export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk如果你把 JAVA_HOME 写到了 /etc/profile 里这里还是要再写一次。因为 Hadoop 启动脚本是在独立环境中执行的未必会加载 /etc/profile这是一个很隐蔽的坑。直接在 hadoop-env.sh 里写死比依赖系统环境变量更可靠。4. 启动过程一步一步走格式化、启动、验证必须按顺序来4.1 格式化 NameNode 是唯一一次性的操作所有配置都完成后第一次启动前必须执行格式化命令。本质上是在 NameNode 的目录里生成一个集群身份标识文件包含 namespaceID、clusterID 等信息。执行方式hdfs namenode -format看到输出中多次出现successfully formatted就说明成功了。格式化完成后检查/data/hadoop/name/current目录会有一个 VERSION 文件里面就是集群的身份证。DataNode 启动时会拿着自己的 clusterID 找 NameNode 核对对不上就会被拒绝注册。这是我特别想强调的一点这个命令是有破坏性的不要随手反复执行。如果你集群已经跑了一段时间因为某种原因想重新格式化必须先搞清楚后果。直接再执行一次格式化会让 NameNode 生成一个新的 clusterID而老 DataNode 上还保留着旧 clusterID两边对不上DataNode 会报错甚至退出。4.2 后台脚本启动与 jps 检查启动集群的操作在 node01 上执行。新版 Hadoop 有两个脚本分工明确start-dfs.sh start-yarn.shstart-dfs.sh 会按 workers 文件里的列表远程启动 DataNodestart-yarn.sh 会启动 ResourceManager 和所有 NodeManager。跑完后在每台机器上执行jps看进程。正常的输出是这样的。node01 上12345 NameNode 12346 ResourceManagernode02 上23456 DataNode 23457 NodeManagernode03 上34567 DataNode 34568 NodeManager如果你发现 node02 和 node03 上没有任何 Java 进程先别急着怀疑脚本回到第 2 章检查 SSH 免密是否真的配通。启动脚本是远程执行的免密不通远端进程就起不来。这类问题占初学者启动失败原因的一半。停止集群则用对应的 stop-dfs.sh 和 stop-yarn.sh。这里有个习惯要养成停止的顺序无所谓但启动时优先启动 HDFS再启动 YARN因为 MapReduce 作业依赖 HDFS 存储数据。4.3 浏览器查看 9870 和 8088 端口启动完成后打开浏览器访问 http://node01:9870能进入 NameNode 的 Web 界面。这个页面会显示集群的健康状态包括 Live Nodes、Dead Nodes、存储容量、文件块数量。注意如果你在 windows 本机访问虚拟机里的集群需要在本机的 hosts 文件里加上 node01 的 IP 映射或者直接用 IP 访问。直接输入 IP 比较省事比如 http://192.168.56.10:9870。再访问 http://node01:8088这是 YARN 的资源调度界面能看到当前集群有多少活跃的 NodeManager。DataNode 的 Web 端口默认是 9864可以从 NameNode 界面里点进去看某台 DataNode 的存储情况。实际操作中最重要的验证点就是9870 页面显示 Live Nodes 是 28088 页面显示 Active Nodes 是 2。4.4 跑一个 WordCount 验证整个链路光看进程还不够必须提交一个真实作业跑完一遍才能证明从客户端到 NameNode、DataNode、ResourceManager、NodeManager 的整个链路都是通的。最简单也最经典的是 WordCount。先创建一些测试文件例如本地/home/hadoop/words.txt里面写几行英文单词。把文件放进 HDFShdfs dfs -mkdir -p /wordcount/input hdfs dfs -put words.txt /wordcount/input然后把目录权限确保可读再运行 Hadoop 自带的示例 jarhadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /wordcount/input /wordcount/output运行过程中会打印一堆进度信息最后出现Job completed successfully就大功告成。查看结果hdfs dfs -cat /wordcount/output/part-r-00000这里有一个坑输出目录不能提前存在否则作业一提交就报org.apache.hadoop.mapred.FileAlreadyExistsException。所以运行第二次之前记得先hdfs dfs -rm -r /wordcount/output我第一次跑 WordCount 时就是忘了删输出目录反复改代码没用最后才发现是目录冲突。5. 集群起不来的真实排查记录5.1 最常见的 Connection refused 错误先从最“玄学”的问题说起进程明明都启动了但客户端执行命令时总是报Connection refused。这一般不是防火墙就是地址配错了。你仔细看报错信息里的 IP 和端口如果它尝试连接的是某个你没配置过的地址十有八九是 fs.defaultFS 写错了主机名或者 hosts 文件没有同步。排查思路是这样的在客户端机器上ping node01看能否解析并连通。在客户端机器上telnet node01 9000看端口是否通。再去 NameNode 所在机器上执行netstat -tlnp | grep 9000确认进程真的在监听。这三个步骤能定位 80% 的连接问题。记住一个原则先确认网络通再确认端口通最后才怀疑配置文件。5.2 DataNode 一个都不显示的真相有时候 jps 显示 DataNode 进程在但 Web 界面的 Live Nodes 却是 0或者一开始是 2过几分钟变成 0。这种情况不用猜直接看 DataNode 日志。日志位置一般在$HADOOP_HOME/logs/hadoop-hadoop-datanode-node02.log。我在实际检查中看到最多的报错是java.io.IOException: Inconsistent clusterID位置在 NameNode 和 DataNode 的 VERSION 文件里。处理办法有两个方向如果集群刚搭建、还没有正式数据我建议直接在所有节点上删掉 name 和 data 目录重新格式化如果集群里已经有数据那就把 DataNode 的 clusterID 手动改成和 NameNode 一致或者把相关目录移走后重新启动再观察。这也解释了为什么我会反复提醒格式化要慎重。你自己算一下如果集群里存了几个 T 的数据你一个格式化下去数据副本全部失效到时候就不是重启一次的问题了。5.3 安全模式下的“假死”现象启动完 NameNode 后Web 页面可能会显示Safe mode is ON。安全模式是 NameNode 启动过程中的保护机制它会限制对文件系统的写操作等 DataNode 上报足够多的块位置信息后自动退出。刚启动的集群进入安全模式是正常的一般几十秒内退出。但如果你启动很久后仍然在安全模式常见原因是 DataNode 一直没有成功注册导致 NameNode 等不到足够的块上报信息。这时执行hdfs dfsadmin -safemode leave强制退出安全模式可以应急但你要知道根本问题还是报错信息继续翻日志找原因。如果是刚搭建的测试环境我更倾向于把 DataNode 的旧数据目录清掉让它重新注册而不是长期依赖跳过安全模式。5.4 关于重新格式化我的最终建议重新格式化是一个“看起来简单、实则危险”的操作。如果你已经决定要彻底重来正确流程是执行 stop-dfs.sh 和 stop-yarn.sh 停止所有进程。在 node01 上删除/data/hadoop/name和/data/hadoop/tmp。在 node02、node03 上删除/data/hadoop/data和/data/hadoop/tmp。在 node01 上重新执行 hdfs namenode -format。重新执行 start-dfs.sh 和 start-yarn.sh。不要在 DataNode 上删除整个/data/hadoop因为它的 data 目录在重新格式化之前不能留旧 clusterID。不要偷懒只删 NameNode 那台机器数据节点上的旧身份不同步消除你会重蹈 clusterID 不一致的覆辙。6. 额外提醒一些我自己踩过的小坑6.1 复制虚拟机会带来哪些隐藏问题很多零基础读者为了省事喜欢直接复制虚拟机。无脑复制确实快但会产生几个典型问题。一是主机名和 IP 完全一样集群认为它们是同一台机器二是 Machine ID 一样SSH 可能拒绝连接三是某些版本的系统复制后网卡 MAC 地址一样网络起不来。如果你必须用复制的办法至少要处理三件事修改 /etc/hostname、修改 /etc/hosts、删除/etc/machine-id后重启让它重新生成。另外确认一下网卡配置文件里的 HWADDR 是否和实际网卡一致。这个过程比手动装一台新的还麻烦所以我的建议是第一台认真装好之后用“虚拟机模板”的方式克隆然后逐项修改比复制已配置好的机器要干净得多。6.2 YARN 内存设置不当导致作业被秒杀提交 WordCount 后如果作业日志显示 Container 被 kill且报错信息里有unreasonable或者exceeds physical memory之类的字样那就是内存设置的问题。YARN 给每个容器分配的内存不能超过 NodeManager 宣称的总内存。这里有个常见操作失误在虚拟机只给了 2GB 内存的情况下把 yarn.nodemanager.resource.memory-mb 设成 2048系统再扣掉一些缓存NodeManager 一启动就濒临崩溃。我的实际处理方案是虚拟机统一 2GBYARN 的 memory 配 1024同时不要额外开太多占用内存的应用。等集群跑通之后你想深入调优再逐步把内存调大。别一上来就想着“配大一点跑得快”分布式调度资源是动态的配置过大只会换来秒死。6.3 集群的日常启动顺序与数据安全习惯集群搭建完成后你会反复开机和关机。我建议你形成一套固定的操作顺序。开机后按这个顺序执行start-dfs.sh start-yarn.sh jps关机前按这个顺序执行stop-yarn.sh stop-dfs.sh在 Linux 环境里关机前不执行 stop 脚本虽然 JVM 进程会随着系统关闭而终止但 HDFS 可能没来得及把内存中的元数据刷盘下次启动时 NameNode 会耗时很久做元数据恢复。如果系统非正常关机次数多元数据损坏的概率会上升。一个良好的数据安全习惯是尽量正常关机并在每次关机前先停集群。最后再分享一条经验全分布式的搭建和伪装分布式最大的不同是你必须理解“身份关系”。节点间靠主机名和 clusterID 互相确认身份配置就是双方的身份证明。你如果能画出“哪台机器跑什么进程、通过什么地址连谁、数据和元数据分别存哪里”这张图那这个集群基本就吃透了。以后无论是学 HDFS 高可用、YARN 调优还是整合 Zookeeper都是在今天这张图上添加新角色而已。