☰
Hadoop 2.6.5实战指南:伪分布式搭建、HA高可用与distcp迁移
2026/10/6 5:05:46 网站建设 项目流程

简介:Hadoop 2.6.5 发行包是一份面向大数据平台搭建者的完整安装资源,用于在 Linux 环境下部署分布式存储与计算框架。压缩包共有 900 个文件,大小约 175.09MB,其中 477 个 jar 文件提供核心库与依赖,129 个 class 文件保存编译后的执行类,51 个 xml 为配置模板,50 个 sh 为管理脚本,另含 Web 监控页面所需的 html、css、js 等内容,结构清晰易于检索。目前已有 1267 人学习下载,常被用于单机伪分布式部署或小型集群实验。解压即可获得完整目录,借助自带配置与脚本可以快速启动服务,并通过示例程序验证 HDFS 文件存储和 MapReduce 并行计算流程;同时也能参照包内组件梳理资源调度与容错机制,适合自学、课程设计及生产环境预研阶段使用。整份安装包既有开箱即用的可执行内容,也保留了便于二次修改的配置细节,能支撑从简单入门到集群调优的完整学习链条。

1. 为什么还在下 hadoop-2.6.5.tar.gz:老版本的真实价值

如果你在 2024 年还要搜 hadoop-2.6.5.tar.gz,原因大概率不是追新,而是被课程设计、老集群维护或者面试官卡住了。Hadoop 2.6.5 是 2016 年发布的维护版本,后面 2.7、2.8 甚至 3.x 都出了很久,但国内大量教材、实验手册、企业内部老任务还是指定这个版本,很多基于 Hadoop 的课程设计和中间件整合方案也是在这套代码上调通的。对现在的从业者来说,它的价值不在于性能多强,而在于生态兼容性最稳定:Hive、HBase、Spark 的某些老版本配套方案都能对上,网上能直接抄的作业也最多。这篇文章我会把它从下载、解压、伪分布式、完全分布式 HA 到 distcp 迁移完整拆一遍,适合正在做课程设计、准备 Hadoop 面试、或者维护老集群的人照着复现。

2. 下载与前置检查:JDK 兼容性、SSH 免密与目录规划

2.1 版本选型:为什么 2.6.5 而不是 2.7+

先回答一个老被问的问题:既然 2.6.5 是老版本,为什么不用 2.7 或者 3.x?常见情况是你所在的课程设计任务书或公司内部文档里写死了版本,配套的 MapReduce 样例、Hive 安装包、Zookeeper 整合方案都是按这个版本调好的。用高版本会出现适配问题,比如 MRv1 时代的 API 在新版里被标记废弃甚至移除,老教程里的命令在 3.x 里不少已经改了路径。2.6.5 的另一个优势是它仍然支持hadoop-daemon.sh这种脚本,很多教材里教的启动方式还能直接照用;新版本把这些内部脚本挪了位置,照着老教程敲命令容易翻车。

选择 2.6.5 还有个实际原因:它配套的 Zookeeper 3.4.x、JDK 8 都是同一时期的产品,版本匹配问题少。我见过有人拿 Hadoop 3.3 去配旧版 Zookeeper,结果zkfc的 watcher 机制不兼容,HA 状态来回切换,查了一下午才发现是版本组合的问题。这类玄学问题在老版本组合里反而少很多,因为社区踩过的坑都沉淀成教程了。

2.2 JDK 版本匹配:先确认环境再解压

下载完hadoop-2.6.5.tar.gz别急着解压,先检查 JDK。这个版本官方推荐的 Java 7,实际生产里用 Java 8 最常见,也是兼容性最好的选择。千万不要装 JDK 11 以上,启动时经常报UnsupportedClassVersionError或者反射相关的异常,NameNode 进程起来十几秒就自动消失,日志里看不到明确错误,排查起来很痛苦。

