Ubuntu 16.04搭建Hadoop真分布式集群:网络配置与故障排查实战
先说个也许有点扫兴的结论在 Ubuntu 16.04 上搭 Hadoop 真分布80% 的时间根本不是花在改配置文件上而是花在和网络、用户权限、集群 ID 这几个看似不起眼的基础问题上。我在很多课程实验和项目里见过同一类情况——伪分布式跑得挺顺真分布一连到第二台机器就各种起不来。原因倒不是 Hadoop 配置有多难而是搭建者被“单机环境”惯坏了忽略了分布式集群在系统层、网络层、用户层的很多前提条件。这篇文章就以 Ubuntu 16.04 为例用 3 台虚拟机完整走一遍 Hadoop 真分布式集群的搭建流程。从集群规划、网络配置、JDK 与 Hadoop 版本选择到 core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml 这几个核心配置文件的逐行拆解再到格式化 NameNode、启动集群、跑 WordCount 验证最后整理几个真实环境里最常见的故障和排查思路。无论你是正在做课程实验还是第一次接触真实的多节点环境按这个顺序走下来能少踩很多坑。1. 三节点集群规划与虚拟机系统初始化1.1 为什么“真分布”至少要三台机器很多人搭 Hadoop 真分布时经常纠结一个问题能不能用两台机器理论上当然可以——一台 NameNode一台 DataNodeHDFS 也能跑起来。但我强烈建议至少三台也就是 1 个 Master 节点加 2 个 Slave 节点。原因很直接Hadoop 的默认副本数是 3虽然集群只有 2 个 DataNode 时 HDFS 会自动把副本数降级为 2不会启动失败但这会引出两个麻烦。第一个麻烦是验证价值变低。真分布和伪分布最本质的区别是数据真正分散到了多台机器上文件块会在不同节点之间复制MapReduce 任务也会真正在不同节点上并行执行。只有 2 个 DataNode 时很多“分布式”行为虽然能看到但不直观。我曾经在一台 2 节点的集群上执行一个统计任务由于节点太少任务调度结果和伪分布几乎没差别最后排查起来反而更混乱。第二个麻烦是故障场景不好模拟。学习真分布的过程中一定会遇到 DataNode 挂掉、网络中断、某台机器负载过高等情况。3 个节点可以让你从容地测试“少一个副本怎么办”“节点下线后 NameNode 怎么处理”这些才是分布式系统的核心知识。所以如果只是为了做实验而不是追求极限硬件开销1 个 Master 加 2 个 Slave 是最低配置也是一个比较合理的起步配置。1.2 虚拟机资源配置与 Ubuntu 16.04 初始化动作我用的是桌面版 VMware 或 VirtualBox 创建的三台 Ubuntu 16.04 虚拟机。每台虚拟机分配 2 核 CPU、2GB 内存、20GB 磁盘这个配置跑小型实验足够。如果你的宿主机内存比较紧张Master 节点至少保证 2GB两个 Slave 节点可以降到 1.5GB。其实这个组合可以用表格来理解节点角色主机名建议最低配置核心功能Mastermaster2 核 / 2GBNameNode ResourceManager SecondaryNameNodeSlave 1slave12 核 / 2GBDataNode NodeManagerSlave 2slave22 核 / 2GBDataNode NodeManager安装 Ubuntu 16.04 时有几个细节需要注意。第一系统安装完成后第一件事就是修改主机名分别改成 master、slave1、slave2。Ubuntu 16.04 可以直接用 hostnamectl 来改执行 hostnamectl set-hostname master 然后重启或者编辑 /etc/hostname 文件后重启。第二建议关闭图形界面如果桌面版用不到的话命令行模式省下不少资源。第三更新一下系统源这里推荐换成比较稳定的公共镜像源然后执行 apt update 和 apt upgrade。Ubuntu 16.04 官方源现在已经不维护了使用合适的镜像源可以避免安装软件时拉不到包。1.3 创建专用用户与统一目录做实验时很多人图方便直接用 root 用户来搭 Hadoop我理解这种心情但非常不建议。Hadoop 的启动脚本里有很多路径权限检查root 运行容易掩盖一些权限问题而且日志文件、数据目录会全部挂在 root 名下后面调整配置、排查问题都比较别扭。更稳妥的做法是创建一个专用用户。在三台机器上都执行一样的命令sudo useradd -m -s /bin/bash hadoop sudo passwd hadoop然后给 hadoop 用户配置 sudo 权限甚至在某些教程中会直接把 root 的 bash 配置文件复制过来。我建议在 /etc/sudoers 里给 hadoop 添加 NOPASSWD 权限这样后面遇到需要管理员权限的操作时能少一些交互。另外还要统一目录结构sudo mkdir -p /usr/local/hadoop sudo chown -R hadoop:hadoop /usr/local/hadoop为什么强调“统一目录”因为分布式集群中每台机器不一定完全同构但大数据组件的安装路径最好保持一致。比如所有节点都把 Hadoop 装在 /usr/local/hadoop这样脚本里的路径、配置里的路径、启动时的环境变量都不需要区分节点管理起来省很多事。我见过有人在 Master 上装到 /opt/hadoop在 Slave 上装到 /home/hadoop/hadoop结果跑起来之后 datanode 日志里全是路径报错一条一条去对路径非常浪费时间。2. 网络配置、hosts 映射与 SSH 免密登录2.1 固定 IP 配置方法与 hosts 文件的大坑真分布和伪分布最大区别就在网络层。所有节点必须能通过主机名互相访问而主机名到 IP 的映射关系需要提前固定好。虚拟机如果使用 DHCP 自动获取 IP一旦宿主机重启、虚拟网络重新分配IP 变了整个集群就找不到队友了。所以第一步就是给三台机器配置固定 IP。Ubuntu 16.04 还是使用 /etc/network/interfaces 文件来配置网卡。假设网卡名称是 ens33实际可用 ip addr 查看配置示例如下auto ens33 iface ens33 inet static address 192.168.56.10 netmask 255.255.255.0 gateway 192.168.56.1 dns-nameservers 114.114.114.114然后执行 sudo systemctl restart networking 使其生效。注意这里我推荐使用 VMnet比如 192.168.56.x 网段这类仅主机的网络模式这样三台虚拟机之间互相访问非常稳定也不依赖外部网络。如果你的实验环境是实验室局域网那就按实际网段分配地址。接下来是 /etc/hosts 文件的配置这是真分布里最容易翻车的环节。三台机器上的 /etc/hosts 都要加上这样的映射192.168.56.10 master 192.168.56.11 slave1 192.168.56.12 slave2这里必须特别提醒一个经典错误在 /etc/hosts 里把 master 映射成 127.0.0.1 或者保留默认的 127.0.0.1 localhost 那行里加了一个 master。一旦 NameNode 绑定到 127.0.0.1外部节点的 DataNode 根本无法连上它而本地启动的 Web UI、IPC 服务也只会监听回环地址。我自己遇到过一次DataNode 一直注册不上去日志里反复出现“Could not connect to master:9000”最后发现 hosts 文件里写的是 127.0.0.1 master。改成实际 IP 后问题立刻消失。所以在配置 hosts 时localhost 就只对应 127.0.0.1其他主机名一律对应实际局域网 IP。2.2 SSH 免密登录的配置顺序Hadoop 的启动脚本 start-dfs.sh 和 start-yarn.sh 都需要通过 SSH 远程执行命令把 DataNode、NodeManager 等进程“分发”到各个节点。所以 Master 到所有节点包括自己要配置 SSH 免密登录。很多人只给 Master 配了到 slave1、slave2 的免密却漏了 Master 到自己最后启动时卡在 name node 连接过一次就崩了。我习惯的配置步骤是这样三台机器上最好都生成好密钥ssh-keygen -t rsa -P -f ~/.ssh/id_rsa这条命令会在 ~/.ssh 下生成密钥对-P 表示空 passphrase避免每次 SSH 都要求输入密码。然后在 master 节点上把公钥拷贝到所有机器ssh-copy-id hadoopmaster ssh-copy-id hadoopslave1 ssh-copy-id hadoopslave2注意这里的 ssh-copy-id 会自动将公钥追加到目标机器的 ~/.ssh/authorized_keys 文件里。但我更建议在两台 Slave 上也执行一次 ssh-keygen 和 ssh-copy-id做到所有节点之间两两互信。为什么要做两两互信因为 DataNode 之间在数据块 pipeline 复制时可能也需要互相连接虽然不强制免密但提前做了能避免一些奇怪问题。验证免密是否生效执行ssh master hostname ssh slave1 hostname ssh slave2 hostname如果直接返回对应的主机名而不要求密码说明配置成功。这里有个小技巧值得记住在第一次执行 ssh-copy-id 时如果目标机器没有 .ssh 目录Ubuntu 会自动创建但有时候权限不对会把 known_hosts 和 authorized_keys 的权限搞乱。如果后面发现 SSH 偶尔要输密码检查一下目标机器上 ~/.ssh 目录权限是不是 700authorized_keys 权限是不是 600。权限过宽时 SSH 默认会拒绝加载公钥。3. JDK 与 Hadoop 版本选择3.1 OpenJDK 8 安装细节Hadoop 3.x 官方明确要求 Java 8 或 Java 11而 Ubuntu 16.04 的软件源里刚好长期保留着 OpenJDK 8这个组合是最省心的。直接安装sudo apt install -y openjdk-8-jdk安装完成后检查版本java -version你会看到 “openjdk version ‘1.8.0_xxx’”。这里有个坑Ubuntu 16.04 上默认可能已经装了 OpenJDK 9 或更新版本或者你之前为了其他项目安装了 JDK 11导致 java 命令指向了错误版本。用下面的命令可以手动切换默认版本sudo update-alternatives --config java在列表里选择 /usr/lib/jvm/java-8-openjdk-amd64/bin/java 对应的序号即可。为什么这么在意 JDK 版本因为 Hadoop 对 Java 版本非常敏感JDK 版本过高会出现 GC 参数不识别、JNI 调用异常等问题版本过低则直接起不来。而且一旦你之后打算编译 Hadoop 源码或者使用 SparkJava 8 的兼容性也是最好的几乎大数据全家桶都能跑。另外要记住 JAVA_HOME 环境变量。Hadoop 的启动脚本在调用 JVM 时依赖 JAVA_HOME。在 /etc/profile.d/ 下创建一个脚本比较干净echo export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 | sudo tee /etc/profile.d/java.sh echo export PATH$JAVA_HOME/bin:$PATH | sudo tee -a /etc/profile.d/java.sh source /etc/profile.d/java.sh注意 JAVA_HOME 的路径千万别写成 /usr/lib/jvm/java-8-openjdk 这种不带版本细节的路径符号链接在某些环境下会导致 Hadoop 脚本判断出错。直接写实际路径最稳妥。3.2 Hadoop 版本选择与单节点包分发Ubuntu 16.04 是个相对较老的操作系统搭配 Hadoop 版本时我推荐 3.1.x 或者 3.2.x。太老的 2.x 虽然也能用但端口配置、Web UI 地址和运维习惯与主流版本差距很大学习价值偏低太新的 3.4.x / 3.5.x 又会因为引入了一些新特性比如 EC、Router 等配置复杂度上升且对系统库要求更高。从官网下载二进制包比如 hadoop-3.1.3.tar.gz。下载完成后在 master 节点上解压并移动到 /usr/local/hadoopsudo tar -zxvf hadoop-3.1.3.tar.gz -C /usr/local/ sudo mv /usr/local/hadoop-3.1.3 /usr/local/hadoop sudo chown -R hadoop:hadoop /usr/local/hadoop然后分发包到 Slave 节点。这里有几种思路第一种是直接在每台机器上下载解压第二种是在 master 上配置好之后用 scp 把整个目录同步到 slave1、slave2。我推荐第二种原因很简单真分布集群要求核心配置保持一致在 master 上改好配置文件后同步过去能避免手动在每台机器上重复修改而造成的低级错误。scp -r /usr/local/hadoop hadoopslave1:/usr/local/ scp -r /usr/local/hadoop hadoopslave2:/usr/local/不过此时先不用同步等配置文件改完再同步否则白白重复传输。另外在每台机器上都要把 /usr/local/hadoop/etc/hadoop/hadoop-env.sh 里的 JAVA_HOME 设置好。如果你是一路 scp 同步过去的那只要 master 上配好即可。但如果你打算在每台机器上分别配置就很容易出现一台机器 JAVA_HOME 写错启动时那台机器上的 NodeManager 直接挂掉的情况日志里一般会显示 “Error: JAVA_HOME is not set and could not be found”。4. 核心配置文件逐行拆解4.1 环境变量文件hadoop-env.sh所有配置文件都位于 /usr/local/hadoop/etc/hadoop/ 目录下。第一个必须改的就是 hadoop-env.sh。Hadoop 在启动脚本中会加载这个文件用来确定 JVM 路径。即使你已经在系统层面配置了 JAVA_HOME这里最好也显式写一遍因为不少发行版脚本并不会自动继承登录 shell 里的环境变量。在该文件里找到 JAVR_HOME 或 JAVA_HOME 字样改成export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64如果你还设置了 HADOOP_HOME 和 PATH可以顺手在 /etc/profile.d/hadoop.sh 里加上export HADOOP_HOME/usr/local/hadoop export PATH$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH这样后续执行 hdfs、yarn、hadoop 命令时就不用写全路径了。顺便说一句hadoop-env.sh 里还可配置 HADOOP_PID_DIR、HADOOP_LOG_DIR建议不要改到 /tmp 下因为 /tmp 可能在系统重启时被清理也会被其他用户干扰。4.2 四个 XML 文件的参数逐项说明接下来是决定集群命运的几个 XML 文件。配置文件改起来不难难的是知道每个参数为什么要这么写。先看 core-site.xmlconfiguration property namefs.defaultFS/name valuehdfs://master:9000/value /property property namehadoop.tmp.dir/name value/usr/local/hadoop/tmp/value /property /configurationfs.defaultFS 定义了整个集群的默认文件系统地址。hdfs://master:9000 表示所有不认识具体文件系统的路径操作都会路由到 master:9000 这个 NameNode 的 RPC 端口。你可能会想为什么不写成 IP写得主机名反而更好——这样换 IP 时不用改所有客户端的配置只要改 hosts 文件。hadoop.tmp.dir 是 NameNode 和 DataNode 存放临时文件的基础目录一定要指向一个磁盘空间充足、权限正确的位置并确保目录存在。hdfs-site.xml 主要控制 HDFS 的存储行为configuration property namedfs.replication/name value2/value /property property namedfs.namenode.name.dir/name valuefile:///usr/local/hadoop/dfs/name/value /property property namedfs.datanode.data.dir/name valuefile:///usr/local/hadoop/dfs/data/value /property /configurationdfs.replication 决定每个数据块保留多少副本。在 3 节点集群里把副本数设为 2 是合理的既满足最少一份副本的要求又能接受一台 DataNode 挂掉后仍然不丢数据。如果设置为 3也可以正常运作只是磁盘使用率更高。对于实验环境我建议保留 2既能看到数据块在多个节点的实际分布又不会让磁盘空间消耗过快。dfs.namenode.name.dir 是 NameNode 元数据存储路径必须放在本地文件系统不要放在 NFS 等共享存储上否则元数据并发写入会有严重性能问题。dfs.datanode.data.dir 是 DataNode 实际存储数据块的路径多个目录可以用逗号分隔Hadoop 会自动轮询选择目录写入。yarn-site.xml 负责 YARN 资源调度configuration property nameyarn.resourcemanager.hostname/name valuemaster/value /property property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.nodemanager.aux-services.mapreduce_shuffle.class/name valueorg.apache.hadoop.mapred.ShuffleHandler/value /property /configurationyarn.resourcemanager.hostname 直接告诉 NodeManager 应该向哪个主机上的 ResourceManager 注册。aux-services 是 YARN 的重要机制NodeManager 需要加载一些辅助服务来处理 MapReduce 阶段的数据洗牌其中最核心的就是 mapreduce_shuffle。对于 Hadoop 3.xaux-services.class 实际上可以省略很多版本会自动加载不过显式写上能避免意外。最后是 mapred-site.xml注意这个文件名不像其他几个一开始就存在需要从模板复制cp mapred-site.xml.template mapred-site.xml然后写入configuration property namemapreduce.framework.name/name valueyarn/value /property /configuration这个配置决定 MapReduce 作业的运行框架。曾经默认值是 classic在 Hadoop 内部模拟调度必须改成 yarn 才能把任务交给 YARN 的 ResourceManager 去调度。忘了这一步你即使再正确启动 HDFS跑 MapReduce 任务时也会卡死在 phase 上或者干脆报 “Retrying connect to server” 之类的错误。4.3 节点名单文件slaves 还是 workersHadoop 3.x 里叫 workersHadoop 2.x 里叫 slaves。我用的是 3.1.3所以 /usr/local/hadoop/etc/hadoop/workers 文件中每一行写一个 DataNode 主机名master slave1 slave2这里有个容易被忽视的点master 节点可不可以同时作为 DataNode当然可以。虽然工业环境通常不这么干但实验环境里多一个 DataNode 就能多一份存储和计算资源。我建议在 workers 里把 master 也写上这样启动脚本会同时在 master 上启动 DataNode 进程你的 3 台机器都是完整的存储和计算节点。不要把注释写到这个文件里也不要混入多余的空格避免脚本解析出错。在 3.x 中如果文件里出现不存在的节点名启动脚本会 SSH 失败并在终端报错整个启动流程看起来就像卡死了一样。所以我在启动前都会用cat -A workers检查一下是否有多余的换行符号或空格。配置文件全部改完后回到 master 上把整个 Hadoop 目录同步到 slave1、slave2scp -r /usr/local/hadoop hadoopslave1:/usr/local/ scp -r /usr/local/hadoop hadoopslave2:/usr/local/ sudo chown -R hadoop:hadoop /usr/local/hadoop注意顺序先 master 改好再同步。同步后 slave 上的文件权限必须以 hadoop 用户所有否则启动时也会被权限检查卡住。另外三台机器的 /etc/hosts 都要保持一致这个在前面已经强调过了。5. 格式化 NameNode 与完整启动链路5.1 首次格式化的正确时机格式化 NameNode 的操作必须在所有配置同步完成、HDFS 目录结构已经创建之后才能做。在 master 上执行mkdir -p /usr/local/hadoop/dfs/name mkdir -p /usr/local/hadoop/dfs/data mkdir -p /usr/local/hadoop/tmp chmod -R 755 /usr/local/hadoop/dfs然后执行hdfs namenode -format看到输出“Successfully formatted”才算成功。很多人容易反复格式化第一次启动失败马上重新 format第二次启动又失败再 format…… 这样最直接的后果是 NameNode 重新生成了集群 IDclusterID而 DataNode 还保留着上次的集群 ID启动时 DataNode 会因为集群 ID 不一致而拒绝注册。到时候你在日志里会看到典型的 “Incompatible clusterIDs” 报错。所以我的建议是在确认配置没有问题之前不要轻易格式化一旦格式化成功就把这个时间点之后的配置变更谨慎化。如果实在需要重新来请在所有节点统一删除 dfs/data、dfs/name、tmp 三个目录后再格式化并且是 master 和 slave 一起清理保证回到一致状态。需要说一下的是格式化只需要在 master 上执行一次。DataNode 节点上的数据目录可以保持为空首次启动时它会自动创建集群 ID 并和 NameNode 对齐。5.2 启动顺序与进程分布启动集群时建议分两步执行不建议用 start-all.sh 一把梭。分开启动能分别观察 HDFS 和 YARN 的日志如果出错排查范围会小很多。在 master 上执行start-dfs.sh start-yarn.shstart-dfs.sh 会依次启动 NameNodemaster、SecondaryNameNodemaster、DataNode所有节点。start-yarn.sh 会启动 ResourceManagermaster和 NodeManager所有节点。启动过程中因为脚本内部通过 SSH 远程到各节点执行命令第一次启动时如果 SSH 仍然询问密码脚本会明显卡住。所以前面强调的免密登录此时会体现出价值。如果某台节点启动失败立即去该节点查看 logs 目录下的相关日志。我在实际操作中总结了一个顺序先看 master 上有没有 NameNode 进程再看 slave1 上有没有 DataNode 进程最后再检查 YARN。HDFS 和 YARN 是两层独立体系HDFS 起不来 YARN 大概率也白搭。启动完成之后在所有节点执行 jps 查看 Java 进程。健康的进程分布如下表节点应出现的进程masterNameNode, SecondaryNameNode, ResourceManager, DataNode, NodeManagerslave1DataNode, NodeManagerslave2DataNode, NodeManager如果 master 上少了 SecondaryNameNode多半是 hdfs-site.xml 里没有配置或者配置路径不匹配如果 DataNode 没有重点查日志如果日志都正常但仍没有进程那就可能是 SSH 或 hosts 的问题。5.3 网页验证与第一个 MapReduce 任务进程都正常后不要急着跑任务先用网页检查集群状态。Hadoop 3.x 的 NameNode Web UI 默认端口是 9870Hadoop 2.x 是 50070。在浏览器里访问http://master:9870如果能打开并看到 Alive Nodes 数量为 3说明 HDFS 层正常。再访问 YARN 资源管理器页面http://master:8088能看到 3 个 Active Node 列表说明 YARN 层正常。这里有个现实问题浏览器在宿主机上master 是一个虚拟机宿主机可能无法直接解析 master 这个主机名。最简单的办法是修改宿主机的 hosts 文件把 master、slave1、slave2 映射到对应虚拟机 IP这样在浏览器里直接用主机名访问。如果你用的是仅主机网络宿主机和虚拟机互相 ping 通的情况下是可以这样玩的。网页正常之后再跑一个 WordCount 验证真分布。先准备输入文件hdfs dfs -mkdir -p /user/hadoop/input hdfs dfs -put /etc/hosts /user/hadoop/input/把系统的 /etc/hosts 文件上传到 HDFS 作为测试数据然后执行hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount /user/hadoop/input /user/hadoop/output任务运行结束后查看输出hdfs dfs -cat /user/hadoop/output/part-r-00000在 YARN 页面上你应该能观察到多个 Container 在 slave1、slave2 上被调度执行的过程并且看到统计结果是所有节点共同计算出来的。这一步跑通你的真分布集群就算正式可用了。6. 真分布搭建中最常见的五个故障6.1 DataNode 进程启动后立即消失集群 ID 冲突这是我见过最多的问题症状也很典型执行 start-dfs.sh 后slave 节点上 DataNode 进程存在几秒就退出用 jps 看不到 DataNode。去 slave 节点的日志目录看 hadoop-hadoop-datanode- .log末尾通常会出现类似 “Incompatible clusterIDs” 的报错。原因就是 NameNode 和 DataNode 的集群 ID 不一致通常是反复格式化 NameNode 或者拷贝了带旧数据的 Hadoop 目录导致的。解决办法是统一清理数据目录再重新启动在三台机器上同时删除 /usr/local/hadoop/dfs/data、/usr/local/hadoop/dfs/name、/usr/local/hadoop/tmp 这三个目录然后只在 master 上重新执行 hdfs namenode -format最后再启动集群。注意是所有节点一起删不是只删 master。6.2 NameNode 一直处于 SafeMode大量数据不够副本数启动完成之后通过网页看到 “Safe mode is ON”并且 DataNode 列表数量正常但 NameNode 迟迟不自动退出安全模式。这是因为 HDFS 在启动时会检查每个数据块的副本数是否达到 dfs.replication 配置的值如果大量数据块只有 1 个副本就认为集群不够安全继续等待复制完成。对小型实验集群可以手动退出安全模式hdfs dfsadmin -safemode leave但如果数据块真的严重少于副本要求即使手动退出也会很快重新进入。更好的做法是检查是否有 DataNode 没注册上来。如果只有一个节点在线而副本数配置为 2NameNode 会一直处于等待状态。这时把 dfs.replication 临时改成 1 重启集群可以解决但实验结束后还是建议调回 2因为你搭建真分布的目的之一就是感受副本机制。6.3 Web UI 显示可用数据节点为 0但进程和端口都正常有一种情况非常隐蔽所有进程都正常jps 也正常但从 NameNode 网页里看到的 DataNode 列表是空的。我排查过几次后发现root 原因基本出在 /etc/hosts 上。如果 slave 节点的 hosts 文件把 master 指向了 127.0.0.1那么 slave 上的 DataNode 尝试连接 NameNode 时走的是回环地址NameNode 虽然收到了请求却无法正确识别这个节点的外部地址注册关系就断断续续网页上自然看不到。解决办法就是严格执行 2.1 节的做法三台机器的 /etc/hosts 都只保留 master、slave1、slave2 与实际局域网 IP 的映射同时确保主机名不是 localhost 的别名。修改 hosts 文件后最好在所有节点重启网络服务或者干脆重启虚拟机否则旧连接还可能挂在内存里让你误以为改完了还是老毛病。6.4 端口冲突与进程残留导致重复启动失败开发机的环境可能不是完全干净的比如你之前跑过伪分布、停过一次集群但没有真正清理进程或者 ZooKeeper、Spark 等别的服务也占用了常用端口。Hadoop 3.x 使用的关键端口有 9000NameNode RPC、9870NameNode HTTP、9866DataNode、8088ResourceManager HTTP等。如果你发现启动脚本反复提示 “Address already in use” 或者服务一直处于节点状态先检查端口ss -lntp | grep 8088 ss -lntp | grep 9000如果有残留进程找到 PID 后用 kill -9 清理再重启对应服务。我还要提醒一点不要直接用 kill -9 杀 NameNode 进程否则元数据可能没落盘导致重启后要花大量时间做 journal 回放。更稳妥的是用 hdfs --daemon stop namenode 或 yarn --daemon stop resourcemanager 这类内置停止命令实在僵死再考虑强杀。6.5 系统时间不同步导致 RPC 握手失败真分布在生产上通常要求各节点时间一致实验环境也不该忽略。如果集群的某台虚拟机时间明显快几分钟Hadoop RPC 层在做 Kerberos 或 SASL 握手时很容易失败日志里会出现 “Authentication failed” 或者 “Clock skew” 相关的字样。不过不用紧张这个故障一般在三台虚拟机刚克隆出来、宿主机经常休眠之后再启动时特别容易出现。最简单的解决办法是在三台机器上都安装 ntpdate然后统一校准系统时间。Ubuntu 16.04 上可以通过 apt install ntp 安装 NTP 服务来自动同步。校准完时间之后最好重启一次集群因为已经失败的 RPC 连接不会自己恢复。我在一次帮朋友排查集群时发现 master 的时间快了 5 分钟所有从节点注册都不成功而当时所有配置文件和进程状态看起来都是完全正常的那个问题排查了整整一个下午。真分布的搭建过程就是这样每一步都不难但每一步都和其他步骤咬合得很紧。网络配置错了后面的 SSH 和启动全受影响版本选错了格式化和启动时冒出各种奇奇怪怪的错误数据目录重复使用又会产生集群 ID 冲突。把这些基础层面的坑摸清之后再回头看那些配置文件你就能明白每一个参数出现的意义。最后再分享一个小技巧第一次搭建时不要追求一次成功适当制造一些故障反而能帮你快速理解整个集群的依赖关系。比如故意停掉一台 Slave 的 NodeManager再跑一次 WordCount你会在日志里直观地看到 Hadoop 如何重试、如何重新分配任务这种体验比读十篇配置教程都有用。