☰
鲲鹏云ARM三节点Hadoop集群搭建与避坑排错指南
2026/10/27 2:14:07 网站建设 项目流程

简介:《鲲鹏云大数据实验docx》是一份面向大数据初学者、云计算实验课程学生及集群运维入门者的实作记录,围绕华为云ECS与OBS环境准备、Hadoop集群搭建和基础服务启动展开,可帮助读者理解云端大数据基础设施从资源开通、节点配置到集群可用的完整流程,适合课程实验、部署练习与过程复盘,也能为云端集群网络与存储衔接提供直观参照。资源包仅含1个docx文档,大小约2.52MB,内容集中,便于查阅和随堂对照。目前已有893人学习,说明其在相关实验场景中具有一定参考价值。文档不只罗列配置项,还保留节点互信、OBS认证信息接入、Java环境修正、安装包分发、NameNode初始化及HDFS启动排错等过程,读者可据此梳理部署思路、定位常见环境问题,并形成可复用的实验记录与操作检查清单。

1. 一份带错误痕迹的鲲鹏云大数据实验文档,值不值得照着做

翻这份鲲鹏云大数据实验 docx,最打动我的不是它写对了什么,而是它把错的地方也留下了:JAVA_HOME 指错、Hadoop 包分发了三次、NameNode 格式化了不止一遍。Hadoop 集群搭建教程满网都是,但把 aarch64 上的 JDK 路径、免密互信方向、对象存储 Endpoint 这些一错就卡一整天的点标出来的不多。

它记录的是三节点 Hadoop 2.8.3 集群在鲲鹏云主机上的完整落地:买云主机、开对象存储桶、取 AK/SK,配 hosts、做互信、写四份 XML、格式化 NameNode、拉起 HDFS 和 YARN。适合要交实验报告的学生,也适合需要在 ARM 上快速起一套验证集群的工程师。下面按我复现的顺序拆,重点标文档里那几处看着对、跑起来错的地方。

2. 环境盘点:三台鲲鹏云主机加一个对象存储桶怎么配

动手之前先把资源和网络搞清楚,这一步省下来的时间比后面调配置多得多。这份文档的路线是「本地 HDFS 做数据面,对象存储做外部数据源」,所以云主机规格、存储桶权限、安全组放行三件事必须一次配对,否则后面每改一次配置都要重新分发、重新启动,节奏全乱。

2.1 云主机规格与 ARM 架构带来的三个硬约束

三台机器的角色分工建议按文档里的 node-0001/0002/0003 来,node-0001 兼任 NameNode 和 ResourceManager,另外两台跑 DataNode 和 NodeManager。实验环境里让 node-0001 也挂一块 DataNode 更划算,三副本才有地方放,否则两台 DataNode 最多只能摆两副本。

节点主要角色建议规格内网地址(示例)
node-0001NameNode / ResourceManager / DataNode4 vCPU 8 GiB / 系统盘 40 GiB10.0.0.11
node-0002DataNode / NodeManager2 vCPU 8 GiB / 系统盘 40 GiB10.0.0.12
node-0003DataNode / NodeManager2 vCPU 8 GiB / 系统盘 40 GiB10.0.0.13

第一个约束是镜像必须选 aarch64 版本。鲲鹏主机跑不了 x86_64 的二进制,JDK、Hadoop 安装包、rsync 这些都得确认是aarch64或noarch包,装错了不是报「格式错误」就是直接执行失败,排查起来很绕。

第二个约束是 Hadoop 官方二进制包里的 native 库是 x86 编译的。2.8.3 的lib/native/libhadoop.so在鲲鹏上加载会失败,启动时打一行Unable to load native-hadoop library for your platform的 WARN。功能不受影响,纯 Java 路径继续跑,但日志里天天有这个告警容易让人误判成别的问题,心里有数就行。

第三个约束是内存。YARN 默认给 NodeManager 分配 8 GiB,实测主机只有 8 GiB 时容器一起来就被内核 OOM 杀掉。后面yarn-site.xml里必须把yarn.nodemanager.resource.memory-mb手动压到 4096 左右。

2.2 对象存储桶、AK/SK 与 Endpoint 的取法