# 先确认 JDK 版本,Hadoop 2.6.5 只建议用 JDK 7 或 8 java -version # 如果 JDK 版本没问题,再解压到固定目录 tar -zxvf hadoop-2.6.5.tar.gz -C /opt/ mv /opt/hadoop-2.6.5 /opt/hadoop

解压后需要把HADOOP_HOME写进环境变量,这是网上教程里写烂了但很多人还是漏掉的步骤。漏掉之后的表现是:命令行敲hadoop报 command not found,或者能跑hdfs但跑不了yarn,因为 PATH 没指全。我一般还会顺手把JAVA_HOME也写进去,避免后面脚本里找不到java路径。

# 编辑 /etc/profile,追加以下内容 export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export HADOOP_HOME=/opt/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# 使环境变量生效并验证 source /etc/profile hadoop version

hadoop version正常输出 “Hadoop 2.6.5” 才说明基础环境没问题。这一步我建议当成强制检查项,很多教程跳过了,导致后边配置全对也起不来服务。另外提醒一下:不要把解压目录放在/root下或者权限为 777 的共享目录里,Hadoop 对目录权限敏感,dfs.datanode.data.dir指向的路径如果父目录权限过宽,DataNode 启动时会报权限相关的安全错误。

2.3 用户、SSH 免密与目录规划:装之前最容易被忽略的三件事

第一件事是创建专用用户。虽然单机伪分布式可以用 root 跑,但生产集群和多节点环境里 root 会引发一堆权限和隔离问题,比如 HDFS 写文件时 owner 全是 root,后续普通用户提交作业会报权限不足。我一般会建一个hadoop用户,所有操作都在这个用户下执行。

# 创建 hadoop 用户并设置密码 useradd hadoop passwd hadoop # 把 /opt/hadoop 属主改成 hadoop chown -R hadoop:hadoop /opt/hadoop

第二件事是配置 SSH 免密。Hadoop 集群启动脚本会通过 SSH 到每台机器上拉起进程,如果在虚拟机上做多节点实验,这一步不做的话start-dfs.sh会在每台机器上停下来等着输密码,非常浪费时间。单机伪分布式建议也配好,后面加节点时不用返工。

# 切换到 hadoop 用户执行 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa # 单机模式直接把公钥加入 authorized_keys cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys # 验证免密是否生效 ssh localhost 'echo ok'

第三步是规划目录。Hadoop 运行时要写三块数据:NameNode 元数据、DataNode 数据块、临时文件。默认情况下它们都落在/tmp下,但/tmp会被系统定时清理,重启一次机器集群就“失忆”了。我习惯统一建目录:

mkdir -p /opt/hadoop/data/namenode mkdir -p /opt/hadoop/data/datanode mkdir -p /opt/hadoop/data/journalnode mkdir -p /opt/hadoop/tmp

这三块路径在后面配置文件里要反复用到,先在命令行里建好能少踩一个“目录不存在导致格式化失败”的坑。如果是虚拟机里装 Hadoop,还要注意/etc/hosts的配置,不要只写localhost,要把主机名和 IP 一一对应写清楚,否则后面的 HA 配置里节点之间互相找不到对方。

3. hadoop 伪分布式搭建:单机跑通 WordCount 的完整流程

3.1 四份配置文件的参数逐项说明

伪分布式是入门 Hadoop 的第一道坎,也是课程设计里最常见的部署形式。核心就是改四份 XML 配置文件。注意$HADOOP_HOME/etc/hadoop/下默认没有mapred-site.xml,只有mapred-site.xml.template,很多人在这里卡住,直接复制模板改名就行。

先看core-site.xml,它决定 HDFS 的访问地址和临时目录:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/tmp</value> </property> </configuration>

fs.defaultFS是文件系统的默认入口地址,9000 是 NameNode 的 RPC 通信端口。伪分布式所以写localhost,完全分布式时要改成逻辑名字服务名,比如hdfs://mycluster。hadoop.tmp.dir是 NameNode 和 DataNode 存放元数据与数据块的根目录,默认值是/tmp/hadoop-${user},前面说过会被系统清理,必须改掉。

