简介:面向高校学生与云计算初学者的一份鲲鹏云大数据实验文档,聚焦在华为云环境中从零搭建并跑通Hadoop集群这一常见难题,属于入门到进阶之间的实操型实验记录。压缩包内仅有1个docx文件,约2.52MB,以图文混排形式完整保留实验过程与配置细节,便于边看边对照操作。目前已有893人学习,说明该实验路径在同类课程中具有一定参考需求。内容覆盖购买与配置ECS、OBS并行文件系统及AK/SK密钥获取,节点间hosts解析与SSH免密互信,core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml与slaves等核心配置,以及Java家目录指向错误导致的重新分发、NameNode初始化与HDFS启动等排错过程,读者可据此理解云上大数据基础设施的部署顺序与常见故障定位思路。
1. 从一次“前七步都对,第八步全崩”的搭建说起
hadoop namenode -format跑下去,终端直接回一行Error: JAVA_HOME is not set and could not be found。可你明明装过 JDK,java -version也能正常打出 1.8.0_242。这个坑在鲲鹏云上搭 Hadoop 集群的实操里出镜率极高,翻这份鲲鹏云大数据实验报告时能看到同一个 Java 路径问题被反复标注了四五次——配置环境变量时错、分发安装包时错、NameNode 初始化时错、启动 HDFS 时错、执行 hdfs 命令时还错。表面看是五个故障,根子上却只有一处:java -version走的是PATH,Hadoop 启动脚本走的是JAVA_HOME指向的那个绝对路径,两条路根本不是一回事,前者通不代表后者通。再叠加节点互信没打通、OBS 的 AK/SK 填错位这两类高频翻车点,一个看起来只有十几步的实验,能把人卡上整整一个下午。
这篇按我实际踩过的顺序走:先拆 Java 路径这个总根因,再讲节点互信和对象存储接入两处最容易配错的细节,最后落一份能照着抄的避坑清单和定位路径。
2. JAVA_HOME 指错:格式化、启动、hdfs 命令全线报错
这一章要先定位一个根本原因,它是整套实验里牵连最广、最容易被误判的故障源头。原始记录里的细节很清楚:节点上装了 openjdk,/etc/profile里也加了环境变量,可 Hadoop 的环境变量配置执行失败,接着 NameNode 初始化报错、HDFS 起不来、hdfs命令也执行失败,全部改对JAVA_HOME之后才逐一恢复正常。这种“一改全通”的现象说明它不是五个独立 bug,而是一个问题在五个环节的投影。
2.1 报错现场:脚本说找不到 JAVA_HOME,终端里 java 却能跑
先把现象摆清楚。你执行下面三条命令,会得到互相矛盾的结果:
# 终端里 java 明明在 java -version # 环境变量看着也设置了 echo $JAVA_HOME # 但 Hadoop 就是不认 hadoop versionjava -version有输出,是因为 shell 通过PATH找到了某个java可执行文件;hadoop version报JAVA_HOME is not set and could not be found,是因为每个 Hadoop 脚本开头都有一句类似exec "$JAVA_HOME/bin/java" ...的调用,脚本只认JAVA_HOME拼出来的绝对路径,路径上没有可执行文件就直接退出。这两套查找机制互不相干,所以完全可能出现“java 能跑,Hadoop 说没有 Java”。
更隐蔽的一种形态是:JAVA_HOME路径确实存在,脚本检查也能通过,但指向的是另一套 JDK,于是报UnsupportedClassVersionError或者Could not reserve enough space for object heap。这类报错更难往 Java 路径上想,很多人会去翻yarn-site.xml的内存参数,绕一大圈才回到原点。
定位动作只有一步,看java到底从哪来:
# which 看 PATH 命中的是谁 which java # readlink -f 追到软链接的最终真实路径 readlink -f $(which java)readlink -f的输出会是类似/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64/jre/bin/java的绝对路径。这个字符串就是判断JAVA_HOME该写什么的唯一依据,凭记忆写或者照抄别人的博客,基本都会翻车。
2.2 为什么在鲲鹏上路径必须写到 aarch64 那一层
问题出在包名上。x86 平台上很多人习惯写/usr/lib/jvm/java-1.8.0-openjdk,这个短路径能生效,是因为发行版通常会给一份不带版本号后缀的目录或软链接。而在鲲鹏这种 aarch64 架构的机器上,通过包管理器装出来的 JDK 目录名带完整版本号与架构后缀,形如java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64,/usr/lib/jvm/下压根没有那个“干净”的短路径。你把短路径写进配置,脚本去.../java-1.8.0-openjdk/bin/java找文件,找不到,于是报错。
正确的写法是带上完整目录名:
# 以 readlink -f 的真实输出为准,这里按实际路径替换 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 # 立刻验证,不要等到启动集群时才发现 $JAVA_HOME/bin/java -version三条命令的分工是:第一行把JAVA_HOME钉死在真实目录上;第二行把该目录下的bin前置到PATH,保证java、javac、jps都来自同一套 JDK;第三行用绝对路径调用一次,绕开PATH缓存,确认这个路径下确实有一个能跑的 java。养成用绝对路径验证的习惯,能省掉一类“改完了还是不对”的自我怀疑。
注意:
JAVA_HOME指到.../aarch64这一层即可,不要指到.../aarch64/jre。部分组件会自己拼/jre/bin/java,指到 jre 层会出现/jre/jre/bin/java这种双重拼接的路径。
环境变量写进/etc/profile之后还必须source一次,而且只能在当前会话生效,新开的 SSH 连接会自动读到——这一点在下一节的“重新分发”中会变成一个关键差异。
2.3 改完 JAVA_HOME 之后,为什么必须重新分发一遍
这是整章最容易被忽略的一步,也是那份实验记录里反复出现“重新配置分发”“重新分发”“因为 javahome 错误重新分发”的原因。hadoop-env.sh这个文件是跟着 Hadoop 安装包一起分发的,你在node-0001上先写错了JAVA_HOME,然后scp -r把整个hadoop-2.8.3目录推到node-0002和node-0003,那么三台机器上的hadoop-env.sh就都带着同一个错误值。这时候你只在node-0001上改了,另外两台没动,启动脚本推过去一样起不来,报错还跟之前一模一样,容易让人以为“改了没用”。
所以改动要按“先改一台、验证、再统一分发”的顺序走:
# 1. 在 node-0001 上定位并修改 hadoop-env.sh 里的 JAVA_HOME cd /home/modules/hadoop-2.8.3/etc/hadoop # 把被注释掉的或写错的 JAVA_HOME 那一行替换成真实路径 sed -i 's|^#*\s*export JAVA_HOME=.*|export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64|' hadoop-env.sh # 2. 确认只有一处生效的 JAVA_HOME,避免文件里留了多行互相覆盖 grep -n "JAVA_HOME" hadoop-env.sh # 3. 本机先验证通过再分发 hadoop versionsed那一行的含义是:匹配行首可选的#和空白,后面接export JAVA_HOME=的整行,替换成你要的那一行。用sed而不是手工vim,是为了让三台机器的结果完全一致——手工改最容易出现多空格、少引号、行尾多一个字符这类肉眼难辨的差异。第三行的hadoop version是放行条件,本机不过就不要再往下推。
分发用scp -r或者rsync -av都可以,关键是要覆盖到etc/hadoop整个目录,而不是只推那一个文件:
# 把改好的配置目录推到另外两个节点,覆盖旧的错误值 for ip in 120.46.156.37 121.36.3.111; do scp -r /home/modules/hadoop-2.8.3/etc/hadoop/ root@$ip:/home/modules/hadoop-2.8.3/etc/hadoop/ done循环里两个 IP 是另外两个节点的地址,scp -r覆盖同名文件时不提示,所以不需要加-f。分发完成后每台机器都要执行一次source /etc/profile,原因是JAVA_HOME还单独写在了/etc/profile里,而hadoop-env.sh是 Hadoop 脚本自己读的,两边都对才行。很多“明明改了还是报错”的情况,就是改了hadoop-env.sh忘了改/etc/profile,或者反过来。
2.4 验证顺序:三条命令按依赖关系排
改完之后不要直接去跑start-dfs.sh,按下面这个顺序走一遍,能在最便宜的位置拦住问题:
# 第一层:Java 本身是否可用 java -version # 第二层:Hadoop 脚本是否认到了 Java hadoop version # 第三层:进程是否真的起来了 jps第一条不通过,说明/etc/profile或包安装有问题;第一条过、第二条不过,问题一定在hadoop-env.sh里的JAVA_HOME;前两条都过但jps只看到Jps自己,那才是 HDFS 配置或启动参数的问题,Java 这一层可以排除了。这个三段式验证的价值在于:它把“Java 路径”和“Hadoop 集群”两个层面切开,不让你在集群日志里翻半天,最后发现只是环境变量少了一级目录。
3. 节点互信没配通:ssh 还要密码,scp 分发直接卡住
集群搭建的第二类高频故障出在节点之间。原始记录里每台节点都做了同一组动作:vim /etc/hosts、cat /root/.ssh/id_rsa.pub、把公钥追加到授权文件、然后验证“无密码跳转”。这几步看着简单,但配置失败的形态很隐蔽——ssh有时候直接进不去,有时候能进但要输密码,还有时候ssh能进、scp却卡住不动。一旦互信没有配置好,后面所有依赖“从主节点推送到子节点”的操作都会失败。
3.1/etc/hosts里主机名和 IP 没对齐,ssh 直接超时
第一个动作是把主机名映射写进/etc/hosts。这一步失败的典型表现是:ssh node-0002卡住十几秒然后Connection timed out,但ssh 120.46.156.37是通的。这说明网络本身没问题,是主机名解析走错了路——要么/etc/hosts里根本没写,要么写的是别的主机名,要么同一台机器在文件里出现了两行不同 IP,解析取到了旧的那条。
# 每台节点都要有一份完整且一致的主机名映射 cat >> /etc/hosts <<'EOF' 10.0.0.11 node-0001 10.0.0.12 node-0002 10.0.0.13 node-0003 EOF # 验证解析结果,别只看文件内容 getent hosts node-0002追加进去的三行 IP 请换成你自己控制台里看到的私网地址,主机名要和hostname命令的输出严格一致——大小写、连字符、下划线都算不同名字。第二行的getent hosts走的是系统实际使用的解析链路,能暴露“文件里写了但被别的配置抢先”的情况,比cat一眼更可靠。三台机器上的这份文件必须完全相同,任何一台缺行,都会在那台机器上表现为“别人连得进我,我连不出去”。
注意:主机名映射优先写私网地址。写公网地址在带宽和时延上都不划算,而且在部分网络环境下公网回环会导致
scp传到一半断开。
3.2 公钥追加与权限:.ssh目录和文件的三个权限位
第二个动作是把id_rsa.pub的内容追加到目标节点的authorized_keys。这一步最常见的两种失败:一是用>覆盖而不是>>追加,把之前配好的公钥全冲掉了;二是权限不对,ssh 出于安全策略直接拒绝使用这个文件,而且默认不告诉你原因。
# 在每台节点上生成密钥对(已有则跳过,不要重复生成覆盖旧的) ssh-keygen -t rsa -N '' -f /root/.ssh/id_rsa # 把三台节点的公钥汇总追加到三台的 authorized_keys(追加,不是覆盖) for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do cat /root/.ssh/id_rsa.pub | ssh root@$ip 'cat >> /root/.ssh/authorized_keys' done # 修权限,这三条缺一条都可能被静默拒绝 chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys chmod 600 /root/.ssh/id_rsassh-keygen的-N ''表示不设密码短语,集群内部自动登录必须无口令,设了密码短语每次都要交互输入,脚本就断在那里。循环里的cat >>是关键,用>>才能保证多台机器的公钥共存;如果第一台执行时文件不存在,>>会自动创建,不用提前touch。三条chmod分别对应目录、授权文件、私钥,权限过宽时 sshd 会认为文件不可信而跳过,表现出来就是“公钥明明在里面却还要密码”。
改完权限后重登一次再测,因为 ssh 客户端有连接复用,不重开会沿用旧状态:
# 带 -v 看握手细节,认证失败会明确打出尝试过哪些公钥 ssh -v node-0002 'hostname'-v的输出里找Offering public key和Authentication succeeded这两行。如果只看到Offering没看到succeeded,说明公钥被拒绝了,回头查权限;如果连Offering都没有,说明 ssh 压根没找到你的私钥,去查id_rsa是否存在、~/.ssh/config有没有指定别的 IdentityFile。这个排查顺序比反复重生成密钥高效得多。
3.3 用一条循环把全部节点验证一遍
互信是 N 对 N 的关系,手工一台台试容易漏。一条循环能一次性把矩阵跑完:
# 遍历所有节点,逐台验证免密登录是否成立 for host in node-0001 node-0002 node-0003; do printf '%-12s' "$host" ssh -o BatchMode=yes -o ConnectTimeout=5 $host 'echo OK' 2>/dev/null || echo FAIL doneBatchMode=yes让 ssh 在需要交互输入时直接失败而不是挂住等输入,这样循环不会卡在某一台上;ConnectTimeout=5给每台机器 5 秒上限,避免网络不通时脚本长时间空转;2>/dev/null把冗长的错误信息吞掉,让输出保持成一列OK/FAIL。三个节点全部OK之后再往下走分发,否则后面每一条scp都会以同样的方式失败,把真正的根因淹没在一堆重复报错里。
4. OBS 接不进去:AK/SK 与 endpoint 填错位的三种报错
Hadoop 集群本身跑起来之后,第三个卡点往往出现在对象存储接入上。实验里用对象存储做数据存放,需要在core-site.xml里填认证信息和访问地址,而这几项参数恰好是最容易抄错、错一个字就完全不通的部分。原始记录里提到创建桶之后会得到一个credentials.csv,里面有三列:User Name、Access Key Id、Secret Access Key。这三个值对应到配置里的位置、以及 endpoint 该选哪个地址,是这一节要拆清楚的事。
4.1 credentials.csv 三列和配置项的对应关系
很多人出错不是不会填,而是把 AK 和 SK 填反了,或者把 User Name 当成了 Access Key。先明确对应关系:
| csv 中的列 | 含义 | 填到core-site.xml的哪个属性 |
|---|---|---|
User Name | 账号名,仅用于标识 | 不参与认证,填了也没用 |
Access Key Id | 访问密钥 ID,公开部分 | fs.obs.access.key |
Secret Access Key | 私钥,保密部分 | fs.obs.secret.key |
对应到配置片段大致是这样:
<configuration> <property> <!-- 访问密钥 ID,取自 credentials.csv 的 Access Key Id 列 --> <name>fs.obs.access.key</name> <value>你的AccessKeyId</value> </property> <property> <!-- 私钥,取自 Secret Access Key 列,注意不要带引号 --> <name>fs.obs.secret.key</name> <value>你的SecretAccessKey</value> </property> <property> <!-- 对象存储的访问域名,见下一小节的选择规则 --> <name>fs.obs.endpoint</name> <value>obs.cn-north-4.myhuaweicloud.com</value> </property> </configuration>三个<value>里只有第一个键是公开的,后两个属于敏感信息。把私钥贴进配置文件、配置文件再被分发到各节点、最后提交进版本库,是这类实验里最常见的泄露路径,所以我一般会在实验结束后把这套密钥直接删掉重建。填充时特别注意:csv 里的值不带引号也不带空格,复制粘贴时最容易带进一个不可见的行尾空格,导致签名校验一直失败而报错信息只说权限不足。
4.2 endpoint 选 IPv4 还是双栈:两个域名不能混用
对象存储给了两个访问地址,一个是 IPv4 的obs.cn-north-4.myhuaweicloud.com,另一个是双栈的obs.dualstack.cn-north-4.myhuaweicloud.com。这两个不是“主备”关系,而是面向不同网络栈的两套入口,选错的表现是域名解析失败或者连接超时。
# 先确认当前机器能解析哪个域名,再决定填哪个 getent hosts obs.cn-north-4.myhuaweicloud.com getent hosts obs.dualstack.cn-north-4.myhuaweicloud.com第一条有返回、第二条没有,就老老实实用 IPv4 那个;两条都有返回,用 IPv4 更省事,因为它对客户端的要求最低。不要因为“双栈听起来更先进”就选它——如果你的客户端所在网络对 IPv6 的支持不完整,连接会先尝试 IPv6 然后超时回退,表现为每次访问都慢几秒甚至直接失败,日志里只有一句含糊的Connection timed out。这个现象很像集群性能问题,实际上只是域名选错了。
core-site.xml改完之后同样要分发到所有节点,并且分发完不需要重启整个集群,但需要让新的配置生效:
# 分发配置 for ip in 120.46.156.37 121.36.3.111; do scp /home/modules/hadoop-2.8.3/etc/hadoop/core-site.xml root@$ip:/home/modules/hadoop-2.8.3/etc/hadoop/ done # 让 NameNode 重新加载配置 hdfs dfsadmin -refreshNodesscp只推单个文件,因为这一步只改了core-site.xml,没必要整目录覆盖。refreshNodes用于让 NameNode 重新读取节点相关配置,配合重启相关服务使用;如果改动涉及客户端访问路径,最稳妥的做法还是重启一次 HDFS 服务,避免部分进程还持有旧配置。判断是否需要重启的标准是:改动影响的是服务端还是客户端。服务端的core-site.xml改了就重启,纯客户端的配置分发后新起的命令自然生效。
4.3 用一次列目录把链路走通
配置对不对,不要靠猜,用一条命令验证到底:
# 列出对象存储桶根目录,能列出来就说明认证和 endpoint 都通了 hadoop fs -ls obs://你的桶名/这条命令走的是完整的“读配置 → 签名请求 → 访问 endpoint → 返回列表”链路,任何一环有问题都会在这里暴露。常见返回与对应根因如下:
AccessDenied:AK/SK 其中一项填错,或者两列填反了,回头对 csv 逐字符比。UnknownHostException:endpoint 域名拼错,或者当前机器解析不了这个域名,回到 4.2 用getent hosts确认。NoSuchBucket:桶名写错,或者桶不在这个 endpoint 对应的区域,跨区域的桶必须换区域域名。- 长时间无响应后超时:多半是 endpoint 选成了双栈但本机 IPv6 不通,换回 IPv4 域名重试。
把这条命令的返回结果和上面对照表比对,比翻 Hadoop 的日志快得多,因为日志里这类错误往往被包装成几层IOException,真正的错误码埋在最里面。
5. 避坑清单:五个在鲲鹏云上搭 Hadoop 反复踩的坑
下面五条都来自这套实验里真实出现过的失败,按“现象 → 原因 → 解决”写,遇到对得上的直接照做。
坑一:hadoop version报 JAVA_HOME 找不到。现象是终端里java -version正常,Hadoop 相关命令全部失败。原因通常是JAVA_HOME写成了不带版本号的短路径,在 aarch64 机器上根本不存在这个目录。解决方式是用readlink -f $(which java)拿到真实路径,把它完整写进hadoop-env.sh和/etc/profile,然后source并重新分发到所有节点。
坑二:只改了主节点,子节点还在跑旧配置。现象是主节点上一切正常,从子节点启动或者从主节点远程拉起时又报同样的错。原因是hadoop-env.sh随安装包一起被分发过,错误值已经复制到了每台机器。解决方式是把etc/hadoop整目录重新推一遍,推完每台机器执行source /etc/profile,再用jps逐台确认进程。
坑三:ssh node-0002卡住或提示输密码。现象是 IP 能 ping 通、ssh却超时或者反复要密码。原因要么是/etc/hosts里主机名没写全或写重了,要么是authorized_keys权限过宽被 sshd 拒绝。解决方式是用getent hosts验证解析结果,用chmod 700/600修权限,再用ssh -v看握手日志里有没有Authentication succeeded。
坑四:hadoop fs -ls obs://桶名/报 AccessDenied。现象是配置都填了、集群也起来了,就是读不到对象存储。原因是fs.obs.access.key和fs.obs.secret.key两列填反,或者粘贴时带了行尾空格。解决方式是对着credentials.csv逐字符比对,重新分发core-site.xml并重启 HDFS 服务,再用同一条ls命令验证。
坑五:同一个“Java 路径错误”在五个环节重复出现,被当成五个 bug。现象是环境变量报错、格式化报错、启动报错、hdfs命令报错,逐个去修,改完一个另一个又冒出来。原因是这些环节共用同一个JAVA_HOME配置,源头没修,下游全部连着倒。解决方式是先停下来做一次三段式验证(java -version→hadoop version→jps),确认 Java 这一层是干净的,再去看具体环节,避免在症状上反复打补丁。
6. 快速定位:从报错关键词到根因的三分钟路径
真到了现场,最耗时间的不是修,而是判断这串报错属于哪一层。我习惯按“报错关键词 → 可能层级 → 第一条验证命令”做一次快速分诊,把范围从“整个集群”缩到“一个配置文件”。
| 报错关键词 | 大概率层级 | 第一条验证命令 |
|---|---|---|
JAVA_HOME is not set | Java 环境变量 | readlink -f $(which java) |
UnsupportedClassVersionError | JDK 版本不匹配 | $JAVA_HOME/bin/java -version |
Connection timed out(ssh) | 主机名解析 | getent hosts node-0002 |
Permission denied (publickey) | 互信与权限 | ssh -v node-0002 hostname |
AccessDenied | AK/SK 配置 | 对照credentials.csv逐字符比 |
UnknownHostException | endpoint 域名 | getent hosts obs.cn-north-4.myhuaweicloud.com |
NoSuchBucket | 桶名或区域 | 回控制台核对桶名与区域 |
进程缺失(jps少进程) | 启动脚本与配置 | 看logs/下对应角色的.log |
拿到报错先在上表里找关键词,不要一上来就tail -f全量日志。找到层级之后,执行对应的那条验证命令,一条命令就能确认或排除这一层。如果命令返回正常,说明问题在更下游,往下走一层;如果返回异常,直接就地修,不要再往下查。这个思路能把排查成本从“翻几千行日志”降到“跑三条命令”。
分诊之后还有两个细节值得刻意做。第一,改任何配置前先备份原文件,cp core-site.xml core-site.xml.bak一行的事,但能让你在改崩之后三十秒回到上一个状态。第二,每次只改一个变量然后立刻验证,不要一次改五处再一起试——五处一起改、结果好了,你也不知道是哪一处起的作用,下次遇到同样的问题还是不会。
还有一条更省事的习惯:把“三台机器的同一个文件是不是完全一致”当成一个常规检查项。集群类实验里绝大多数诡异现象,追到底都是某台机器上的某个文件跟别人不一样。一条md5sum就能比出来:
# 在三台机器上分别执行,比对同一个配置文件的摘要 md5sum /home/modules/hadoop-2.8.3/etc/hadoop/core-site.xml三行输出一致,说明分发到位;有一行不同,就单独把那个文件重推一次。这个动作花不了十秒,但能挡掉一大类“看起来都配了就是不生效”的困惑。
从那以后我每次动 Hadoop 配置,都强制走一遍“改一台 →hadoop version验证 → 整目录分发 → 三台md5sum比对 → 再启动”这个流程,宁可多花两分钟,也不愿意在启动失败之后回头猜是哪台机器没同步。希望这套顺序能帮你少卡一个下午。
这份鲲鹏云大数据实验文档把从云主机与对象存储的准备、节点互信、Hadoop 分发与配置,到 NameNode 初始化和 HDFS 启动的每一步都记了下来,连出错后怎么回改都留了痕迹,需要对照原始步骤复现的可以直接取用。
本文还有配套的精品资源,点击获取