桶的创建在控制台点几下就完事,真正要记牢的是三个值:桶名、Endpoint、以及下载下来的credentials.csv里的 AK/SK。文档里同时给了 IPv4 和双栈两个 Endpoint,形如obs.<region-id>.example-cloud.com和obs.dualstack.<region-id>.example-cloud.com,集群内部跑 IPv4 就用前者,别图新鲜填双栈地址,Hadoop 的 s3a 客户端在某些版本上对 IPv6 解析会超时。

credentials.csv打开就是三列,User Name、Access Key Id、Secret Access Key。这份文件只在下发时出现一次,SK 丢了只能重新生成一对。我一般会先落到跳板机的/root/.obs/credentials.csv,chmod 600,然后只把值写进配置文件,绝不把明文 AK/SK 打到终端回显里。

注意:AK/SK 等同于账号密码,写进 Git 仓库或者截图发出去就是事故。示例里我一律用占位符,复现时自己替换。

主账号的 AK 权限太大,实验环境也建议在控制台新建一个子用户,只授予目标桶的读写权限,再拿子用户的 AK/SK。这样即使配置被误传到别处,影响范围也只是一两个桶。

2.3 安全组与内网互通的最小放行清单

集群内部通信走内网,安全组只需要放行内网段;公网只留 22 端口给自己登录,其余端口一律不对外。文档里那几台机器的公网 IP 只用于 SSH,/etc/hosts里千万别写公网 IP,否则 DataNode 之间传块会绕一圈公网,速度掉到几 MB 不说,流量费也跑得飞快。

端口服务放行范围
22SSH 与分发办公网段
9000NameNode RPC集群内网
50010 / 50075DataNode 数据传输 / HTTP集群内网
50070NameNode Web UI办公网段
8032 / 8088ResourceManager RPC / Web UI集群内网 / 办公网段
8042NodeManager Web UI集群内网
19888JobHistory Web UI办公网段

排错时最常见的现象是jps能看到进程但 Web UI 打不开,八成是安全组没放 50070 或 8088,而不是服务没起来。养成先看端口再看日志的习惯,能省下大量无谓的配置回滚。

2.4 目录规划:为什么要把数据和程序分开

文档里建了/home/modules/data/buf和/home/nm/localdir两个目录,这个分法是对的:/home/modules放解压后的 Hadoop 程序,/home/modules/data放 HDFS 的元数据和数据块,/home/nm/localdir放 YARN 容器的本地临时文件。三者混在一起,节点磁盘写满时你分不清是谁撑爆的。

# 三台节点都执行,先建目录再谈配置 mkdir -p /home/modules/data/dfs/name mkdir -p /home/modules/data/dfs/data mkdir -p /home/modules/data/tmp mkdir -p /home/nm/localdir chmod -R 755 /home/modules /home/nm df -h /home # 顺手确认剩余空间,HDFS 块默认 128 MiB,空间不够会提前退出

dfs/name存的是 fsimage 和 edits,通常几 MB 到几十 MB;dfs/data才是真正吃盘的地方。实验环境 40 GiB 系统盘跑两三副本没问题,但如果你打算拿它跑 WordCount 之类的小作业,注意输入数据别直接怼到 HDFS 上几十 GB,先把盘扩了再说。

3. Hadoop 集群落地:从 hosts 互信到四份 XML

这一章是整个复现过程的主干。配置项本身不多,难的是顺序:hosts 先于免密,免密先于分发,分发先于格式化,任何一步颠倒都会得到一堆看不懂的报错。下面按我实际的执行顺序走一遍,每一步都给可抄的命令。

3.1 主机名、hosts 与 SSH 免密:顺序错了全白干

先把三台机器的主机名改掉,再把内网地址写进/etc/hosts,这一步必须在ssh-copy-id之前完成。文档里有个细节值得学:它是先各节点执行cat /root/.ssh/id_rsa.pub拿到公钥,再统一追加到其他节点的authorized_keys,这种做法在节点数量多时比ssh-copy-id更可控。

# 每台机器按自己的角色改主机名,以 node-0001 为例 hostnamectl set-hostname node-0001 # 三台都写入同一份 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 # 生成密钥对,-N "" 表示不设口令,集群免密必须空口令 ssh-keygen -t rsa -b 4096 -N "" -f /root/.ssh/id_rsa # 把本机公钥推到三台(含自己),实现双向免密 for h in node-0001 node-0002 node-0003; do ssh-copy-id -i /root/.ssh/id_rsa.pub root@$h done # 验证:三台之间任意方向都应该直接返回主机名,不提示输密码 ssh node-0002 hostname