接着是hdfs-site.xml,这里主要设置数据副本数和目录位置:

<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///opt/hadoop/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file:///opt/hadoop/data/datanode</value> </property> </configuration>

dfs.replication在伪分布式下必须设为 1,因为只有一台 DataNode,默认值 3 会导致数据块写入时报NotEnoughReplicasException。dfs.namenode.name.dir和dfs.datanode.data.dir分别指定元数据和数据块的落盘路径,指向 2.3 节建好的目录。这里要提醒的是用file:///前缀,不是hdfs://。

然后是yarn-site.xml,负责资源调度和任务执行:

<configuration> <property> <name>yarn.nodemanager.aux-service</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>2048</value> </property> </configuration>

yarn.nodemanager.aux-service是 NodeManager 给 MapReduce 提供的辅助服务,值固定写mapreduce_shuffle,这是网上最容易抄错的一项,少一个字符整个作业都跑不起来。resource.memory-mb是单台机器可分配给容器的内存上限,虚拟机里如果机器总内存只有 2G,这里就不要调大,否则 NodeManager 直接挂掉。

最后是mapred-site.xml,指定计算框架为 YARN:

<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration>

这里的逻辑是:Hadoop 1.x 用自带的 JobTracker 跑作业,2.x 之后统一走 YARN,这一步不写的话作业会跑在本地模式,虽然不报错,但日志里完全看不到 MapReduce 的进度,新手容易误以为任务卡死了。

3.2 格式化、启动与 jps 验证

配置写完后,第一次启动 HDFS 之前必须先格式化 NameNode,这相当于给文件系统建立初始的元数据目录结构。不格式化直接启动的话,NameNode 根本起不来。格式化命令如下:

# 在 hadoop 用户下执行 hdfs namenode -format

看到输出里有SHUTDOWN_MSG并且没有Exception就算成功。格式化成功后会生成/opt/hadoop/data/namenode/current/VERSION文件,里面有clusterID,这个 ID 后面排错要用,先留着别删。

格式化完成后依次启动 HDFS 和 YARN:

# 启动 HDFS 守护进程 start-dfs.sh # 启动 YARN 守护进程 start-yarn.sh

启动过程中会输出每个节点的进程启动日志,看有没有报错回显。启动完用jps检查进程状态:

jps

伪分布式正常应该看到五个进程:NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager。少任何一个都说明启动有问题。最常见的遗漏是 SecondaryNameNode 没有,原因是core-site.xml里没配dfs.namenode.secondary.http-address,但这个在 2.6.5 下有默认值,所以正常启动都会带。

启动完还可以在浏览器里确认一下:访问http://localhost:50070能看到 NameNode 的界面,DataNode 节点列表里应该有且只有一个节点,状态为 Live。如果页面打不开,优先检查防火墙,很多虚拟机镜像默认开着防火墙,50070 端口被挡在外面。

3.3 用 hadoop-mapreduce-examples 跑第一个 WordCount

进程都起来之后,跑一个内置的 WordCount 来验证整个链路。这一步是课程设计最常要求的验收项,也是后面所有 MapReduce 作业的模板。先建输入目录并上传测试文件:

# 在 HDFS 上创建 input 目录 hdfs dfs -mkdir -p /user/hadoop/input # 把本地的配置文件上传为输入数据 hdfs dfs -put /opt/hadoop/etc/hadoop/core-site.xml /user/hadoop/input/ # 查看确认文件已上传 hdfs dfs -ls /user/hadoop/input

上传完成后提交 WordCount 作业:

hadoop jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.5.jar \ wordcount /user/hadoop/input /user/hadoop/output

这条命令的构成是:hadoop jar指定要跑的 jar 包,文件名里的2.6.5要和你的版本对上;wordcount是 jar 包里的主类名;后面两个路径分别是输入目录和输出目录。注意输出目录必须不存在,如果已经是第二次跑了,得先把旧的输出删掉,否则报FileAlreadyExistsException。

