Hadoop集群搭建排错:JAVA_HOME、SSH互信与OBS接入避坑

发布时间:2026/10/31 7:13:56
Hadoop集群搭建排错:JAVA_HOME、SSH互信与OBS接入避坑 简介《鲲鹏云大数据实验docx》是一份面向大数据初学者、云计算实验课程学生及需要完成华为云Hadoop集群部署实践者的实验报告。文档围绕鲲鹏云环境下的完整建链过程展开涵盖ECS与OBS购买配置、AK/SK获取、节点互信与SSH免密、Hadoop安装包分发、core-site.xml等核心配置文件设置以及NameNode初始化与HDFS启动等环节。资源包仅含1个docx文件大小约2.52MB篇幅集中便于按实验阶段对照查阅。文档还保留了Java家目录配置错误、重新分发与多次尝试等排错记录能帮助读者理解云上Hadoop集群的部署流程、节点一致性维护和常见问题定位。目前已有893人学习适合作为课程实验参考和动手实践前的流程预演材料。1. JAVA_HOME 少写一个 aarch64 后缀Hadoop 集群白折腾一晚上按鲲鹏云大数据实验报告里的顺序ECS 买好、OBS 建好、AK/SK 抄下来、三个节点互信也通了最后卡在start-dfs.sh弹一句JAVA_HOME is not set and could not be found。我第一遍就是这么翻车的java -version明明能跑/etc/profile改了、source也执行了重新分发 Hadoop 包、重启节点全试过报错一个字都没变。真正的原因小得离谱——OpenJDK 在鲲鹏aarch64机器上的安装目录名带架构后缀手上那份配置抄的是别处的路径改完还不生效是因为走的是 ssh 非交互执行。这类「环境看着全对、路径差一点」的故障在集群搭建里出现频率极高新手会被它耗掉整晚熟手也常因为一次手滑重来一遍。下面按我实际踩过的顺序把 JAVA_HOME、SSH 互信、OBS 接入这几道关卡的报错、根因和修法逐个拆开。2. JAVA_HOME 报错改完 /etc/profile 为什么远程启动还是找不到2.1 报错原文拆开看三种 JAVA_HOME 报错不是一回事同样是「找不到 Java」不同脚本吐出来的字不一样指向的位置也不一样先分清再动手能省一半时间。第一种是JAVA_HOME is not set and could not be found.出自 Hadoop 的启动脚本链hadoop-config.sh被hdfs、yarn、start-dfs.sh一路 source。它的含义很明确脚本执行到这里时JAVA_HOME这个变量在当前进程环境里是空的于是它试图用一个内置的猜测逻辑去找 java找不到就退出。第二种是ERROR: JAVA_HOME is incorrectly set.这通常来自 HBase、Hive 或某些版本的守护进程脚本。注意措辞是「incorrectly」而不是「not set」——变量有值但那个路径下面bin/java不存在或者不可执行。这几乎百分百是把 x86 的路径写到了 ARM 机器上或者路径末尾多写了一个斜杠、少写了一段版本号。第三种最容易被忽略/usr/bin/env: java: No such file or directory或者干脆hdfs: command not found。前者是 PATH 里没有 java后者是 PATH 里没有$HADOOP_HOME/bin跟 JAVA_HOME 是两个层面的问题别混在一起改。我一般的判断顺序是先在报错节点上登录着敲一次echo $JAVA_HOME和java -version如果都有值那问题基本不在机器本身而在「脚本执行时的环境」——这就引出下面最关键的一节。2.2 定位 aarch64 的真路径别再抄 x86 的写法鲲鹏 ECS 是 ARM 架构用系统包管理器装出来的 OpenJDK 目录名会带架构标识典型形态长这样/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64最后那一段aarch64和前面的h5.oe1系统发行版标识都是版本相关字段换个镜像、换个小版本就可能变。所以千万别复制粘贴别人文档里的完整路径一定要在自己机器上现场查# 1. 列出所有已安装的 JVM 目录注意看带 aarch64 的那条 ls -ld /usr/lib/jvm/*/ # 2. 从 java 可执行文件反查真实安装根目录最可靠 readlink -f $(command -v java) | sed s#/bin/java$## # 3. 用查到的路径直接验证能出版本号才算数 JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64 $JAVA_HOME/bin/java -versionls -ld里的-d是关键不加它会把目录内容全列出来几十行刷屏反而看不清名字。readlink -f会一路解析软链接到最终的真实文件因为/usr/bin/java往往是个指向alternatives的软链顺着它反推根目录最不容易出错。sed那一段是把结尾的/bin/java切掉得到纯粹的家目录。顺带提醒一句如果机器上装了多个 JDK/usr/bin/java指向哪个由alternatives决定readlink -f拿到的就是当前生效的那个。想显式切换可以用alternatives --config java但在集群场景里我不建议切来切去直接把JAVA_HOME写死成一条确定的路径更省心。2.3 hadoop-env.sh 写死才是终点profile 只是起点这是整篇文章里我认为最值钱的一条经验。很多教程让你把JAVA_HOME写进/etc/profile然后source一下你照做了登录进去敲java -version也确实正常可start-dfs.sh依旧报错——因为两者执行环境根本不是同一个。Shell 读配置文件的规则大致是这样交互式登录 shell 读/etc/profile和~/.bash_profile交互式非登录 shell 读~/.bashrc而ssh 远程执行单条命令时是非交互非登录 shell两者都不读。start-dfs.sh恰恰是通过 ssh 到每个从节点去执行hadoop-daemon.sh的从节点那边启动 DataNode 的进程压根没经历过你手动登录时的那些source。所以正确做法是让配置管到「进程启动的那一刻」有两条路我通常两条都做# 方案 A首选写进 Hadoop 自己的 hadoop-env.sh不依赖任何 shell 环境 # 编辑 $HADOOP_HOME/etc/hadoop/hadoop-env.sh # 找到被注释掉的 # export JAVA_HOME... 那一行替换成真实路径并取消注释 export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64 # 顺带把日志和 pid 目录也固定下来避免默认写到 /tmp 被清理 export HADOOP_LOG_DIR/home/modules/hadoop/logs export HADOOP_PID_DIR/home/modules/hadoop/pids # 方案 B兜底放在 /etc/profile.d 下并被 bashrc 加载 cat /etc/profile.d/java.sh EOF export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64 export PATH$JAVA_HOME/bin:$PATH EOF # 让非交互 shell 也能拿到/etc/bashrc 或 ~/.bashrc 末尾加一行 grep -q profile.d/java.sh /etc/bashrc || echo . /etc/profile.d/java.sh /etc/bashrc方案 A 是决定性的因为它绕开了所有 shell 加载顺序问题hadoop-env.sh是被启动脚本显式 source 的跑不掉。方案 B 解决的是「我登录进去想直接敲 java、jps 也能用」的体验问题顺手做掉。HADOOP_LOG_DIR和HADOOP_PID_DIR单独拎出来是因为默认值在/tmp下机器一重启 pid 文件没了、日志也可能被清查问题时连黑匣子都找不到。改完先别急着启集群用下面这条命令模拟 Hadoop 的启动方式能干净地暴露问题# 模拟 start-dfs.sh 的行为ssh 到对端在非交互 shell 里取变量 for h in node-0002 node-0003; do echo $h ssh -o BatchModeyes $h echo JAVA_HOME$JAVA_HOME; command -v java done如果这里JAVA_HOME是空的但command -v java有输出说明方案 B 生效了、方案 A 还没分发到从节点如果两个都空那从节点连方案 B 都没做。-o BatchModeyes的作用是禁止任何交互式输入密码提示会直接变成失败退出不会让你误以为「卡一下就好了」。另外别忘了hadoop-env.sh改完必须分发到每个节点它躺在$HADOOP_HOME/etc/hadoop/里属于配置目录不是共享存储不会自己同步过去。2.4 已经格式化过的半成品集群怎么救回来如果报错出现在hdfs namenode -format之前直接改好重启即可。但如果格式化那一步已经跑过哪怕后面启动失败事情会麻烦一点。hdfs namenode -format会生成一个新的clusterID并写进 NameNode 的元数据目录同时 DataNode 首次启动时会把这个 ID 记在自己的目录里。如果你之前已经启动过一次 DataNode后来改了路径重新 format两边 ID 对不上DataNode 起来就会报Incompatible clusterIDs或者直接拒绝注册。这类问题在实验环境里特别常见因为大家习惯「反正才三个节点删了重来」。我一般的做法是先判断需不需要保留数据# 清空现场前先停服务否则守护进程还在写目录 stop-yarn.sh 2/dev/null; stop-dfs.sh 2/dev/null # 确认没有残留的 java 进程 jps # 输出里如果还有 NameNode / DataNode / ResourceManager用 kill 收掉 # 在【每个节点】上清空数据目录路径以 hdfs-site.xml 里的配置为准 rm -rf /home/modules/data/buf/* # 回到 node-0001 重新格式化这一步只能做一次 hdfs namenode -format # 启动前先确认从节点环境变量已就位用 2.3 里那条 ssh 检查 start-dfs.sh start-yarn.sh # 最后核对进程三个节点上应该能看到对应角色 jpsrm -rf那行对应的是实验报告里的/home/modules/data/buf这个目录通常是dfs.namenode.name.dir和dfs.datanode.data.dir的父目录。如果你的hdfs-site.xml里把 name 和 data 分到了不同目录就分别清别只清一个——只清 NameNode 不清 DataNode反而会把 ID 冲突坐实。jps是这套流程里最廉价的体检工具NameNode 在 node-0001DataNode 和 NodeManager 在全部节点缺哪个就去看那个节点的日志。日志位置就是上面HADOOP_LOG_DIR设的地方hadoop-用户名-datanode-主机名.log。很多人卡在「起了但不对」其实日志第一屏就写了原因。3. 节点互信失败Permission denied (publickey) 的四条排查线3.1 先分清是解析不到主机还是公钥被拒报错文本能直接把你导向不同方向别看到红字就当同一类问题处理报错关键词真正的问题先去查Could not resolve hostname node-0002主机名解析失败/etc/hostsPermission denied (publickey,gssapi-keyex,gssapi-with-mic)公钥没有被接受authorized_keys与权限Host key verification failed.对端指纹变了或 known_hosts 有旧记录~/.ssh/known_hostsAuthentication refused: bad ownership or modes目录/文件权限过宽家目录与.ssh权限停在Are you sure you want to continue connecting首次连接等待交互确认StrictHostKeyChecking最左列那几种里第一种其实跟 SSH 密钥一点关系没有纯粹是名字解析不到第三种也不是密钥问题是重装 ECS 后 IP 复用了、对端 host key 变了。把这两类误判成「密钥没配好」会导致你反复重做ssh-copy-id做十遍也不管用。3.2 /etc/hosts 里写内网 IP三个节点必须写成同一份Hadoop 的各处配置slaves、core-site.xml里的fs.defaultFS都会用到主机名而主机名要靠/etc/hosts或者内部 DNS 解析。最省事的做法是把三台机器的映射写死且每一台写成完全一样的内容# 三个节点都执行内容必须一模一样 cat /etc/hosts EOF 192.168.0.11 node-0001 192.168.0.12 node-0002 192.168.0.13 node-0003 EOF # 逐条验证解析结果注意看返回的 IP 是否与上面一致 getent hosts node-0001 node-0002 node-0003这里有两个容易翻车的点。一是别写公网 IPECS 之间走公网 IP 通信流量会绕出去再绕回来延迟和费用都不划算而且不少环境的安全组默认只放行内网互通公网回环反而连不上。二是三份内容必须一致我曾经遇到某台机器只写了另外两台、没写自己结果那台机器上start-dfs.sh能起来但它自己的 DataNode 连不上 NameNode——因为它解析node-0001失败了只是这个失败发生在子进程里主脚本不报错。getent hosts比ping更适合做验证它直接走系统解析器、不受 ICMP 是否被禁的影响输出也干净。ping在某些云环境里默认被拦你 ping 不通不代表解析有问题容易误判。写完 hosts 之后再用hostname确认一下本机的主机名确实是node-0001这类短名而不是带域名的全名如果全限定名和短名混用slaves文件里写的东西可能对不上。3.3 authorized_keys 与权限位700/600 不是玄学公钥认证被拒绝大多数情况不是「没复制」而是「复制了但权限不对被拒绝加载」。SSH 服务端对权限的要求近乎偏执家目录不能对 group 和 other 可写.ssh目录必须是 700authorized_keys必须是 600否则直接忽略这个文件且不会在客户端报出细节只会给你一句 publickey denied。# 1. 在每台节点生成密钥-N 表示空口令集群内部免交互必须这样 ssh-keygen -t rsa -b 2048 -N -f ~/.ssh/id_rsa # 2. 把所有节点的公钥收拢后统一追加这里演示本机分发到三台 for h in node-0001 node-0002 node-0003; do ssh-copy-id -i ~/.ssh/id_rsa.pub root$h done # 3. 修权限一行都不能省 chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_rsa chmod 755 ~/. # 家目录不能组内可写 # 4. 用 sshd 自己的视角检查配置是否被拒 sshd -T 2/dev/null | grep -Ei pubkeyauth|strictmodes|authorizedkeysfile第 2 步用ssh-copy-id比手抄公钥可靠它会自动建目录、追加内容、修权限。第 3 步的chmod 755 ~/.常被忽略StrictModes yes默认值会检查家目录权限家目录如果是 777 或者属于别的用户整条链路直接废掉。第 4 步查sshd -T是我后来养成的习惯如果PubkeyAuthentication是no你把密钥复制一百遍也没用如果AuthorizedKeysFile指向的路径不是你写的那个有些镜像改过默认值同样是白忙。这个命令能一次性排掉这两种可能。还有一个隐藏条件互信要求的是两两之间都能通不是只从 node-0001 到其他节点。DataNode 之间、NodeManager 之间都有通信start-dfs.sh也会 ssh 回来操作本机。所以我一般用嵌套循环把九个方向全测一遍而不是只测三条。3.4 用 BatchMode 把「卡住」变成「立刻报错」首次 ssh 到新机器会问Are you sure you want to continue connecting (yes/no)?手动敲一次 yes 就过去了但在批量脚本里它会静默等待表现成「命令执行了但没反应」这是最折磨人的一类「故障」——它连报错都不给你。# 关闭首次连接的交互确认并把失败立即暴露出来 cat ~/.ssh/config EOF Host node-0001 node-0002 node-0003 StrictHostKeyChecking no UserKnownHostsFile /dev/null LogLevel ERROR EOF chmod 600 ~/.ssh/config # 九个方向一次性验证任何一条失败都会立刻打出来 for a in node-0001 node-0002 node-0003; do for b in node-0001 node-0002 node-0003; do ssh -o BatchModeyes -o ConnectTimeout5 $a ssh -o BatchModeyes -o ConnectTimeout5 $b hostname \ /dev/null 21 echo OK $a - $b || echo FAIL $a - $b done doneStrictHostKeyChecking no加UserKnownHostsFile /dev/null的组合适合实验环境里频繁重建节点的场景代价是放弃了主机指纹校验生产环境不要这么写。BatchModeyes才是这套检测的核心它让任何需要人工输入的场景直接以非零码退出FAIL那一行出现的位置就是你要修的方向。ConnectTimeout5防止某台机器网络不通时卡满默认的几十秒九个方向跑一遍也就几十秒。这个双层嵌套是故意的外层 ssh 到 a内层再从 a ssh 到 b模拟的正是start-dfs.sh从 node-0001 跳到其他节点的真实调用路径。4. OBS 接不上No FileSystem for scheme obs 的三个层级4.1 第一层jar 没进 classpathNo FileSystem for scheme: obs这个报错的意思很直白Hadoop 的FileSystem是一个可插拔的抽象层靠fs.scheme.impl配置去反射出实现类反射不到就抛这个异常。它跟网络、跟密钥、跟桶权限统统无关纯粹是当前 JVM 的 classpath 里没有那个 jar。# 1. 先确认配置里确实声明了实现类这一步在后面 core-site.xml 里配 grep -A2 fs.obs.impl $HADOOP_HOME/etc/hadoop/core-site.xml # 2. 把 OBS 的接入 jar 放进 Hadoop 的公共库目录 cp /root/hadoop-obs-*.jar $HADOOP_HOME/share/hadoop/common/lib/ # 3. 验证 jar 是否真的进了 classpath hadoop classpath | tr : \n | grep -i obs # 4. 直接调实现类能打印出类名说明加载成功 hadoop org.apache.hadoop.fs.obs.OBSFileSystem 21 | head -3第 2 步的落点很关键share/hadoop/common/lib/会被所有守护进程NameNode、DataNode、ResourceManager、NodeManager以及hdfs命令行共享。放在share/hadoop/hdfs/lib/下命令行能用但 YARN 上的作业读不到放在自己的 classpath 里只有当前用户当前目录生效。每个节点都要放这一点在第 5 章还会再提一次。第 3 步的hadoop classpath | tr : \n是把长串路径拆成一行一条方便 grep比肉眼在那条几百字符的字符串里找快得多。第 4 步是很多人不知道的用法直接用hadoop命令去跑一个类的main类加载成功会打出帮助或抛参数错误类加载失败就是ClassNotFoundException一眼分明。关于 jar 版本Hadoop 2.x 时代 OBS 的接入包有多个发行版本命名方式和依赖关系不太一样有些还需要一并放入对象存储的 SDK 包。遇到NoClassDefFoundError而不是ClassNotFoundException时通常就是缺了传递依赖把 SDK 包一起丢进同一个目录即可。4.2 第二层core-site.xml 的四个必填项与 endpoint配置文件的写法决定了 Hadoop 去哪里找实现类、用哪套凭据、连哪个接入点。这四个属性缺一个都跑不通configuration !-- 1. 声明 obs:// 协议由哪个类处理缺了就是 No FileSystem for scheme -- property namefs.obs.impl/name valueorg.apache.hadoop.fs.obs.OBSFileSystem/value /property !-- 2. AK/SK从控制台下载的凭据文件里取注意不要带引号不要带空格 -- property namefs.obs.access.key/name value替换成你的AK/value /property property namefs.obs.secret.key/name value替换成你的SK/value /property !-- 3. endpoint 必须和桶所在区域一致写错会 301 或 404 -- property namefs.obs.endpoint/name valueobs.cn-north-4.你的平台域名/value /property !-- 4. 默认文件系统仍指向 HDFSOBS 用全路径访问 -- property namefs.defaultFS/name valuehdfs://node-0001:9000/value /property /configuration第一条是无脑照抄的但要注意大小写fs.obs.impl全是小写类名是全限定名多一个字母就会回到No FileSystem for scheme。第二条里的值一定不要加引号XML 里加了引号会被当成值的一部分最终签名错误报 403而报错信息不会告诉你「你的密钥多了个引号」。第三条的 endpoint 是最容易搞错的一项。控制台的桶概览页会给出两种地址普通地址和双栈地址格式分别是obs.区域.平台域名和obs.dualstack.区域.平台域名。集群在 ECS 内网跑用普通地址就够了别手动拼双栈如果桶和 ECS 不在同一区域跨区访问不仅慢还可能因为 endpoint 与桶区域不匹配直接返回 301。第四条是我特意保留的一个坑位。fs.defaultFS指向hdfs://而不是obs://因为你仍然需要一个完整的 HDFS 集群来跑 NameNode、DataNode、YARNOBS 在这里的角色是外挂的数据源不是替换 HDFS。如果把它改成obs://桶名后续所有不带 scheme 的相对路径都会被解析到 OBShdfs dfs -put之类操作的行为会变得非常反直觉还会冒出Wrong FS: obs://..., expected: hdfs://node-0001:9000这类报错。访问 OBS 时一律写全路径obs://桶名/路径干净利落。顺带说一句并行文件系统实验报告里提到创建并行文件系统它在 Hadoop 侧同样按obs://访问配置项完全一样差别在于元数据操作延迟和 rename 语义更接近本地文件系统适合跑对目录操作敏感的计算任务。普通对象桶在 rename 上是「复制再删除」跑大作业时能明显感觉到差别。4.3 第三层AK/SK 与 403/404 报错对照配置齐了、jar 也加载了剩下的报错基本都能从状态码反推报错片段含义排查方向Status Code: 403AccessDenied凭据无效或无权限AK/SK 抄错、多了空格引号、凭据已失效Status Code: 404NoSuchBucket桶名或 endpoint 不对桶名拼错、endpoint 区域与桶不一致Status Code: 301请求被重定向endpoint 写的区域不是桶所在区域SignatureDoesNotMatch签名计算不一致SK 错误或系统时间偏差过大Connect timed out网络不通安全组未放行出方向、endpoint 写错SignatureDoesNotMatch这一条我单独说一下它跟时间强相关签名里带时间戳如果 ECS 的系统时间偏差超过允许窗口签名就会对不上而凭据完全正确。云主机通常有时间同步但如果你手动改过时间做别的实验忘了改回来就会出现「昨天还好好的今天全 403」的诡异局面。date看一眼就知道。拿到凭据文件后正确姿势是只取需要的两列而不是整份文件到处传# 凭据文件是 CSV第四列是 AK、第五列是 SK跳过表头读取 tail -n 2 credentials.csv | awk -F, {print AK$2\nSK$3} # 更稳妥的做法用环境变量临时注入不落盘到配置文件里 export OBS_AK你的AK export OBS_SK你的SKtail -n 2跳过表头那行awk -F,按逗号切列。凭据文件如果被换了行尾符Windows 的 CRLFSK 末尾会多一个\r肉眼完全看不出来但签名必然失败。遇到「怎么都对但就是 403」用cat -A credentials.csv看一眼行尾是不是^M$能省下大量时间。注意AK/SK 等同于账号密码不要写进要提交的代码、不要贴进文档、不要出现在截图里。实验做完在控制台把旧凭据删掉重建是最省事的收尾方式。4.4 验证用 hdfs dfs 打通 OBS 再回去配集群我调整了验证顺序先在单机上把 OBS 通路彻底打通再去动集群。因为集群一旦启动OBS 出问题时的报错会混在 YARN 的容器日志里噪音极大。# 1. 列桶根目录这一步能过说明 jar、配置、凭据、网络四条线全通 hdfs dfs -ls obs://你的桶名/ # 2. 写一个小文件上去验证写权限和签名 echo obs connectivity test /tmp/t.txt hdfs dfs -put -f /tmp/t.txt obs://你的桶名/test/t.txt # 3. 读回来看内容是否一致 hdfs dfs -cat obs://你的桶名/test/t.txt # 4. 确认删除能生效能测出桶权限是否给全 hdfs dfs -rm -r obs://你的桶名/test第 1 步如果报No FileSystem就回 4.1报 403 就回 4.3报超时就查安全组。这个顺序把四类问题完全解耦比对着集群日志猜要快得多。还有一点值得提醒hdfs dfs走的是客户端侧的配置集群侧的守护进程走的是它自己读到的core-site.xml。你在命令行测通了不代表 DataNode 也能读 OBS——后者的配置文件是分发过去的那一份可能还是旧的。所以第 1 章里反复强调的「改完要分发」到这一章依然成立。5. 避坑清单分发、格式化、重启三个环节的五个翻车现场5.1 分发环节改了配置只发一半现象node-0001 上一切正常start-dfs.sh也能把进程拉起来但 node-0003 上的 DataNode 死活不出现或者出现了却报ClassNotFoundException。原因配置或 jar 只更新了部分节点。Hadoop 的安装目录是每台机器各自的本地目录不存在共享scp到一台不等于到了三台。更隐蔽的情况是分发时用了scp -r覆盖目标节点上原有的文件因为同名被覆盖、不同名的旧文件被保留造成「新老混装」。解决固定用一条循环命令分发并在分发后做校验而不是靠记忆# 从 node-0001 把整个 Hadoop 目录推到其他节点--delete 保证目标与源一致 for h in node-0002 node-0003; do rsync -az --delete $HADOOP_HOME/ root$h:$HADOOP_HOME/ done # 校验关键文件的一致性MD5 不同就是没同步到 md5sum $HADOOP_HOME/etc/hadoop/core-site.xml $HADOOP_HOME/etc/hadoop/hadoop-env.sh ssh node-0002 md5sum $HADOOP_HOME/etc/hadoop/core-site.xml $HADOOP_HOME/etc/hadoop/hadoop-env.shrsync -az里的-a保留权限和时间戳-z压缩传输--delete是核心——它让目标端与源端完全一致避免旧文件残留。/结尾不能丢$HADOOP_HOME/和$HADOOP_HOME在 rsync 里语义完全不同前者同步内容后者会在目标下再套一层目录。分发完跑一次md5sum两边输出一模一样才算完这一步花十秒能省掉半小时的「为什么行为不一致」排查。5.2 启动环节重复 format 与 slaves 写错现象一NameNode 正常起来但 DataNode 一个都没有日志里写Incompatible clusterIDs或者 NameNode 日志里出现Datanode denied communication。原因hdfs namenode -format被执行了不止一次或者清数据目录时只清了 NameNode 侧。每次 format 都会生成新的 clusterIDDataNode 目录里存的还是旧的两边对不上就拒绝注册。解决要么把所有节点的数据目录全部清空后只 format 一次要么用-clusterid参数显式指定同一个 ID。实验环境里前者更省事# 停服务 - 清所有节点的数据目录 - 只 format 一次 - 启动 stop-yarn.sh; stop-dfs.sh for h in node-0001 node-0002 node-0003; do ssh $h rm -rf /home/modules/data/buf/* done hdfs namenode -format start-dfs.sh现象二slaves文件里明明写了三行实际只有一两个 DataNode 起来。原因slaves里写的是主机名但文件里混入了空行、末尾多了空白字符或者写的是 IP 而/etc/hosts里只有主机名。start-dfs.sh逐行读取并 ssh 过去读到解析不了的行就静默跳过不报错。解决用cat -A查出隐藏字符并保证写的内容与getent hosts能解析的名字一致# 检查是否有 ^MCRLF或行尾多余空格 cat -A $HADOOP_HOME/etc/hadoop/slaves # 逐个验证 slaves 里的名字都能解析 while read -r h; do [ -n $h ] { getent hosts $h /dev/null echo OK $h || echo FAIL $h; } done $HADOOP_HOME/etc/hadoop/slavescat -A会把不可见字符显式打出来行尾是$才正常出现^M$就是 Windows 换行。那个 while 循环做的事很朴素把slaves里的每一个名字拿去解析一遍解析不了立刻暴露比等start-dfs.sh静默跳过强太多。5.3 重启环节ECS 重启后被覆盖的 /etc/hosts现象昨天还好好的集群今天机器重启后互信失效、getent hosts node-0002也查不到但文件看着没动过。原因部分云镜像带 cloud-init 之类的初始化逻辑重启时会根据元数据重写/etc/hosts或重置主机名你手工追加的那几行被覆盖。也有情况是主机名被改回了默认值导致slaves里的名字与本机hostname对不上。解决把主机名固定下来并把 hosts 条目交给 cloud-init 管理而不是手工改# 1. 永久设置主机名每个节点设成自己的名字 hostnamectl set-hostname node-0002 # 2. 检查本机主机名与 hosts 里的条目是否自洽 hostname; getent hosts $(hostname) # 3. 若确认是启动时被覆盖把追加动作写成开机后执行的脚本 # 而不是只改 /etc/hosts 文件本身 cat /etc/rc.local EOF grep -q node-0001 /etc/hosts || echo 192.168.0.11 node-0001 /etc/hosts EOF chmod x /etc/rc.localhostnamectl set-hostname是持久化设置比hostname xxx那种临时改法可靠得多后者重启就没了。第 2 步那个自洽检查很关键本机主机名如果解析不到自己很多组件会用回环地址或者直接报错而这在单机测试时完全看不出来。5.4 权限与用户目录建好了但守护进程写不进去现象启动脚本没报错进程也在但 DataNode 日志里持续刷Permission denied或者 YARN 的容器一直失败。原因hdfs-site.xml里配的数据目录是用 root 建的守护进程却以别的用户启动或者locals目录的权限是 755容器需要在该目录下再建子目录时被拒。解决目录的属主必须和实际运行守护进程的用户一致权限给到 755 就够不要图省事给 777# 确认目录属主与运行用户一致用 ps 看进程实际以谁的身份跑 ps -eo user,pid,cmd | grep -E DataNode|NodeManager | grep -v grep # 按实际用户修正属主与权限 chown -R root:root /home/modules/data /home/nm/localdir chmod -R 755 /home/modules/data /home/nm/localdirps -eo user,pid,cmd是判断「到底以谁的身份在跑」的唯一可靠方式配置文件里写什么都不算数。修正属主时-R递归要慎用指到系统目录上会很危险落点一定是自己建的这几个业务目录。5.5 时钟与主机名混用那些「昨天还好好的」现象集群跑了一阵突然大面积失败作业提交不了、OBS 访问 403重启后又恢复。原因多台机器的系统时间出现偏差或者某台机器的主机名被改过后没有重开 ssh 会话。前者影响签名和 Kerberos 类认证后者影响基于主机名的服务注册。解决把时间同步和主机名检查放进巡检脚本而不是等出问题再查。这类问题最气人的地方在于它不可复现——你去看的时候往往已经自己好了只能靠事前检查兜住。6. 快速定位把报错关键词映射成一条命令排查这类集群故障我的原则是先按依赖顺序体检再看具体报错。因为大量「报错」其实是上游某个环节失败后引发的连锁反应从下游往上找永远在追影子。下面这套顺序是我自己固定会跑的从最底层开始第一个失败项基本就是根因。顺序检查项一条命令失败时的典型结论1主机名解析getent hosts node-0001 node-0002 node-0003/etc/hosts缺失或被覆盖2免密互信ssh -o BatchModeyes node-0002 hostname公钥或权限问题3Java 环境ssh -o BatchModeyes node-0002 echo $JAVA_HOMEhadoop-env.sh未分发4配置一致ssh node-0002 md5sum $HADOOP_HOME/etc/hadoop/*.xml分发不完整5jar 加载hadoop classpath | tr : \n | grep -i obs接入包没放对位置6进程存活jps每台都跑角色缺位去看对应日志7HDFS 通路hdfs dfs -ls /NameNode 未格式化或未启动8OBS 通路hdfs dfs -ls obs://桶名/凭据、endpoint 或网络把上面这些串起来写成一份可以反复执行的体检脚本比每次现查命令可靠得多#!/bin/bash # cluster-check.sh按依赖顺序体检遇到第一个 FAIL 就停那个就是根因 set -u NODESnode-0001 node-0002 node-0003 fail() { echo FAIL [$1] $2; exit 1; } # 1. 解析 for h in $NODES; do getent hosts $h /dev/null || fail dns $h 解析失败; done echo OK 主机名解析 # 2. 互信BatchMode 保证失败立刻返回而不是卡住 for h in $NODES; do ssh -o BatchModeyes -o ConnectTimeout5 $h hostname /dev/null 21 \ || fail ssh $h 免密失败 done echo OK 节点互信 # 3. 各节点 JAVA_HOME 非空且可执行 for h in $NODES; do ssh -o BatchModeyes $h test -x $JAVA_HOME/bin/java \ || fail java $h 上 JAVA_HOME 无效 done echo OK Java 环境 # 4. 配置一致性以 node-0001 为基准 base$(md5sum $HADOOP_HOME/etc/hadoop/*.xml | md5sum | cut -d -f1) for h in $NODES; do cur$(ssh -o BatchModeyes $h md5sum \$HADOOP_HOME/etc/hadoop/*.xml | md5sum | cut -d -f1) [ $base $cur ] || fail config $h 配置文件与基准不一致 done echo OK 配置一致 echo 体检通过可以去看具体报错了这个脚本有几个设计取舍值得说明。set -u让未定义变量直接报错退出避免因为$HADOOP_HOME恰好为空导致md5sum /*.xml这类灾难性展开。fail()里直接exit是因为按依赖顺序检查时第一个失败一定在上游后面的检查结果没有参考价值继续跑只会刷出一屏噪音误导判断。第 4 步的配置一致性用的是「把所有 xml 的 md5 再取一次 md5」的折叠写法好处是节点数量变化时不用改脚本坏处是定位不到具体哪个文件不同所以它只负责报警具体哪个不一样还得回去让rsync --delete重推一遍。真正跑起来之后你会发现绝大多数「莫名其妙」的故障都能在前三步被抓住。我印象最深的一次是某个节点jps里 DataNode 时有时无日志翻遍了没线索最后是第 3 步暴露出来的那个节点的hadoop-env.sh里JAVA_HOME写的是旧路径那个旧 JDK 被另一个实验删掉了于是 DataNode 启动时偶尔能起来走 PATH 里的 java、偶尔起不来走$JAVA_HOME里的表现出完全随机的样子。如果没有这条固定检查项我大概率还在猜是不是磁盘满或者内存泄漏。从那以后我每次改完集群配置不管改的只是一行 XML 还是一个 jar 包都强制走一遍「分发 → md5 校验 → 体检脚本 → 看 jps」这四步再动手启服务。多花的那两分钟比起在日志里大海捞针实在太划算了。希望这套顺序也能帮你把卡住的那个报错快速定位出来完整的实验流程和配置参数都在那份鲲鹏云大数据实验报告文档里可以对照着走一遍。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询