-b 4096指密钥长度,默认 2048 也能用,4096 在 ARM 上生成稍慢但一次性的。-N ""是关键,设了口令的密钥在脚本里调用会卡在交互输入上,Hadoop 的启停脚本就死在那儿。

双向免密不是必须的,start-dfs.sh只需要 NameNode 能 ssh 到各 DataNode;但分发配置、scp拷包、日志收集这些日常操作都要反向,省这一步后面还会补回来。做完之后一定手动ssh到另外两台验一次,别只信ssh-copy-id的返回码。

提示:authorized_keys的权限必须是 600,家目录不能是 777,否则 sshd 会静默拒绝公钥认证,现象是「怎么都不免密但也不报错」。这是最典型的一个玄学坑。

3.2 安装包与对象存储插件的分发

Hadoop 2.8.3 的包解压到/home/modules后,程序目录是/home/modules/hadoop-2.8.3。除了主包,文档里提到的「jar 包」指的是让 Hadoop 能直接读写对象存储的插件。两条路可走:一是厂商提供的 Hadoop 插件,二是 Hadoop 自带的 s3a 协议栈。后者在社区版里是标配,只要把aws-java-sdk-bundle对应版本的 jar 补齐就能用,跨云迁移时改动最小。

cd /root tar -zxvf hadoop-2.8.3.tar.gz -C /home/modules/ export HADOOP_HOME=/home/modules/hadoop-2.8.3 # s3a 依赖 aws-java-sdk-bundle,版本要和 Hadoop 2.8.3 的 pom 对齐(1.11.x 系列) # 把 jar 同时放进 common/lib(运行时)和 tools/lib(distcp 等工具用) cp aws-java-sdk-bundle-*.jar $HADOOP_HOME/share/hadoop/common/lib/ cp aws-java-sdk-bundle-*.jar $HADOOP_HOME/share/hadoop/tools/lib/ # 确认 jar 带进来了 ls $HADOOP_HOME/share/hadoop/common/lib/ | grep -i s3a

如果走厂商插件路线,插件 jar 通常要同时拷到share/hadoop/common/lib/和share/hadoop/tools/lib/,少放一处就会出现「hdfs dfs -ls能用、hadoop distcp报找不到 FS」的诡异现象。

分发用rsync比scp -r好,能保留权限和属主,重复执行只传差异部分。我一般会在/usr/local/bin放一个几十行的脚本,避免每次手敲三行命令。

cat > /usr/local/bin/xsync <<'EOF' #!/bin/bash # 把文件或目录同步到其余两个节点,路径保持一致 if [ $# -lt 1 ]; then echo "usage: xsync <path>"; exit 1; fi for host in node-0002 node-0003; do rsync -rlptgoD "$1" "$host:$(dirname "$1")/" done EOF chmod +x /usr/local/bin/xsync # 用法:改完配置后一条命令推全网 xsync /home/modules/hadoop-2.8.3/etc/hadoop/

-rlptgoD这几个参数分别对应递归、保留软链接、保留权限、保留时间、保留属主属组、保留设备文件,分布式场景下这套组合基本是标配。少了-p会导致目标端的脚本丢了执行权限,start-dfs.sh直接报 Permission denied。

3.3 core-site.xml:HDFS 与对象存储两条路径

core-site.xml是这份文档里信息密度最高的一份配置。它同时要干两件事:告诉 Hadoop 默认文件系统是本地 HDFS,以及告诉它对象存储怎么连。很多人只写了前半部分,结果hdfs dfs -ls obs://bucket/报 UnknownFS。

<configuration> <!-- 默认文件系统指向本地 HDFS,NameNode 在 node-0001 的 9000 端口 --> <property> <name>fs.defaultFS</name> <value>hdfs://node-0001:9000</value> </property> <!-- 临时目录,MapReduce 和 HDFS 都会用,别放 /tmp,重启会被清 --> <property> <name>hadoop.tmp.dir</name> <value>/home/modules/data/tmp</value> </property> <!-- 对象存储 Endpoint,换成自己区域的地址;双栈环境慎用 --> <property> <name>fs.s3a.endpoint</name> <value>obs.<region-id>.example-cloud.com</value> </property> <!-- AK/SK 占位,实际复现时替换成 credentials.csv 里的值 --> <property> <name>fs.s3a.access.key</name> <value>AK_REPLACE_ME</value> </property> <property> <name>fs.s3a.secret.key</name> <value>SK_REPLACE_ME</value> </property> <!-- 关键:对象存储大多数走 path-style 寻址,不设会报 NoSuchBucket --> <property> <name>fs.s3a.path.style.access</name> <value>true</value> </property> </configuration>