作业跑完会说Successfully completed,然后查看结果:

hdfs dfs -cat /user/hadoop/output/part-r-00000

能看到类似hadoop 4这样的词频统计结果,说明整个 Hadoop 从存储到计算的链路都通了。这一步别只跑通就算完,可以把core-site.xml换成一篇自己的长文本,看一看不同大小的输入跟运行时长之间的关系,对理解 MapReduce 的拆解逻辑很有帮助,面试也常问这个。

3.4 伪分布式到完全分布式的差别:同样是 2.6.5,配置思路完全两样

伪分布式跑通后,很多人直接把这几份配置复制到三台机器上就以为能组成集群,结果 DataNode 全起不来,或者起来了但互相不认。差别在于伪分布式依赖本地文件系统做元数据存储,而完全分布式需要配置名字服务、HA 共享存储和 Zookeeper 协调,是另一套配置维度。第 4 章会完整展开。

有个血泪提醒:伪分布式阶段不要在配置里预先开启 HA 相关参数,比如dfs.ha.automatic-failover.enabled。有些教程为了少改一次配置,从一开始就把 HA 的项写进去,结果单机环境下 ZKFC 找不到 Zookeeper,NameNode 反复重启。伪分布式和 HA 是两套模式,先单机验证完,再切换,别一步到位。

4. 集群搭建与 Zookeeper 整合:NameNode HA 自动故障转移实战

4.1 HA 为什么需要 Zookeeper:QJM 共享存储和 fence 原理

2.6.5 的 NameNode HA 用的是 QJM(Quorum Journal Manager)方案:两个 NameNode 节点通过一组 JournalNode 共享编辑日志,Active 节点把元数据操作写入 JournalNode,Standby 节点读取并持续同步,保证内存中的元数据始终跟得上。如果只有一台 NameNode,元数据全在本地磁盘,机器挂了数据就断了;有了 QJM,Active 挂了之后 Standby 可以直接接管。

那 Zookeeper 在这里扮演什么角色?它管的是自动故障转移。ZKFC(Zookeeper Failover Controller)进程会在 Zookeeper 里创建临时节点,Active 的 NameNode 持有锁,Standby 的监听锁的状态。Active 节点挂掉后,临时节点超时消失,ZKFC 收到通知触发切换。这个机制叫自动故障转移,没有它就只能手动执行hdfs haadmin -failover,生产环境里半夜出故障根本等不到人手动切。

这里常被面试官追问的是脑裂问题:如果 Active 节点没有真正死掉,只是网络分区了,Standby 会以为自己可以接管,结果集群里出现两个 Active。Hadoop 的解法是 fencing(隔离):在切换前用配置好的dfs.ha.fencing.methods把老 Active 强制杀掉或 SSH 进去执行kill -9。这也是为什么在 4.3 节配置里要指定sshfence和对应的私钥,fence 不成功,ZKFC 会拒绝切换。

4.2 节点规划与端口分配:三台虚拟机怎么摆活

我这里按最常见的三节点方案来规划,这也是课程设计和中小团队用的标准布局。三台机器分别叫n01、n02、n03,每台都装 Zookeeper 和 JournalNode,两个 NameNode 分布在不同的机器上,三个 DataNode 每台一个。具体分配如下表:

节点NameNodeDataNodeJournalNodeZookeeperZKFC
n01Active是是是是
n02Standby是是是是
n03无是是是无

端口规划也建议先列出来写进文档里,排查问题时对照着防火墙策略检查。NameNode 的 RPC 端口配 9000,HTTP 界面端口 50070,JournalNode 的 RPC 端口固定 8485,Zookeeper 的客户端端口默认 2181。每个节点之间都要能互相访问这些端口,虚拟机环境下我习惯先把三台机器的防火墙全关掉或者精确放行,然后统一改/etc/hosts:

