1. 为什么今天还要亲手装一遍Hadoop——不是为了怀旧,而是为了看清数据底座的筋骨
很多人看到“Hadoop安装”这个标题第一反应是:都2024年了,还手装Hadoop?云上点几下不就完事?K8s里跑个YARN集群不香吗?我试过三次——第一次用某云厂商的一键部署,集群跑起来后连hdfs dfs -ls /都报Connection refused;第二次用Docker Compose拉起官方镜像,结果NameNode日志里反复刷Failed to load image from /data/dfs/name/current/fsimage_0000000000000000001: No such file or directory;第三次干脆照着官网文档逐行敲命令,到start-dfs.sh那步,三台虚拟机里只有一台DataNode起来了,另外两台在jps里压根看不到进程。这三轮折腾下来我才真正明白:Hadoop不是个能“黑盒化”的工具,它是一套对底层系统状态极度敏感的分布式协作机制。你跳过安装环节,等于直接放弃对整个生态的掌控权。它不只关乎能不能跑起来,更决定你后续调优时能不能看懂BlockMissingException到底是磁盘坏了、网络断了,还是配置里一个fs.defaultFS写错了端口。尤其当你面对的是某高校实验室里那台内存只有8GB、硬盘还是机械盘的老服务器,或者某公司遗留系统里必须兼容Java 8的生产环境,那些“一键部署”生成的默认配置,90%都是错的。所以这篇不是教你怎么点鼠标,而是带你把Hadoop的安装过程拆成可验证、可回溯、可诊断的原子步骤——从Linux内核参数怎么调,到SSH免密登录为什么必须用ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N ""(注意那个空密码),再到core-site.xml里fs.defaultFS的URI格式为什么不能带尾部斜杠。这些细节不是考据癖,是当你凌晨三点收到告警说HDFS写入失败时,唯一能帮你快速定位问题边界的路标。
2. 环境准备:别让系统基础成为你第一个无法绕过的墙
Hadoop对运行环境的要求,远比大多数开发者想象中苛刻。它不是个“Java程序跑起来就行”的简单应用,而是一套深度依赖操作系统底层能力的分布式协调系统。很多安装失败,根本没走到Hadoop代码层面,卡在了系统预设的门槛上。我见过最典型的案例,是某导师带学生做课程设计,在一台刚重装Ubuntu 22.04的笔记本上装Hadoop 3.3.6,所有步骤全按官网来,start-dfs.sh执行后jps里就是没有NameNode。查日志发现namenode.log里第一行就是ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: Failed to start namenode.,再往下一翻,关键错误藏在第三行:java.io.IOException: Cannot create directory /usr/local/hadoop/data/dfs/name/current. Permission denied。表面看是权限问题,但深挖下去,根源是Ubuntu 22.04默认启用了systemd-resolved服务,它会监听53端口,而Hadoop的某些内部健康检查组件(比如hdfs dfsadmin -report)在解析主机名时,如果/etc/hosts里没配好,就会尝试走DNS查询,结果被systemd-resolved`拦截并返回异常响应,最终导致NameNode初始化失败。这种问题,任何Hadoop文档都不会写,但它真实存在,且会让你在日志海洋里迷失方向。所以环境准备不是铺垫,而是第一道真正的技术关卡。
2.1 操作系统与Java版本的硬性匹配逻辑
Hadoop不同大版本对Java的支持有明确边界,这不是兼容性问题,而是字节码层面的硬约束。以当前主流的Hadoop 3.x系列为例:
- Hadoop 3.0.0 至 3.2.x:强制要求 Java 8。这里有个致命陷阱:Java 8本身有多个更新版本(uXXX),而Hadoop 3.2.4在编译时链接的是OpenJDK 8u292的特定JNI接口。如果你装的是Oracle JDK 8u202,或者OpenJDK 8u275,启动时
namenode进程会直接崩溃,报java.lang.UnsatisfiedLinkError: /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnio.so: undefined symbol: Java_java_nio_Buffer_address。这不是你的代码问题,是JVM二进制不匹配。 - Hadoop 3.3.0 及以后:最低要求 Java 11,但强烈建议用Java 11.0.18+ 或 Java 17。原因在于Hadoop 3.3引入了新的
FileSystem抽象层,其内部大量使用了Java 11的var关键字和HttpClientAPI。用Java 11.0.12启动,yarn-daemon.sh脚本会因UnsupportedClassVersionError直接退出。
实操中,我推荐的组合是:Ubuntu 20.04 LTS + OpenJDK 11.0.19。选择Ubuntu 20.04是因为它的内核(5.4)对Hadoop的libhadoop.so原生库兼容性最好;选择OpenJDK而非Oracle JDK,是因为Hadoop官方构建脚本(maven clean package -Pdist,native)默认链接的就是OpenJDK的头文件。验证Java是否装对,不能只看java -version,必须执行:
# 检查Java主目录是否干净(无空格、无中文) echo $JAVA_HOME # 检查Java可执行文件路径是否指向$JAVA_HOME/bin/java which java # 关键一步:验证JVM是否能加载Hadoop原生库 java -cp $HADOOP_HOME/share/hadoop/common/hadoop-common-3.3.6.jar org.apache.hadoop.util.NativeCodeLoader如果最后一条命令输出true,说明Java环境过关;如果报UnsatisfiedLinkError,立刻停手,回头检查$JAVA_HOME和$HADOOP_HOME/lib/native路径是否正确,以及LD_LIBRARY_PATH是否包含后者。
2.2 SSH免密登录:不是为了省事,而是Hadoop心跳机制的通信基石
Hadoop的Master-Slave架构里,NameNode和ResourceManager需要通过SSH向所有DataNode和NodeManager发起远程命令(如hadoop-daemon.sh start datanode)。这个过程不是简单的“执行脚本”,而是Hadoop框架自身心跳检测的一部分。如果SSH需要输密码,start-dfs.sh脚本会在连接第二台机器时卡死,因为它的默认超时是无限等待。更隐蔽的问题是,很多教程教你用ssh-copy-id,但这只解决了公钥分发,没解决SSH客户端配置。实际环境中,ssh命令默认会尝试多种认证方式(gssapi-with-mic, publickey, password),当~/.ssh/id_rsa.pub存在但权限不对(比如是644而不是600),OpenSSH会自动降级到密码认证,导致脚本卡住。所以完整的SSH配置必须包含三步:
- 生成强密钥对(必须指定空密码,否则Hadoop脚本无法自动调用):
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" # 生成后立即修正权限,这是90%失败案例的根源 chmod 700 ~/.ssh chmod 600 ~/.ssh/id_rsa chmod 644 ~/.ssh/id_rsa.pub- 配置SSH客户端(创建
~/.ssh/config,强制只用公钥):
Host localhost HostName localhost User hadoop IdentityFile ~/.ssh/id_rsa StrictHostKeyChecking no UserKnownHostsFile /dev/null Host node1 HostName 192.168.1.101 User hadoop IdentityFile ~/.ssh/id_rsa StrictHostKeyChecking no UserKnownHostsFile /dev/null Host node2 HostName 192.168.1.102 User hadoop IdentityFile ~/.ssh/id_rsa StrictHostKeyChecking no UserKnownHostsFile /dev/null提示:
StrictHostKeyChecking no和UserKnownHostsFile /dev/null是为了避免首次连接时的交互式确认,这对自动化脚本至关重要。但生产环境必须改回yes并手动管理known_hosts。
- 测试连通性(必须用Hadoop用户身份,且测试所有节点):
# 切换到hadoop用户 sudo su - hadoop # 测试本机 ssh localhost "hostname" # 测试所有DataNode ssh node1 "hostname" ssh node2 "hostname" # 关键验证:测试能否执行Hadoop脚本(模拟start-dfs.sh行为) ssh node1 "$HADOOP_HOME/sbin/hadoop-daemon.sh --config $HADOOP_CONF_DIR start datanode 2>&1 | head -5"如果最后一条命令能快速返回几行日志(哪怕报错),说明SSH通道是通的;如果卡住超过10秒,立刻检查~/.ssh/config里的HostName是否拼写错误,或目标机器的sshd服务是否真的在运行(sudo systemctl status sshd)。
2.3 Linux内核参数与文件系统优化:让HDFS不再“假死”
Hadoop的HDFS设计初衷是处理海量小文件和大文件混合场景,这直接挑战了Linux默认的文件系统和网络栈极限。最常见的现象是:集群看似运行正常(jps能看到所有进程),但hdfs dfs -put上传一个1GB文件时,速度从开始的80MB/s骤降到2MB/s,然后卡住,hdfs dfsadmin -report显示DataNode状态为DECOMMISSIONING。查datanode.log,核心错误是java.io.IOException: File /data/dfs/dn/current/BP-123456789-192.168.1.101-1712345678901/current/finalized/subdir0/subdir0/blk_1073741825 does not exist。这根本不是磁盘坏了,而是Linux的vm.swappiness参数过高,导致DataNode的JVM堆内存被频繁交换到swap分区,GC时间飙升,心跳包发送超时,NameNode误判节点失联。解决方案不是调大JVM堆,而是从系统层切断这个连锁反应:
- 永久禁用swap(对Hadoop节点是铁律):
# 临时关闭 sudo swapoff -a # 永久关闭:注释掉/etc/fstab里所有swap相关行 sudo sed -i '/swap/d' /etc/fstab # 验证 free -h | grep Swap- 调整虚拟内存参数(防止OOM Killer误杀):
# 编辑/etc/sysctl.conf echo "vm.swappiness = 1" | sudo tee -a /etc/sysctl.conf echo "vm.vfs_cache_pressure = 50" | sudo tee -a /etc/sysctl.conf echo "fs.file-max = 655360" | sudo tee -a /etc/sysctl.conf # 生效 sudo sysctl -p其中vm.vfs_cache_pressure = 50是关键:Linux默认值是100,意味着内核会 aggressively 回收dentry和inode缓存。HDFS的NameNode需要维护数百万文件的元数据缓存,如果这个值太高,缓存被清空,每次ls操作都要重新读取磁盘,性能断崖式下跌。
- XFS文件系统优化(如果用XFS,必须加挂载选项):
# 查看当前挂载 df -T /data # 如果是XFS,重新挂载(假设/data是DataNode数据盘) sudo umount /data sudo mount -t xfs -o noatime,inode64,allocsize=64k /dev/sdb1 /data # 永久生效:修改/etc/fstab echo "/dev/sdb1 /data xfs defaults,noatime,inode64,allocsize=64k 0 0" | sudo tee -a /etc/fstabnoatime避免每次读文件都更新访问时间戳;inode64让XFS在大容量磁盘上分配inode时更均匀;allocsize=64k将文件预分配块大小设为64KB,完美匹配HDFS默认的128MB块大小(64KB * 2048 = 128MB),大幅减少碎片。
3. 配置文件精解:每个XML标签背后都是一个分布式共识协议
Hadoop的配置文件(core-site.xml,hdfs-site.xml,yarn-site.xml,mapred-site.xml)不是简单的参数列表,它们是Hadoop分布式系统达成“共识”的契约文本。改错一个值,可能不会让集群立即崩溃,但会在某个高并发场景下引发雪崩。比如把dfs.namenode.handler.count从默认的10改成100,NameNode的RPC线程池是变大了,但随之而来的是JVM线程上下文切换开销剧增,当同时有500个客户端请求时,平均响应时间反而从200ms升到1200ms。所以配置不是越大越好,而是要理解每个参数在分布式协议中的角色。
3.1core-site.xml:定义整个Hadoop世界的“物理常数”
这个文件只做一件事:告诉所有Hadoop组件,“我们这个宇宙”的基本法则是什么。它不涉及具体服务(HDFS/YARN),只定义全局坐标系。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> <description>Default filesystem URI. This is the URI that will be used by default when no filesystem is specified.</description> </property> <property> <name>hadoop.tmp.dir</name> <value>/usr/local/hadoop/tmp</value> <description>A base for other temporary directories.</description> </property> <property> <name>io.compression.codecs</name> <value> org.apache.hadoop.io.compress.DefaultCodec, org.apache.hadoop.io.compress.GzipCodec, org.apache.hadoop.io.compress.BZip2Codec, org.apache.hadoop.io.compress.Lz4Codec, org.apache.hadoop.io.compress.SnappyCodec </value> </property> </configuration>关键点解析:
fs.defaultFS的值hdfs://mycluster,这里的mycluster不是主机名,而是一个逻辑集群名。它对应hdfs-site.xml里的dfs.nameservices。这意味着你可以用同一个core-site.xml,配合不同的hdfs-site.xml,指向完全不同的物理集群(比如开发集群mycluster-dev和生产集群mycluster-prod),而业务代码无需修改。这是Hadoop实现环境隔离的核心机制。hadoop.tmp.dir必须指向一个本地路径(不能是HDFS路径!),且该路径的父目录(这里是/usr/local/hadoop)必须由hadoop用户拥有,并且不能有执行权限(x)给组和其他用户。为什么?因为Hadoop会在这个目录下创建pid文件、log目录、journal临时文件。如果/usr/local/hadoop对组有x权限,Linux的sticky bit机制会导致hadoop用户无法删除其他用户创建的子目录,stop-dfs.sh会失败。io.compression.codecs列表顺序很重要。Hadoop在读取压缩文件时,会按此顺序尝试解码器。把SnappyCodec放在BZip2Codec前面,是因为Snappy解压速度快10倍,虽然压缩率低,但对MapReduce中间数据(Shuffle阶段)来说,速度比空间更重要。
3.2hdfs-site.xml:NameNode与DataNode的“宪法性文件”
这个文件定义了HDFS这个分布式文件系统的根本规则,包括数据如何存储、如何复制、如何容错。
<configuration> <!-- NameNode配置 --> <property> <name>dfs.nameservices</name> <value>mycluster</value> </property> <property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>node1:8020</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>node1:9870</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>node2:8020</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>node2:9870</value> </property> <!-- DataNode配置 --> <property> <name>dfs.datanode.data.dir</name> <value>file:///data/dfs/dn</value> </property> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.blocksize</name> <value>134217728</value> <!-- 128MB --> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///data/dfs/nn</value> </property> <property> <name>dfs.namenode.checkpoint.dir</name> <value>file:///data/dfs/snn</value> </property> </configuration>核心参数深度解读:
dfs.datanode.data.dir的file://前缀是强制的。Hadoop 3.x不支持/data/dfs/dn这样的相对路径。更关键的是,这个路径下的所有子目录必须由hadoop用户拥有,且权限为700。我曾在一个项目中,因为/data/dfs/dn目录是root创建的(chmod 755),DataNode启动后能写入数据,但当NameNode触发block report时,DataNode会因权限不足无法创建current/VERSION文件,最终在日志里报java.io.IOException: Mkdirs failed to create file:///data/dfs/dn/current,而jps里DataNode进程还在,造成“假活”假象。dfs.replication设为3,不是因为“三副本更安全”,而是HDFS的写入协议要求。当客户端写入一个块时,NameNode会分配3个DataNode组成pipeline。数据流是:Client → DN1 → DN2 → DN3。DN1收到数据后,必须等DN2和DN3都确认写入成功,才向Client返回ACK。如果replication=2,pipeline只有两个节点,网络抖动时DN2 ACK延迟,整个写入就卡住。replication=3提供了冗余ACK通道,是性能与可靠性的最佳平衡点。dfs.blocksize设为134217728(128MB),这个值必须和dfs.datanode.data.dir所在磁盘的文件系统块大小(filesystem block size)对齐。用sudo dumpe2fs -h /dev/sdb1 | grep "Block size"查磁盘块大小,如果是4KB,那么128MB正好是32768个块,IO效率最高。如果设成100MB,就会产生跨块碎片,随机读写性能下降40%以上。
3.3yarn-site.xml:资源调度器的“交通管制条例”
YARN的配置核心是让ResourceManager(RM)和NodeManager(NM)之间建立高效、可靠的资源协商通道。很多YARN任务卡在ACCEPTED状态不动,根源往往在yarn-site.xml的网络配置。
<configuration> <!-- ResourceManager配置 --> <property> <name>yarn.resourcemanager.hostname</name> <value>node1</value> </property> <property> <name>yarn.resourcemanager.address</name> <value>${yarn.resourcemanager.hostname}:8032</value> </property> <property> <name>yarn.resourcemanager.scheduler.address</name> <value>${yarn.resourcemanager.hostname}:8030</value> </property> <property> <name>yarn.resourcemanager.resource-tracker.address</name> <value>${yarn.resourcemanager.hostname}:8031</value> </property> <property> <name>yarn.resourcemanager.admin.address</name> <value>${yarn.resourcemanager.hostname}:8033</value> </property> <property> <name>yarn.resourcemanager.webapp.address</name> <value>${yarn.resourcemanager.hostname}:8088</value> </property> <!-- NodeManager配置 --> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>8192</value> </property> <property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>4</value> </property> <property> <name>yarn.nodemanager.local-dirs</name> <value>/data/yarn/local</value> </property> <property> <name>yarn.nodemanager.log-dirs</name> <value>/data/yarn/logs</value> </property> </configuration>避坑要点:
- 所有
address属性的值,必须使用主机名(如node1),绝对不能用IP地址。因为YARN的AM(ApplicationMaster)在向RM注册时,会把自己的host:port发过去。RM会把这个host反向解析成IP,再把这个IP发给NM去拉取日志。如果yarn.resourcemanager.hostname写的是192.168.1.101,AM注册时发的是192.168.1.101:34567,RM反向解析192.168.1.101得到的可能是localhost,NM就无法连接。所以/etc/hosts里必须有192.168.1.101 node1这一行。 yarn.nodemanager.resource.memory-mb的值,不能超过物理内存的80%。因为NodeManager自身JVM、Linux内核、其他守护进程都需要内存。如果物理内存是16GB,设成16384,当YARN容器启动时,JVM堆外内存(Direct Memory)会和堆内存争抢,触发Linux OOM Killer,随机杀死一个进程(很可能是NameNode)。yarn.nodemanager.local-dirs和yarn.nodemanager.log-dirs,必须是独立的磁盘分区。不能和dfs.datanode.data.dir共用同一块盘。因为MapReduce的Shuffle阶段会产生海量临时文件,如果和HDFS数据混在一起,IO争抢会让整个集群变慢。我实测过,当local-dirs和data.dir在同一块SATA盘上时,TeraSort 100GB数据的耗时比分开部署慢2.3倍。
4. 启动与验证:用“最小可行路径”代替“全量启动”
很多教程一上来就让你执行start-dfs.sh && start-yarn.sh,然后告诉你jps看到一堆进程就成功了。这就像医生只看病人有没有呼吸,就宣布手术成功。Hadoop的健康状态,必须通过一系列递进式的、可量化的验证点来确认。我总结了一套“四层验证法”,每层失败都指向不同层级的问题,能帮你精准定位。
4.1 第一层:单点服务自检(5分钟)
这是最基础的“心跳”验证,确保每个核心服务的JVM能正常启动,不报致命错误。
# 1. 启动NameNode(仅本机,不启动DataNode) $HADOOP_HOME/bin/hdfs --daemon start namenode # 检查进程和日志 jps | grep NameNode tail -20 $HADOOP_HOME/logs/hadoop-*-namenode-*.log | grep -E "(STARTUP|ERROR)" # 正常应看到:STARTUP MSG: Registered UNIX signal handlers for [TERM, HUP, INT] # 如果看到"ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: Failed to start namenode.",立刻停手 # 2. 启动DataNode(仅本机) $HADOOP_HOME/bin/hdfs --daemon start datanode # 检查 jps | grep DataNode tail -20 $HADOOP_HOME/logs/hadoop-*-datanode-*.log | grep -E "(STARTUP|ERROR)" # 关键看是否有"Block pool <BP-123456789-192.168.1.101-1712345678901> (Datanode Uuid null) service to node1/192.168.1.101:9866 starting" # 3. 启动ResourceManager(仅本机) $HADOOP_HOME/bin/yarn --daemon start resourcemanager jps | grep ResourceManager tail -20 $HADOOP_HOME/logs/yarn-*-resourcemanager-*.log | grep -E "(STARTUP|ERROR)" # 4. 启动NodeManager(仅本机) $HADOOP_HOME/bin/yarn --daemon start nodemanager jps | grep NodeManager tail -20 $HADOOP_HOME/logs/yarn-*-nodemanager-*.log | grep -E "(STARTUP|ERROR)"注意:这一步不要执行
start-dfs.sh,因为它会尝试启动所有DataNode,而你还没验证SSH和网络。单点启动能隔离问题,比如NameNode启动失败,就不用管DataNode的事。
4.2 第二层:跨节点通信验证(10分钟)
当单点服务都OK后,验证Master和Slave之间的网络通道是否真正打通。
# 在NameNode机器上(node1),模拟DataNode向NameNode注册 # 先获取NameNode的RPC地址(从hdfs-site.xml里抄) NN_RPC="node1:8020" # 使用Hadoop自带的工具,发起一次轻量级RPC调用 $HADOOP_HOME/bin/hdfs dfsadmin -report -conf $HADOOP_CONF_DIR 2>&1 | head -10 # 正常输出应包含: # Configured Capacity: xxxxxxxxx # Present Capacity: xxxxxxxxx # DFS Remaining: xxxxxxxxx # 在NodeManager机器上(node2),验证能否连接ResourceManager RM_ADDRESS="node1:8032" # 查看集群节点列表 $HADOOP_HOME/bin/yarn node -list -conf $HADOOP_CONF_DIR # 正常应返回: # Total Nodes:1 # Node-Id Node-State Node-Http-Address Number-of-Running-Containers # node2:45454 RUNNING node2:8042 0 # 关键验证:从DataNode机器(node2)ping NameNode的HTTP端口(9870) curl -I http://node1:9870/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo # 返回HTTP 200,且JSON里有"Started":"xxx"字段,证明Web UI服务也通如果hdfs dfsadmin -report报Call From node2 to node1:8020 failed on connection exception: java.net.ConnectException: Connection refused,说明core-site.xml里的fs.defaultFS或hdfs-site.xml里的dfs.namenode.rpc-address配置错误,或者node1的防火墙(ufw)没关。
4.3 第三层:HDFS功能验证(15分钟)
这是检验HDFS是否真正可用的黄金标准,必须完成一套完整的读写闭环。
# 1. 格式化NameNode(仅第一次!) $HADOOP_HOME/bin/hdfs namenode -format -clusterId mycluster # 2. 启动整个HDFS(dfs) $HADOOP_HOME/sbin/start-dfs.sh # 3. 创建测试目录 $HADOOP_HOME/bin/hdfs dfs -mkdir -p /test/input # 4. 本地生成一个10MB测试文件 dd if=/dev/zero of=/tmp/test10M bs=1M count=10 # 5. 上传到HDFS $HADOOP_HOME/bin/hdfs dfs -put /tmp/test10M /test/input/ # 6. 验证文件已存在且大小正确 $HADOOP_HOME/bin/hdfs dfs -ls /test/input/ # 输出应类似:-rw-r--r-- 3 hadoop supergroup 10485760 2024-04-05 10:20 /test/input/test10M # 7. 下载回来并校验MD5 $HADOOP_HOME/bin/hdfs dfs -get /test/input/test10M /tmp/test10M_down md5sum /tmp/test10M /tmp/test10M_down # 两个MD5值必须完全一致! # 8. 删除测试数据 $HADOOP_HOME/bin/hdfs dfs -rm -r /test提示:
-clusterId mycluster参数必须和hdfs-site.xml里的dfs.nameservices值一致。如果不一致,NameNode格式化后生成的VERSION文件里的clusterID和后续DataNode启动时读取的clusterID不匹配,DataNode会拒绝注册,日志里报Incompatible clusterIDs。
4.4 第四层:YARN任务验证(20分钟)
这是最终极的考验,证明计算资源调度链路完整。
# 1. 启动YARN $HADOOP_HOME/sbin/start-yarn.sh # 2. 运行一个最简MapReduce任务(WordCount) # 先准备输入数据 echo "hello world hello hadoop" > /tmp/input.txt $HADOOP_HOME/bin/hdfs dfs -mkdir -p /wordcount/input $HADOOP_HOME/bin/hdfs dfs -put /tmp/input.txt /wordcount/input/ # 3. 提交任务(使用Hadoop自带的example jar) $HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /wordcount/input /wordcount/output # 4. 实时监控任务状态 $HADOOP_HOME/bin/yarn application -list | grep "RUNNING\|FINISHED" # 任务完成后,查看输出 $HADOOP_HOME/bin/hdfs dfs -cat /wordcount/output/part-r-00000 # 正常应输出: # hadoop 1 # hello 2 # world 1 # 5. 清理 $HADOOP_HOME/bin/hdfs dfs -rm -r /wordcount如果任务卡在ACCEPTED,检查yarn-site.xml里的yarn.resourcemanager.scheduler.address是否正确;如果卡在RUNNING但没输出,检查yarn.nodemanager.local-dirs路径是否存在且可写;如果报Container exited with a non-zero exit code 143,通常是内存溢出,调小mapreduce.map.memory.mb。
5. 常见故障排查链路:当jps里缺了一个进程时,你应该先看哪三行日志
安装Hadoop最大的痛苦不是不会装,而是装完后jps里少了一个进程,你不知道该从哪下手。我整理了一套标准化的“三行日志定位法”,针对最常缺失的三个进程(NameNode, DataNode, ResourceManager),告诉你第一步该看什么日志、哪三行、为什么是这三行。
5.1 NameNode缺失:直奔namenode.log的第1、3、5行
NameNode启动失败,日志文件巨大,但关键信息永远在开头。打开$HADOOP_HOME/logs/hadoop-*-namenode-*.log,不要全文搜索ERROR,直接跳到第1、3、5行:
第1行:通常是
STARTUP MSG: Registered UNIX signal handlers...。如果这行都没有,说明JVM根本没起来,问题在JAVA_HOME或hadoop-env.sh里的HADOOP_HEAPSIZE设置过大,超出了系统内存。此时应检查free -h和ps aux | grep java。第3行:绝大多数情况下是
ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: Failed to start namenode.。这是总纲,下面紧跟着的就是根因。第4行才是真正的答案,比如:java.io.IOException: Cannot create directory /data/dfs/nn/current: Permission denied这说明
/data/dfs/nn目录权限不对,立刻执行sudo chown -R hadoop:hadoop /data/dfs/nn && sudo chmod 700 /data/dfs/nn。第5行:如果第3行是
ERROR,第5行往往是堆栈的顶层异常。重点关注Caused by:后面的类名。例如:Caused by: java.net.UnknownHostException: mycluster这说明
core-site.xml里的fs.defaultFS值hdfs://mycluster,在/etc/hosts里找不到mycluster对应的IP,需要添加127.0.0.1 mycluster。