fs.defaultFS决定hdfs dfs -ls /看的是谁,写错整个集群的数据路径就指到别处去了。hadoop.tmp.dir默认是/tmp/hadoop-${user},云主机重启后/tmp被清理,NameNode 的元数据如果放在下面会一起没,务必改到持久化目录。

fs.s3a.path.style.access这条最容易被忽略。对象存储的虚拟主机风格寻址在自建 S3 兼容服务上经常解析不到,报NoSuchBucket或者连接超时,改成 path-style 后立刻正常。fs.s3a.endpoint不要带https://前缀,Hadoop 2.8.3 的 s3a 会自己拼协议,带了会变成双协议头。

如果沿用文档里的厂商插件方案,配置项名称会变成fs.obs.endpoint、fs.obs.access.key这一套,fs.defaultFS里的 scheme 也要对应改成obs://。两种方案不要混用,混用会出现同一份配置里两个 FS 抢默认值的情况。

3.4 hdfs-site、yarn-site、mapred-site 与 slaves 的最小可用集

剩下三份配置的目标是「能跑起来且不吃满内存」。实验集群最容易犯的错是把默认值直接搬过来,YARN 按 8 GiB 分配容器,实际主机只有 8 GiB,跑第一个作业就被 OOM Killer 干掉了。

<!-- hdfs-site.xml:三节点环境副本数给 2,省盘又不至于一挂就丢 --> <configuration> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/home/modules/data/dfs/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/home/modules/data/dfs/data</value> </property> <property> <name>dfs.namenode.http-address</name> <value>node-0001:50070</value> </property> </configuration>
<!-- yarn-site.xml:内存压到 4096,单容器上限给 2048,避免小内存主机被压垮 --> <configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>node-0001</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.local-dirs</name> <value>/home/nm/localdir</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>4096</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>2048</value> </property> </configuration>
<!-- mapred-site.xml:指定跑在 YARN 上,并给全 classpath,否则 ApplicationMaster 起不来 --> <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>mapreduce.application.classpath</name> <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value> </property> </configuration>

dfs.replication=2是我在实验环境的常用值,三台机器给 3 也行,但 NameNode 还占着 node-0001,实际可用空间会紧。yarn.nodemanager.resource.memory-mb的取值经验是「物理内存减 2 GiB」,8 GiB 主机给 4096 偏保守但不会翻车。

slaves文件注意是 2.x 的写法,Hadoop 3.x 已经改成workers。内容只写主机名,一行一个,别带 IP,别写注释:

node-0001 node-0002 node-0003

mapreduce.application.classpath这一项如果漏了,现象是作业提交成功但 ApplicationMaster 反复重启,日志里刷Could not find or load main class。这是新手最容易卡住的地方,因为 ResourceManager 页面看起来一切正常。

3.5 格式化 NameNode 与启动顺序

格式化这一步只能对空的dfs/name目录做,做两次就会产生两个不同的 clusterID,DataNode 启动时报Incompatible clusterIDs直接退出。文档里因为 JAVA_HOME 错误反复格式化了几次,正好踩中这个坑。

cd /home/modules/hadoop-2.8.3 # 显式指定 clusterId,重复格式化也不会变,等于给自己留一颗后悔药 ./bin/hdfs namenode -format -clusterId cid-hadoop283-01 # 先起 HDFS,确认 DataNode 都注册上来,再起 YARN ./sbin/start-dfs.sh ./sbin/start-yarn.sh # 三台分别看进程,node-0001 应有 NameNode/SecondaryNameNode/ResourceManager jps

-clusterId参数在 2.8.3 已经支持,作用是把这个 ID 固定下来,后面哪怕再 format 一次,DataNode 的 VERSION 文件也能对上。这个参数知道的人不多,但能省掉「删数据目录重来」的整轮操作。