192.168.56.101 n01 192.168.56.102 n02 192.168.56.103 n03

这个 hosts 文件三台机器要完全一致,顺序不要乱。很多人主机名用了带横线的名字或者大写字母,导致解析出来的 hostname 和/etc/hosts对不上,DataNode 注册不到 NameNode 上,网页上看节点数始终为 0,这种问题最难查,因为日志里没有明显报错,只是进程在但集群不健康。

4.3 hdfs-site.xml 的 HA 相关配置完整拆解

完全分布式模式下,hdfs-site.xml的配置量和伪分布式完全不同。建议直接逐项替换成下面这份,花十分钟把每个参数看懂再动手,这里放的是我自己用的最小可用配置:

<configuration> <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>n01:9000</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>n02:9000</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>n01:50070</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>n02:50070</value> </property> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://n01:8485;n02:8485;n03:8485/mycluster</value> </property> <property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/home/hadoop/.ssh/id_rsa</value> </property> <property> <name>dfs.journalnode.edits.dir</name> <value>/opt/hadoop/data/journalnode</value> </property> <property> <name>dfs.replication</name> <value>2</value> </property> </configuration>

dfs.nameservices是这个 HA 集群的逻辑名字,后面所有跟集群相关的配置都以它为前缀,三台机器上这个名字必须一致。dfs.ha.namenodes.mycluster给两个 NameNode 起了别名nn1和nn2,下面的 RPC 和 HTTP 地址都挂在它们下面。dfs.namenode.shared.edits.dir指定三个 JournalNode 的地址,这个值的格式是qjournal://host1:8485;host2:8485;host3:8485/集群名,分号隔开,最后一个mycluster是 namespace 名称,跟dfs.nameservices对应。dfs.client.failover.proxy.provider的作用是让客户端拿到 Active NameNode 的地址,否则作业提交后不知道该找哪台机器。dfs.ha.fencing.methods配sshfence表示用 SSH 方式隔离旧的 Active 节点,私钥路径必须能被启动 hadoop 的用户读取。

配合这份配置,core-site.xml也要调整,文件名解析不再指向localhost而是指向逻辑集群:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property> <property> <name>ha.zookeeper.quorum</name> <value>n01:2181,n02:2181,n03:2181</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/tmp</value> </property> </configuration>

ha.zookeeper.quorum是 Zookeeper 集群的地址列表,ZKFC 和 NameNode 都要靠它来做分布式协调。这里建议用 Zookeeper 3.4.x 系列,我用的 3.4.14 和 2.6.5 配合得很稳定,3.5 及以上版本的配置格式有变化,旧版 ZKFC 读起来偶尔会出兼容性问题。

4.4 ZKFC 启动顺序与 Active/Standby 状态验证

HA 集群的启动顺序和伪分布式完全不同,顺序乱了会报各种莫名其妙的错误。很多人在这一步翻车,原因是直接在 n01 上执行start-dfs.sh,结果 JournalNode 没起,NameNode 找不到共享目录直接挂掉。我总结的可靠顺序如下。

第一步,先在三台机器上启动 Zookeeper,确认在三台上执行zkServer.sh status能看到两个 follower 和一个 leader:

# 每台机器分别执行 zkServer.sh start zkServer.sh status

第二步,三台机器都启动 JournalNode:

hadoop-daemon.sh start journalnode

检查方式是用jps看进程,三台上都应该有JournalNode。确认都起来之后再格式化。格式化必须在 n01 上做,并且dfs.namenode.name.dir指向的目录必须是空的,否则会提示目录已存在并拒绝执行。接着把元数据同步到 n02:

# 在 n01 上执行格式化 hdfs namenode -format # 在 n02 上执行,让它从 n01 拉取元数据 hdfs namenode -bootstrapStandby

bootstrapStandby的作用是把 n01 的 NameNode 元数据复制到 n02 的本地目录,让两个 NameNode 从相同的状态开始工作。这个命令只会在第一次搭建时执行,之后 n02 会通过 JournalNode 持续同步。

第三步,初始化 ZKFC 的元数据。在任意一台机器上执行:

hdfs zkfc -formatZK

这条命令会在 Zookeeper 里创建与 HA 相关的 znode,相当于给自动故障转移搭好舞台。执行完后可以到 Zookeeper 里确认一下ls /hadoop-ha/mycluster是否有内容。

第四步,回到 n01 上正常拉起整个 HDFS:

start-dfs.sh

这时jps观察,理想状态是三台机器各有一个JournalNode和一个ZKFC,n01 和 n02 各有NameNode,三台都有DataNode。然后查两个 NameNode 的状态:

hdfs haadmin -getAllServiceState

正常输出是n01:active和n02:standby,说明 HA 已经就绪。这个验证建议做两次:第一次查状态,第二次直接模拟故障。

# 在 n01 上找到 NameNode 的 PID,然后杀掉 jps kill -9 <NameNode进程PID> # 等 20 秒让 Zookeeper 检测到超时 hdfs haadmin -getAllServiceState

20 秒后 n02 会从 standby 变成 active,这就完成了一次真实的故障转移。注意kill -9模拟的是进程突然死亡的场景,能验证自动转移;如果只是手动停掉服务,ZKFC 会认为这是正常下线,不会触发切换。这项操作也可以作为课程设计的验收演示,比单纯截图配置页面有说服力得多。

5. 常见问题排查:2.6.5 启动失败的高频原因与解决

5.1 格式化后 DataNode 起不来:clusterID 不一致

现象是start-dfs.sh执行完,jps显示 DataNode 进程存在,但 HDFS 网页上 Datanodes 列表为空,日志里反复出现Incompatible clusterIDs或者datanode with the same address already exists。

原因是 NameNode 格式化时生成了一个clusterID,而 DataNode 的current/VERSION文件里保存的是旧 ID。常见场景是hdfs namenode -format执行了两次,或者把别的机器的数据目录拷了过来。解决方法是把 DataNode 数据目录里的current目录删掉再重启:

# 停掉 HDFS 后执行 rm -rf /opt/hadoop/data/datanode/current # 然后重新启动 hdfs datanode -format start-dfs.sh

这是我踩得最多的坑,格式化命令本身没有任何提示风险,但反复格式化就会让元数据和数据块版本错位。现在的习惯是:每次namenode -format之前,都顺手把所有 data 目录清空一遍,保证集群里的节点拿的是同一套 ID。

5.2 JDK 版本过高导致 NameNode 直接退出

现象是start-dfs.sh执行时 NameNode 日志打出UnsupportedClassVersionError或IllegalArgumentException,然后进程消失。有些环境里报错更隐蔽,只是jps里根本没有 NameNode。

原因是 Hadoop 2.6.5 的hadoop-daemon.sh会调用java命令,如果JAVA_HOME指向 JDK 9 以上,字节码版本对不上。解决方式是把JAVA_HOME明确指向 JDK 8,并且检查系统默认的java和javac版本一致:

# 在 /etc/profile 里强制指定 export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export PATH=$JAVA_HOME/bin:$PATH

检查的时候有个细节:java -version输出的可能是 JRE 版本,但 Hadoop 的补丁工具比如hdfs命令需要 JDK,所以一定要确认javac -version也是 1.8。如果系统里装了多个 JDK,可以用update-alternatives --config java切到 8。这类环境问题最磨人,日志里错误信息经常不完整,我一般先把环境变量输出一遍,确认无误再谈别的。

5.3 SSH 免密没生效,start-dfs.sh 卡在密码输入

现象是执行start-dfs.sh时所有输出组件名之后就不动了,光标停在提示符前像是在等输入;或者提示Permission denied (publickey,gssapi-keyexch,password)。