启动顺序建议 HDFS 先于 YARN。start-yarn.sh里的 NodeManager 启动时会去连 NameNode 解析主机名,HDFS 没起来时虽然也能起,但日志里会多出一堆连接超时,排查时容易被误导。启动完用hdfs dfsadmin -report看 DataNode 数量,三个都 Live 才算集群健康。

4. JAVA_HOME 连环翻车:aarch64 上的路径与生效顺序

这份文档里出现频率最高的词就是 JAVA_HOME。它前后错了三次,导致 Hadoop 包重新分发了三次、NameNode 格式化了两次、hdfs命令失败了一次。这一段单独拿出来讲,因为它是 ARM 环境下最容易翻车的地方,也是新手最不容易定位的地方。

4.1 aarch64 版 JDK 的目录名与 JAVA_HOME 的落点

x86 上装 OpenJDK,目录名一般是java-1.8.0-openjdk-1.8.0.xxx.el7.x86_64;换到鲲鹏上,同样的包变成了带aarch64后缀的名字,中间还可能夹着发行版标识。照着 x86 的教程抄路径,JAVA_HOME指到一个不存在的目录,表现就是java -version正常(因为 PATH 里有 java),但 Hadoop 启动时报JAVA_HOME is not set。

# 别猜路径,用这两条命令查真实位置 readlink -f "$(which java)" # 典型输出:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64/jre/bin/java ls -l /usr/lib/jvm/ # 目录列表里那个以 aarch64 结尾的就是 JDK 根目录

关键判断点:readlink出来的路径里带/jre/bin/java,而JAVA_HOME要填的是它上面那一级,即.../java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64,不带/jre。Hadoop 的脚本内部会拼$JAVA_HOME/bin/java,如果你把JAVA_HOME指到了jre子目录,拼出来就是.../jre/bin/java——等等,那反而对了。真正的问题在于某些发行版的jre目录只是个软链,/bin/java在 JDK 根下也可能不存在。安全做法是JAVA_HOME指 JDK 根,并验证$JAVA_HOME/bin/java -version和$JAVA_HOME/jre/bin/java -version都通。

提示:验证永远比记忆可靠。改完JAVA_HOME,先跑$JAVA_HOME/bin/java -version,有输出再往下走,别直接重启集群。

4.2 /etc/profile 改了为什么不生效

文档里最让人抓狂的一段是:/etc/profile明明改了,source也执行了,echo $JAVA_HOME在本机终端里是对的,但start-dfs.sh起 DataNode 时还是报 JAVA_HOME 未设置。原因是 shell 的加载规则——/etc/profile只在登录 shell(login shell)里被读取,而start-dfs.sh通过 ssh 远程执行命令时走的是非交互非登录 shell,根本不会读它。

配置文件生效场景对 Hadoop 是否够用
/etc/profile交互式登录 shell只影响手动敲命令
~/.bashrc交互式非登录 shell同上,脚本里不读
hadoop-env.shHadoop 所有启停脚本 source最终真正生效的地方
yarn-env.shYARN 相关进程需要单独设置

也就是说,/etc/profile是给人用的,hadoop-env.sh才是给 Hadoop 用的。正确做法是两处都写,但以hadoop-env.sh为准。

# 写进 hadoop-env.sh,这是 Hadoop 脚本真正读取的地方 HADOOP_HOME=/home/modules/hadoop-2.8.3 JDK_ROOT=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.h5.oe1.aarch64 # 用 sed 替换已有行,避免文件里出现两条 export JAVA_HOME 互相覆盖 sed -i "s|^export JAVA_HOME=.*|export JAVA_HOME=${JDK_ROOT}|" \ $HADOOP_HOME/etc/hadoop/hadoop-env.sh grep -n "JAVA_HOME" $HADOOP_HOME/etc/hadoop/hadoop-env.sh

用sed而不是直接echo >>追加,是因为hadoop-env.sh里本来就有一行被注释掉的export JAVA_HOME,追加会让文件里出现两行有效赋值,后写的那行赢——这时候你改了半天发现没生效,就是被后面的覆盖了。

4.3 改完配置后的正确重来顺序