原因是ssh localhost都需要密码,Hadoop 脚本没有能力自动输密码,只能卡住。解决方式是对集群里每个主机名都验证一遍免密效果:

ssh n01 'date' ssh n02 'date' ssh n03 'date'

如果其中某个节点提示输入密码,说明authorized_keys没配置好。常见的原因是权限不对:.ssh目录权限应为 700,authorized_keys权限应为 600,权限给宽了 SSH 直接忽略这个文件。这属于安全限制,不是 Hadoop 的问题,改掉权限即可。配好之后重新执行一遍三台机器的 ssh 测试,确保所有主机名都能秒过。

5.4 YARN 容器内存设置过大导致 NodeManager 挂掉

现象是start-yarn.sh执行完jps里能看到 NodeManager,但过几十秒进程消失,日志里有OutOfMemoryError或者Container [pid=...] is running beyond virtual memory limits。

原因是默认的yarn.nodemanager.resource.memory-mb在某些教程模板里被改得很大,比如在 2G 内存的虚拟机里配了 8G,NodeManager 自身分配内存失败直接退出。解决方法是把内存相关的参数调到物理机实际可用内存的 60% 左右:

<property> <name>yarn.nodemanager.resource.memory-mb</name> <value>2048</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>2048</value> </property> <property> <name>yarn.nodemanager.vmem-pmem-ratio</name> <value>2.1</value> </property>

vmem-pmem-ratio是虚拟内存与物理内存的比值上限,MapReduce 作业默认会申请虚拟内存,不调这个参数时物理内存只剩 1G 的机器上,作业经常报Container killed on request. Exit code is 143。这个参数在课程设计的机器上尤其重要,很多人的虚拟机只有 2G 内存,不改vmem-pmem-ratio就是作业跑不动。

5.5 防火墙与 /etc/hosts 没同步,节点间通信失败

现象是集群里的 DataNode 全部显示 Live,但跨节点的 NameNode 之间连不上;或者hdfs haadmin命令返回连接超时,日志里全是ConnectException。

原因一般有二:防火墙拦截了 8020、8485、2181 等端口;或者各机器的/etc/hosts内容不一致,n01 解析n02时拿到的 IP 是错的。解决方式是先互 ping 验证网络,再检查端口:

# 三台机器互 ping 主机名 ping n02 # 检查端口是否能通 telnet n02 8485 telnet n02 2181

如果不通,先放行端口或直接关防火墙。虚拟机实验环境我建议直接关闭防火墙,减少干扰项:

systemctl stop firewalld systemctl disable firewalld

这类网络排查最怕的是只盯着一台机器看,正确做法是在三台机器上分别执行telnet验证双向连通性。我见过一个案例,n01 能连 n02,但 n02 连不上 n01,最后发现是 n02 上 hosts 写错了一个 IP,这种问题不逐台验证很难定位。

6. distcp 参数说明与集群间迁移:一次实战的边界

6.1 distcp 常用参数速查

distcp 是 Hadoop 自带的跨集群复制工具,全称是 distributed copy。你可能会问:它和hdfs dfs -cp有什么区别?区别在于 distcp 会启动 MapReduce 作业并发复制,几十 GB 的数据几百个 map 同时跑,速度完全不在一个量级。下面是 2.6.5 版本里我实际用过的参数,先看表格:

参数作用使用建议
-update只复制源端有而目标端没有或版本更新的文件增量复制首选
-overwrite无条件覆盖目标端同名文件源端变更少时配合-update
-delete删除目标端多余的路径做镜像同步时配合-update
-skipCrC跳过 CRC 校验,减少读写开销目标端能接受校验精度损失时用
-m设置最大 map 数不要超过集群可用容器数
-bandwidth限制单 map 的传输带宽(MB/s)避免挤占业务带宽
-p保留权限、属主、时间戳等属性跨集群迁移时强烈建议加
-i单个文件失败时忽略错误继续大目录复制时推荐加

这组参数里最容易用错的是-update和-overwrite的关系。很多人以为-update会覆盖本地已有文件,官方其实解释了:-update只根据文件大小和修改时间判断是否重新复制,若两边文件大小相同,即使内容有差异,-update也会跳过。这时想要强制刷新,得用-overwrite。我自己的习惯是:基准同步用-update,全量替换用-overwrite,不要同时盲目叠加。

6.2 实战:跨集群搬迁 HDFS 目录

从一个 Hadoop 集群向另一个集群迁移目录,我一般是这样执行的。假设源集群的 active NameNode 是n01:9000,目标集群是n02:9000,要复制/user/hadoop/weblogs到对方的同样路径:

hadoop distcp \ -update \ -skipCrC \ -m 20 \ -bandwidth 10 \ -i \ hdfs://n01:9000/user/hadoop/weblogs \ hdfs://n02:9000/user/hadoop/weblogs

先说明参数:-update增量复制,避免重复拷贝;-skipCrC跳过 CRC 校验,拷贝大目录时性能提升明显;-m 20表示最多跑 20 个 map 任务;-bandwidth 10限制每个 map 的带宽为 10MB/s,避免把源集群网络占满;-i单个文件失败时继续跑,不至于因为一个坏文件导致整个任务废弃。源和目标路径都用完整的hdfs://host:port/path格式,特别是目标端如果不在同一个集群,必须写全 RPC 地址。

作业执行后,屏幕上会弹出一个 MapReduce job 的进度窗口。注意观察每个 map 的完成情况和失败次数,如果出现大面积失败,先停掉作业检查网络。作业结束后,到目标集群验证样本文件:

hdfs dfs -ls hdfs://n02:9000/user/hadoop/weblogs/ | head # 抽查某个文件的大小是否与源端一致 hdfs dfs -ls hdfs://n01:9000/user/hadoop/weblogs/

如果-skipCrC开着,文件块大小对不上但复制仍显示成功,这时用hdfs fsck对目标目录做一次完整性检查更稳妥。生产环境里我一般不开-skipCrC做首次全量,只有做增量时才配合-update使用。

6.3 别踩的坑:实时性、块大小、权限

distcp 看起来就是一个hadoop distcp命令,但实践中它有不少边界条件。第一个边界是它不是实时同步工具,支持的是“一次性批量复制”。源端如果一直在产生新文件,distcp 只会复制启动瞬间已经存在的文件,之后进入的文件完全不管。想要持续同步,要么定时调度 distcp,要么上真正的复制工具,不要把 distcp 当成同步软件用。

第二个边界是跨版本迁移时块大小不匹配的问题。源集群的 block size 是 128MB,目标集群是 64MB,distcp 在目标端会按新集群的块大小重新切分,这不影响数据内容,但会导致目标端文件占用的 block 数量不同。有人拿hdfs dfs -du对比源和目标,发现字节数对不上,就以为复制出错了,其实是 block 粒度导致的统计差异,文件内容是对的。

第三个边界是权限保留。跨集群迁移时,如果目标集群的用户体系跟源集群不一样,不加-p会导致所有文件属主变成执行 distcp 的用户。反过来,加了-p但当前用户没有目标路径的写权限,复制会直接失败。建议在目标集群先创建好对应的用户和父目录:

hdfs dfs -mkdir -p /user/hadoop/weblogs hdfs dfs -chown hdfs:hadoop /user/hadoop/weblogs

我的习惯是先拿一个小目录试跑一遍,观察参数组合的效果,再上全量。具体做法是先复制一个只有几十 MB 的测试目录,查看结果文件的权限、时间戳、大小是否和预期一致,确认没问题后再把-m和-bandwidth去掉,跑正式全量。从那以后我每次用 distcp 都强制走一遍小目录验证,这个习惯帮我避免了好几次因为参数理解偏差导致的大范围覆盖事故。希望帮到你。

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

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

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

立即咨询