JAVA_HOME 改对之后,不需要重装 Hadoop,但必须按固定顺序重来一遍,顺序错了等于白改。文档里的做法是分发、清目录、重新格式化,顺序基本正确,缺的是「先清哪、后清哪」的明确说法。

# 1. 改好的 hadoop-env.sh 推到所有节点 xsync /home/modules/hadoop-2.8.3/etc/hadoop/hadoop-env.sh # 2. 停掉所有残留进程(停不掉就 kill,进程名带 Hadoop 的都清) $HADOOP_HOME/sbin/stop-yarn.sh $HADOOP_HOME/sbin/stop-dfs.sh ps -ef | grep -E "namenode|datanode|resourcemanager|nodemanager" | grep -v grep # 3. 清空旧数据目录,旧的 VERSION 文件里存着上一次的 clusterID rm -rf /home/modules/data/dfs/name/* /home/modules/data/dfs/data/* rm -rf /home/nm/localdir/* # 4. 重新格式化并启动,clusterId 保持和上次一致 $HADOOP_HOME/bin/hdfs namenode -format -clusterId cid-hadoop283-01 $HADOOP_HOME/sbin/start-dfs.sh $HADOOP_HOME/sbin/start-yarn.sh

第 3 步的清理很多人只清name不清data,结果 NameNode 起来了,DataNode 还是因为 clusterID 对不上被拒绝注册,hdfs dfsadmin -report里 Live 数量是 0。两处一起清是最省事的做法,反正实验环境没有真实数据。

第 4 步的格式化只在name目录为空时是安全的。如果目录里有内容,NameNode 会提示Re-format filesystem in Storage Directory ... ?,输Y也能过,但 DataNode 那边必须同步清,否则又是 clusterID 问题。养成「格式化前先清目录」的习惯,比背报错信息可靠。

5. 排查清单:五类高频翻车与定位手法

前面几章是按「正确路径」讲的,这一章反过来,按「出错现象」组织。每一条都是我在鲲鹏环境上真实遇到过的,现象、原因、解决按顺序写,遇到时直接对号入座。

5.1 native-hadoop 库告警刷屏

现象:start-dfs.sh之后日志里反复出现WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable,DataNode 也能起来,但每次看日志都觉得有问题。

原因:Hadoop 官方二进制包里的lib/native/libhadoop.so是在 x86_64 上编译的,aarch64 上加载失败后回落到纯 Java 实现。这不影响 HDFS 读写和 MapReduce,只影响压缩(zlib/snappy 的 native 加速)和 CRC 校验的性能。

解决:实验环境直接忽略,把日志级别调高即可;要真消除,得在鲲鹏上重新编译 Hadoop native 库,耗时半小时以上,收益是压缩性能提升。我的做法是在hadoop-env.sh里加一行export HADOOP_ROOT_LOGGER=INFO,console把 WARN 过滤掉,眼不见心不烦。

5.2 NameNode 格式化两次后 DataNode 起不来

现象:NameNode 的 Web UI 能打开,hdfs dfsadmin -report里 Live datanodes 是 0,node-0002/0003 上jps看不到 DataNode 进程,日志尾部是java.io.IOException: Incompatible clusterIDs in /home/modules/data/dfs/data。

原因:每次hdfs namenode -format都会生成一个新的 clusterID 写进 NameNode 的 VERSION 文件,DataNode 的 VERSION 里还是旧的,启动时校验不通过就自杀退出。

解决:停掉所有进程,rm -rf清空dfs/name和dfs/data两个目录,重新format一次再启动。想避免复发就在 format 时带-clusterId参数固定 ID。这条坑在文档里出现过两次,就是因为第一次没清 data 目录。

5.3 免密只做了单向,脚本卡住不动

现象:在 node-0001 上手动ssh node-0002不用密码,但start-dfs.sh执行到一半停住,没有任何输出,等几分钟后报Starting datanodes失败。

原因:免密只配了 node-0001 到其他节点,其他节点回连 node-0001 还要密码;或者authorized_keys权限不对被 sshd 静默拒绝。

解决:三台互推一次公钥,用for循环保证双向;再检查/root/.ssh是 700、authorized_keys是 600。验证方法是每台机器上执行ssh node-0001 hostname、ssh node-0002 hostname、ssh node-0003 hostname,九个方向全通才算完。

5.4 对象存储能读不能写,或者 rename 报错

现象:hdfs dfs -ls obs://bucket/正常列出文件,但hdfs dfs -put上传报Access Denied,或者 MapReduce 作业把输出目录指到对象存储时卡在 commit 阶段。

原因有两个层面。权限层面是子用户的桶策略只给了读;语义层面是对象存储本身不支持原子 rename,Hadoop 的FileOutputCommitter依赖 rename 来提交结果,在对象存储上会退化成逐文件拷贝,小文件多时慢到不可接受。

解决:权限问题去控制台把桶策略补成读写;语义问题则不要在对象存储上直接跑作业输出,把mapreduce.output.fileoutputformat.outputdir指回 HDFS,作业跑完再用distcp搬到桶里。这条经验比任何配置项都值钱。

5.5 容器被 OOM Killer 杀掉

现象:作业提交成功,ApplicationMaster 正常,但 task 跑到一半变成Container killed on request. Exit code is 137,NodeManager 日志里有Killing container ... because it is running beyond physical memory limits。

原因:yarn.nodemanager.resource.memory-mb用了默认值,主机实际内存小于 YARN 认为可分配的量,容器一起来就被内核干掉。Exit code 137 就是 SIGKILL 的编号。

解决:按「物理内存减 2 GiB」重设yarn.nodemanager.resource.memory-mb,并把yarn.scheduler.maximum-allocation-mb压到单容器上限以内,同时检查mapreduce.map.memory.mb和mapreduce.reduce.memory.mb是否超过这个上限。三个值是一套的,只改一个没用。

6. 交付前跑一遍:一份三十行的集群自检脚本

集群起来不等于可用。我习惯在交付或截图交报告之前跑一遍自检,把「进程在、端口通、副本齐、作业能跑」四件事一次性验完,避免演示时当场翻车。

#!/bin/bash # cluster_check.sh —— 三节点 Hadoop 2.8.3 集群自检 HADOOP_HOME=/home/modules/hadoop-2.8.3 FAIL=0 check() { # $1=描述 $2=命令 if eval "$2" >/dev/null 2>&1; then echo "[ OK ] $1"; else echo "[FAIL] $1"; FAIL=1; fi } check "HDFS 可访问" "$HADOOP_HOME/bin/hdfs dfs -ls /" check "DataNode 数量为 3" "test \$($HADOOP_HOME/bin/hdfs dfsadmin -report | grep -c 'Live datanodes') -eq 1" check "YARN 节点可用" "yarn node -list | grep -q RUNNING" check "写入并读回测试" "$HADOOP_HOME/bin/hdfs dfs -put /etc/hosts /tmp/hosts.bak && $HADOOP_HOME/bin/hdfs dfs -cat /tmp/hosts.bak" check "对象存储列举" "$HADOOP_HOME/bin/hdfs dfs -ls s3a://REPLACE_BUCKET/" exit $FAIL

脚本里check函数用eval执行传入的命令,成功打 OK、失败打 FAIL 并置退出码,最后exit $FAIL让 CI 或人工一眼看出整体结果。第二条判断写得取巧——hdfs dfsadmin -report的输出里Live datanodes (3)只出现一次,所以用grep -c计数再和 1 比,比解析数字简单。第五条需要把REPLACE_BUCKET换成自己的桶名,没配对象存储就把这行删掉。

跑完这五条,再补一个真实的 MapReduce 作业验证计算链路:

# 用自带示例跑一个 Pi 估算,能出结果说明 YARN + MapReduce 全链路通 $HADOOP_HOME/bin/hadoop jar \ $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.3.jar \ pi 2 100

pi 2 100表示 2 个 map、每个采样 100 个点,几十秒内应该输出一个接近 3.14 的值。这个例子不依赖输入数据,是最干净的链路验证。如果它跑不通而 HDFS 一切正常,问题一定在 YARN 的内存配置或mapreduce.application.classpath上,按第 5.5 条对号入座即可。

血泪经验是:这五条自检我一开始只写了两条,觉得进程在就行。后来一次演示,jps三个进程全绿,结果hdfs dfs -put因为 DataNode 磁盘满直接失败,当场下不来台。从那以后我每次搭完集群,都强制把这份脚本从头到尾走一遍,一条不过就不算交付。这份实验文档里保留的报错记录和修错顺序,正好可以拿来对照着排,比自己从零试错快得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询