news 2026/10/26 20:25:39

Hadoop完全分布式集群搭建实战:配置、启动与故障排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hadoop完全分布式集群搭建实战:配置、启动与故障排查

刚接触 Hadoop 那会儿,我在一台虚拟机上连续运行了三个月伪分布式环境,觉得完全分布式无非就是多开几台机器、多复制几份配置文件。真正上手才发现,从一台机器到三台机器,难度并非线性增长,而是直接跳了一个台阶:主机名解析、SSH 免密、时钟漂移、clusterID 不一致、容器内存被 kill,每一个都能让你对着日志看半天。这篇内容讲的是 Hadoop 完全分布式集群搭建,一件看起来配置项只有几十个,但每一步都有隐藏前提的事。它要做的是把 NameNode、DataNode、ResourceManager、NodeManager 这些角色拆分到不同物理节点上,让存储和计算真正具备横向扩展能力,而不只是让教程能从一台机器跑通。适合已经跑过伪分布式、准备把环境做得更接近生产形态的读者,也适合那些集群启不起来、DataNode 一启动就掉线、跑作业报找不到主类的同学对照排查。下面这些内容都来自我在三到五台虚拟机上反复重装、反复格式化后攒下来的实操记录,涉及参数的地方我会给出计算过程,涉及取舍的地方我会说明为什么这么选。

1. 先把部署模式的分野搞清楚:为什么最终都得走到完全分布式

1.1 本地模式、伪分布式、完全分布式的本质差别

很多人把三种模式理解成“配置复杂度不同”,这个理解是有偏差的。真正的差别在于进程边界和故障边界。本地模式所有逻辑跑在同一个 JVM 里,用的是本地文件系统,fs.defaultFS指向file:///,它连 HDFS 都没启动,只能用来验证 MapReduce 代码逻辑对不对。伪分布式把各个守护进程都拉起来了,但全部挤在一台机器上,NameNode、DataNode、ResourceManager、NodeManager 各一个,数据副本数只能配 1,它验证的是“配置项写对了没有”。完全分布式才第一次让多个 DataNode 同时存在,让副本真正落在不同机器上,让 ResourceManager 去跨节点调度容器。

这个区别带来的直接后果是:伪分布式阶段你不会遇到的网络问题、时间问题、权限问题、资源竞争问题,在完全分布式阶段会集中爆发。举个最典型的例子,伪分布式下你怎么改主机名都没事,因为所有通信都走 localhost;一旦上了三台机器,hdfs://hadoop101:8020里的主机名解析不了,客户端直接卡在连接超时,日志里只有一句干巴巴的Connection refused,新手很容易以为是 Hadoop 本身的问题。

所以我的建议是:伪分布式至少要跑通一次完整的“上传文件、跑一个 WordCount、去 Web UI 看块分布”,再动手做完全分布式。原因很实在,伪分布式阶段你能把配置文件的结构、环境变量的写法、启停脚本的位置全部摸熟,到了多机阶段,出问题时你才有能力判断是“配置写错了”还是“环境没对齐”。

1.2 完全分布式真正解决的三个问题

第一个是存储容量与吞吐的线性扩展。单机磁盘再大也有上限,而 HDFS 的设计前提就是数据分散在几十上百台机器上,客户端写入时由 NameNode 分配块位置,多个 DataNode 并行接收。三台机器听起来不多,但它已经能让你观察到副本放置策略、机架感知、块汇报这些机制的真实行为。

第二个是计算与存储的本地化。YARN 调度容器时会尽量把任务分配到数据所在节点,这叫数据本地性。伪分布式下这个机制形同虚设,因为数据只有一份且就在本机。上了完全分布式,你在 ResourceManager 的 Web UI 里能看到每个任务的本地性级别,NODE_LOCAL、RACK_LOCAL、OFF_SWITCH的分布比例直接反映你的集群规模和数据分布是否健康。

第三个是角色分离带来的稳定性认知。把 NameNode 单独放一台、ResourceManager 单独放一台,看起来只是“分散压力”,实际上它让你第一次直面单点问题:NameNode 挂了整个 HDFS 不可用,ResourceManager 挂了所有新作业提交不上去。这也是为什么后面要考虑 SecondaryNameNode 或者更进一步的 HA 方案。三台机器的集群虽然小,但它把这个结构性风险暴露得很清楚。

1.3 集群规模怎么定:从三台虚拟机说起

三台是最小可用的完全分布式规模,理由不是“教程都这么写”,而是副本数与角色分布的硬约束。默认副本数是 3,如果只有两台 DataNode,HDFS 会一直处于副本不足的告警状态,虽然能用,但日志里天天刷under-replicated blocks,你没法判断哪些是真问题。三台刚好让每个副本落在一个独立节点上,副本因子和节点数对齐,状态最干净。

角色分配上我习惯这么排:一台做 NameNode 加 ResourceManager,一台做 SecondaryNameNode 加 DataNode 加 NodeManager,第三台纯 DataNode 加 NodeManager。也有人把 NameNode 和 ResourceManager 拆到两台,这样单点更分散,但三台机器拆得太细会导致每台都身兼数职,反而不利于观察。我的做法是主角色集中在第一台,从角色铺满三台,好处是排查问题时目标明确:NameNode 相关的日志只看第一台,DataNode 相关的日志三台都看。

硬件资源上,虚拟机给每台 4 核 8G 是比较舒服的起点。低于 4G 内存会非常难受,因为 JVM 堆、操作系统缓存、NodeManager 容器都要抢内存,你会频繁遇到容器被 kill 的情况。磁盘给 50G 起步,因为要放安装包、数据块、日志,而且 HDFS 的hadoop.tmp.dir默认会存元数据,格式化几次就占掉不少。这里有个容易被忽略的点:虚拟机的磁盘不要用动态分配的精简模式,我吃过亏,精简盘在大量小文件写入时性能抖动非常明显,块写入超时导致的 DataNode 假死很难定位。

2. 环境规划:把能提前填的坑先填掉

2.1 主机名、IP 与 hosts 映射

主机名这件事,看起来是最没技术含量的一步,但它是最容易埋雷的地方。Hadoop 的各组件之间大量使用主机名通信,配置文件里写的是hadoop101,如果这个名字在节点上解析不了,整个集群就是一堆连不上的进程。我的习惯是固定用hadoop101、hadoop102、hadoop103这种命名,IP 用192.168.10.101到192.168.10.103这样的内网段,避开常见的192.168.1.x,因为家里路由器经常占这个段,虚拟机网络模式一换就冲突。

具体操作分三步。第一步改主机名,用hostnamectl set-hostname hadoop101,改完hostname确认一下,注意很多发行版需要重新登录终端提示符才会变。第二步改/etc/hosts,三台机器上都要写入完整的三行映射:

192.168.10.101 hadoop101 192.168.10.102 hadoop102 192.168.10.103 hadoop103

第三步验证,在每台机器上ping hadoop102和ping hadoop103,能通才算过关。这里有个细节:不要只在一台机器上改 hosts 就复制过去,因为如果你中途改过 IP,复制过去的文件里可能还是旧地址,而ping有时候因为缓存还能通,等到启动集群才暴露。我的做法是每台机器单独编辑,改完统一cat一遍核对。

注意:如果虚拟机用的是 NAT 模式,克隆出来的机器网卡 MAC 会变,IP 可能重新分配,务必在克隆后逐台确认ip addr的结果和 hosts 里写的一致。

2.2 JDK 安装与 JAVA_HOME 的路径陷阱

Hadoop 3.x 要求 JDK 8 及以上,我一般用 JDK 8 的某个稳定小版本,兼容性最好。安装方式上,直接解压 tar 包比包管理器安装更可控,因为包管理器装的 JDK 路径常常带版本号软链,JAVA_HOME指向软链时,某些脚本读取会出问题。

解压到/opt/module/下面,然后配置/etc/profile.d/my_env.sh:

export JAVA_HOME=/opt/module/jdk1.8.0_212 export PATH=$JAVA_HOME/bin:$PATH export HADOOP_HOME=/opt/module/hadoop-3.3.6 export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH

这里有个必须说清楚的点:JAVA_HOME必须写绝对路径,不能写$PATH里推导出来的路径。Hadoop 的启动脚本在hadoop-env.sh里会读这个变量,如果写成相对路径或者软链,远程 SSH 启动进程时环境变量不会传递过去,表现就是本机start-dfs.sh正常,但其他节点上的 DataNode 起不来,日志里报JAVA_HOME is not set。

改完source /etc/profile,然后用echo $JAVA_HOME和java -version双重确认。我还会额外做一件事:在hadoop-env.sh里把JAVA_HOME再显式写一遍。原因是 SSH 远程执行命令时用的是非交互式 shell,不会加载/etc/profile,只靠系统环境变量是不够的。这一行冗余配置救过我很多次。

2.3 SSH 免密登录的完整链路

Hadoop 的启停脚本依赖 SSH,start-dfs.sh在 NameNode 上执行时,会通过 SSH 去每台 DataNode 上拉起进程。如果免密没配好,脚本会反复提示输密码,批处理场景下直接失败。

标准做法是三个节点都生成密钥对,然后把公钥汇总。我习惯这样做:在hadoop101上执行ssh-keygen -t rsa三次回车生成密钥,然后分发:

ssh-copy-id hadoop101 ssh-copy-id hadoop102 ssh-copy-id hadoop103

接着在hadoop102和hadoop103上重复生成密钥并分发到三台。为什么要三台都配而不是只配第一台?因为后面如果你在hadoop102上手动启动某个组件,或者用分发脚本同步配置文件,都需要免密。全互联是最省心的方案,虽然看起来冗余。

验证方式是逐条执行ssh hadoop102和ssh hadoop103,能直接进 shell 不提示密码就算成功。这里有两个常见坑:一是~/.ssh目录权限必须是 700,authorized_keys必须是 600,权限不对 SSH 会静默拒绝;二是如果之前配过别的机器的密钥,known_hosts里可能残留旧记录,重装系统后 IP 不变但主机密钥变了,SSH 会报Host key verification failed,删掉对应行或者直接清空known_hosts即可。

2.4 时间同步、防火墙与 SELinux

时间同步在多机环境里不是可选项。Hadoop 的很多机制依赖时间戳,比如日志排序、块汇报的超时判断、Kerberos 场景下的票据有效期。三台机器时间差超过几分钟,你会看到日志顺序错乱,排查问题时完全理不清因果。

简单做法是用chronyd或者ntpd指向同一个时间源。如果环境不允许连外网,就在第一台上装个本地时间服务,另外两台指向它。配置完之后用date在三台机器上同时执行一次,秒级差异可以接受,分钟级必须处理。

防火墙和 SELinux 这两项,在实验环境里我建议直接关掉,不是因为它们不重要,而是因为它们会让排查问题的变量变多。关防火墙用systemctl stop firewalld加systemctl disable firewalld,SELinux 改/etc/selinux/config里的SELINUX=disabled然后重启。如果你所在的环境有安全要求不能关,那就得逐个端口放行,Hadoop 用到的端口不少:NameNode 的 8020 和 9870、DataNode 的 9864 和 9866、ResourceManager 的 8032 和 8088、NodeManager 的 8042、JobHistory 的 10020 和 19888,还有 DataNode 之间传输数据用的随机端口段。放行随机端口很麻烦,所以实验环境关闭是最务实的选择。

3. 配置文件逐个拆解:每个参数为什么这么写

3.1 安装包解压与目录规划

把 Hadoop 安装包解压到/opt/module/hadoop-3.3.6,路径里不要带空格和中文,这是常识但确实有人踩。目录规划上,我习惯把数据目录也放在同一个父目录下,比如/opt/module/hadoop-3.3.6/data/,下面分tmp、namenode、datanode三个子目录。这样做的好处是卸载或者重装时整个目录一删就干净,不会出现“数据残留在/tmp里导致格式化后还是旧 clusterID”这种经典问题。

很多人直接用默认配置,数据会落到/tmp/hadoop-用户名/下面。/tmp目录在部分系统上会被定时清理,重启后元数据丢失,NameNode 直接起不来,报找不到fsimage。这个坑非常隐蔽,因为第一次搭建时一切正常,过几天重启就崩了。所以hadoop.tmp.dir必须显式指定到一个持久化路径。

3.2 core-site.xml 与 hdfs-site.xml 的关键项

core-site.xml里最重要的两项是fs.defaultFS和hadoop.tmp.dir:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://hadoop101:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/module/hadoop-3.3.6/data/tmp</value> </property> <property> <name>io.file.buffer.size</name> <value>131072</value> </property> <property> <name>hadoop.http.staticuser.user</name> <value>root</value> </property> </configuration>

fs.defaultFS里的端口,Hadoop 2.x 时代习惯写 9000,3.x 默认是 8020,两者都能用,关键是所有节点必须一致。我见过有人主节点写 8020,从节点复制的时候手抖写成 9000,结果客户端连得上 NameNode 但 DataNode 注册不上,因为 DataNode 上报的地址和 NameNode 期望的不匹配。

io.file.buffer.size设成 128KB,是为了匹配大多数文件系统的块大小,减少读写syscall次数。这个值不是越大越好,超过操作系统页缓存粒度后收益递减。

hadoop.http.staticuser.user这一项很多人不写,后果是通过 Web UI 浏览 HDFS 目录时点删除会报权限错误,因为 Web 端默认用dr.who这个虚拟用户。设成你实际的操作用户,能省掉一堆困惑。

hdfs-site.xml里核心是目录和副本数:

<configuration> <property> <name>dfs.namenode.name.dir</name> <value>/opt/module/hadoop-3.3.6/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/module/hadoop-3.3.6/data/datanode</value> </property> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.blocksize</name> <value>134217728</value> </property> <property> <name>dfs.namenode.http-address</name> <value>hadoop101:9870</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>hadoop102:9868</value> </property> </configuration>

dfs.blocksize写的是字节数,134217728 就是 128MB。为什么是 128MB 而不是更大?简单算一下:一个 1GB 的文件切成 128MB 是 8 个块,如果块太小,NameNode 的元数据压力会急剧上升,因为它要为每个块维护一条记录,默认每条记录占 150 字节左右;块太大,单块读取的并行度下降,而且一个节点故障时需要重传的数据量更大。128MB 是在“元数据开销”和“传输粒度”之间比较平衡的取值,SSD 普及后有人调到 256MB,但在虚拟机环境里 128MB 更稳。

dfs.namenode.secondary.http-address里指定的hadoop102就是 SecondaryNameNode 所在节点。要注意 SecondaryNameNode 不是 NameNode 的备份,它的作用是周期性合并fsimage和edits,降低 NameNode 重启时的恢复时间。放在第二台机器上是分担 I/O 压力,放在同一台会造成合并时和 NameNode 抢磁盘。

3.3 yarn-site.xml 与 mapred-site.xml

YARN 的配置决定你的集群能跑多大的任务,yarn-site.xml里:

<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>hadoop101</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>4096</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>4096</value> </property> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>1024</value> </property> <property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>4</value> </property> <property> <name>yarn.log-aggregation-enable</name> <value>true</value> </property> <property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> </property> </configuration>

yarn.nodemanager.resource.memory-mb是最容易配错的一项,配大了容器启动时被系统 OOM killer 干掉,配小了任务跑不动。计算逻辑是这样:一台 8G 内存的机器,操作系统和基础服务占 1.5G 到 2G,Hadoop 自身守护进程(DataNode 加 NodeManager)占 1G 左右,剩下的留给容器。8 - 2 - 1 = 5G,再留 1G 弹性,所以配 4096。这个数字不是拍脑袋,是按“物理内存减去常驻开销再打折”来的。

yarn.scheduler.maximum-allocation-mb必须小于等于节点管理器可用内存,否则申请大内存任务时会一直被拒。minimum-allocation-mb设 1024 是因为 MapReduce 默认的容器内存就是 1024,设小了会被强制上调,设大了小任务也会占满。

yarn.nodemanager.vmem-check-enabled设成 false 是虚拟机环境的经验值。虚拟内存检查依赖系统的 swap 统计,在虚拟机里这个统计经常不准,导致正常的容器被误杀,日志里报Container killed on request. Exit code is 137。关掉它,用物理内存控制就够了。

mapred-site.xml:

<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>mapreduce.jobhistory.address</name> <value>hadoop101:10020</value> </property> <property> <name>mapreduce.jobhistory.webapp.address</name> <value>hadoop101:19888</value> </property> <property> <name>yarn.app.mapreduce.am.env</name> <value>HADOOP_MAPRED_HOME=/opt/module/hadoop-3.3.6</value> </property> <property> <name>mapreduce.map.env</name> <value>HADOOP_MAPRED_HOME=/opt/module/hadoop-3.3.6</value> </property> <property> <name>mapreduce.reduce.env</name> <value>HADOOP_MAPRED_HOME=/opt/module/hadoop-3.3.6</value> </property> </configuration>

后面三个env配置是 Hadoop 3.x 的高频坑。如果不配,作业提交后会报Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMaster,原因是容器启动时找不到 MapReduce 的类路径。这个报错极具误导性,看着像 Java 环境问题,实际是配置缺项。我在这一项上浪费过一个下午。

3.4 workers 文件、hadoop-env.sh 与环境变量分发

workers文件在 Hadoop 3.x 里取代了 2.x 的slaves,内容就是所有从节点的列表:

hadoop101 hadoop102 hadoop103

注意这里把 NameNode 所在的hadoop101也写进去了,因为这台机器同时也跑 DataNode 和 NodeManager。如果你的规划里主节点不承担存储和计算角色,就只写从节点。我建议小集群里主节点也参与,否则三台机器只有两台干活,资源利用率太低。

hadoop-env.sh里要加上前面提到的 Java 路径和用户变量:

export JAVA_HOME=/opt/module/jdk1.8.0_212 export HDFS_NAMENODE_USER=root export HDFS_DATANODE_USER=root export HDFS_SECONDARYNAMENODE_USER=root export YARN_RESOURCEMANAGER_USER=root export YARN_NODEMANAGER_USER=root

后面五个用户变量在 Hadoop 3.x 里必须配,2.x 时代不需要。它们的作用是告诉启动脚本以什么身份执行进程,用 root 跑实验环境能避免权限问题,生产环境当然要用独立用户,但实验阶段用 root 能少踩一半的坑。

配置文件改完之后,要分发到所有节点。我习惯写一个小脚本放在~/bin/下:

#!/bin/bash if [ $# -lt 1 ]; then echo "Usage: xsync <file>" exit 1 fi for host in hadoop101 hadoop102 hadoop103; do if [ "$host" != "$(hostname)" ]; then rsync -av "$@" "$host:$@" fi done

分发完在每台机器上核对关键文件内容,别嫌麻烦。我遇到过一次分发脚本漏掉了mapred-site.xml,结果只有主节点知道怎么跑 MapReduce,作业提交后一直卡在 ACCEPTED 状态,日志里没有任何明显报错,纯粹是资源管理器不知道去哪找 ApplicationMaster,最后靠diff两台机器的配置目录才发现。

4. 格式化、启动与验证的完整闭环

4.1 第一次格式化 NameNode

格式化命令是hdfs namenode -format,这一条命令被讲了无数遍,但它的不可重复性还是每年让很多人崩溃。格式化会生成一个新的 clusterID 并写入 NameNode 的元数据目录,DataNode 启动时会带着自己记录的 clusterID 去注册,两者不一致就拒绝注册,表现是 DataNode 启动几秒后自动退出。

所以规则很简单:只在首次搭建时格式化一次。如果后续改了 IP、改了主机名、改了数据目录,需要重新格式化,那就必须先把所有节点上的data/namenode和data/datanode目录清空,包括 NameNode 和所有 DataNode,一台都不能漏。只清主节点不清从节点,下回启动还是 clusterID 冲突。

格式化成功的标志是日志里出现Formatting using clusterid: CID-xxxx-xxxx以及Storage directory ... has been successfully formatted。看到这两句再往下走,如果中间有ERROR就别启动,先解决。

4.2 启动顺序与进程分布

启动顺序上,我的习惯是先在 NameNode 上执行start-dfs.sh,再执行start-yarn.sh,最后启动历史服务器mapred --daemon start historyserver。DFS 先起是因为 YARN 的容器可能要把中间数据写到 HDFS,反过来的话偶尔会报 HDFS 不可用的警告。

start-dfs.sh执行后,hadoop101上应该有 NameNode、DataNode、SecondaryNameNode(如果你的 SecondaryNameNode 配在 102,那就在 102 上),hadoop102和hadoop103上各有一个 DataNode。用jps检查是最直接的办法:

节点期望进程
hadoop101NameNode、DataNode、ResourceManager、NodeManager
hadoop102DataNode、SecondaryNameNode、NodeManager
hadoop103DataNode、NodeManager

jps的输出如果少了某个进程,直接去$HADOOP_HOME/logs/下看对应角色的日志,文件名是hadoop-用户名-角色-主机名.log,比如hadoop-root-datanode-hadoop102.log。日志末尾的Caused by就是根因。

启动过程中有个细节值得说:start-dfs.sh脚本本身不启动任何进程,它只是通过 SSH 到各节点去执行hdfs --daemon start xxx。所以如果某台机器 SSH 免密不通,脚本会卡在那里等密码,看起来像“启动很慢”。遇到启动卡住,先怀疑 SSH,再怀疑配置。

4.3 Web UI 与命令行的双重验证

进程起来了不代表集群健康,还要从两个维度验证。Web UI 方面,浏览器访问http://192.168.10.101:9870看 NameNode 页面,重点看两处:一是Summary里的Live Nodes数量,应该是 3;二是Utilities里的Browse the file system能正常列出根目录。如果 Live Nodes 是 1,那是另外两台没注册上,回到 clusterID 和网络排查。

ResourceManager 的界面在http://192.168.10.101:8088,看Cluster页签下的Active Nodes,应该是 3,同时能看到总内存和总 vcore 数,这两个数应该等于你配置的memory-mb乘以节点数。

命令行方面,用hdfs dfsadmin -report能看到每个 DataNode 的详细状态,包括容量、已用空间、剩余空间、是否处于In Service状态。这个输出比 Web UI 更详细,尤其适合确认块池和副本情况。再用hdfs dfs -mkdir -p /test/input和hdfs dfs -put传个文件上去,然后hdfs dfs -ls看结果,最后去 Web UI 点开文件详情,确认三个副本分布在三个不同的 DataNode 上,这才算存储链路真的通了。

4.4 跑一个真实作业做端到端验证

存储通了不等于计算通了,必须跑一个 MapReduce 作业验证 YARN。最省事的是用 Hadoop 自带的示例 jar,在$HADOOP_HOME/share/hadoop/mapreduce/下找一个hadoop-mapreduce-examples-3.3.6.jar,执行:

hadoop jar hadoop-mapreduce-examples-3.3.6.jar wordcount /test/input /test/output

提交后去 8088 页面看应用状态,从ACCEPTED到RUNNING再到FINISHED。如果卡在ACCEPTED超过一分钟,说明调度器分配不出容器,回到内存配置检查。如果进入RUNNING后失败,去 19888 历史服务器看具体任务的日志,常见的失败是容器内存不足被 kill,日志里会有Container [pid=xxx] is running beyond physical memory limits。

作业成功后会看到Job ... completed successfully,然后用hdfs dfs -cat /test/output/part-r-00000看词频结果。这一步我建议一定做完,因为它是唯一能把 HDFS 读写、YARN 调度、MapReduce 执行三条链路一次性验证通过的方式。很多教程止步于jps看到进程就收工,结果真正用的时候才发现作业跑不起来。

5. 日常运维:启停脚本、日志与故障排查

5.1 一键启停脚本的编写

每次手动start-dfs.sh加start-yarn.sh加启动历史服务器,三台机器来回敲,很容易漏。写个脚本能省很多事:

#!/bin/bash case $1 in "start") echo "========== 启动 Hadoop 集群 ==========" ssh hadoop101 "$HADOOP_HOME/sbin/start-dfs.sh" ssh hadoop101 "$HADOOP_HOME/sbin/start-yarn.sh" ssh hadoop101 "mapred --daemon start historyserver" ;; "stop") echo "========== 停止 Hadoop 集群 ==========" ssh hadoop101 "mapred --daemon stop historyserver" ssh hadoop101 "$HADOOP_HOME/sbin/stop-yarn.sh" ssh hadoop101 "$HADOOP_HOME/sbin/stop-dfs.sh" ;; *) echo "Usage: cluster.sh start|stop" ;; esac

停止顺序和启动顺序相反,先停 YARN 再停 DFS。如果反过来,YARN 的容器可能还在写 HDFS,突然断掉会留下一堆Under-Replicated的临时块,下次启动要等副本恢复。

脚本里用ssh hadoop101只是为了统一入口,实际执行还是在那台机器上。如果你的 NameNode 在别的机器上,改成对应主机名即可。

5.2 日志体系与关键日志位置

Hadoop 的日志分散在$HADOOP_HOME/logs/下,命名规则是hadoop-当前用户-角色-主机名.log,另外还有按天滚动的.log.2024-xx-xx文件。排查问题时优先看当前正在写的那个 .log,历史文件是归档,里面可能是几天前的问题了。

几个高频日志和它们的用途:

角色日志关键词典型问题
NameNodenamenode格式化失败、元数据损坏、安全模式卡住
DataNodedatanodeclusterID 不一致、块损坏、磁盘满
ResourceManagerresourcemanager容器分配失败、调度器异常
NodeManagernodemanager容器启动失败、内存超限、本地目录权限
SecondaryNameNodesecondarynamenode合并失败、与 NameNode 通信超时

看日志有个技巧:从文件末尾往前看,先找ERROR和FATAL,找到后往下读十行,通常就是完整的原因和上下文。比从头读快得多。

5.3 常见故障速查表

下面这些是我实际遇到过的、以及帮别人排查时最常见的几类问题,整理成表方便对照:

现象可能原因排查命令或动作
DataNode 启动后立刻消失clusterID 不一致对比data/namenode/current/VERSION和data/datanode/current/VERSION里的 clusterID
NameNode 页面只有 1 个 Live Nodehosts 解析失败或防火墙拦截在从节点ping hadoop101,telnet hadoop101 8020
启动脚本卡住不返回SSH 免密失效逐台ssh hadoop10x测试
作业卡在 ACCEPTED 不动容器内存配置不合理检查memory-mb和maximum-allocation-mb的大小关系
报找不到 MRAppMaster 主类缺HADOOP_MAPRED_HOME配置补上三处 env 配置后重启 YARN
容器被杀,退出码 137物理内存或虚拟内存超限关掉 vmem 检查,下调容器内存或调大节点内存
Web UI 删除文件报权限错误staticuser未配置设置hadoop.http.staticuser.user
重启后 NameNode 找不到 fsimage数据目录被清理检查hadoop.tmp.dir是否在/tmp下
时间戳错乱、日志顺序反常节点间时间不同步三台机器date对比,配置时间同步
写入报Could not obtain blockDataNode 全部异常或副本不足hdfs fsck /检查块状态

5.4 几条踩出来的经验

第一条是别急着改一堆参数。新手常见的心态是“网上说要调优”,于是一次性改十几个参数,出问题后完全不知道是哪一个导致的。我的做法是每次只改一项,改完重启、跑一遍 WordCount,确认没问题再改下一项。

第二条是格式化之前先备份配置。hdfs namenode -format会清掉元数据,但不会清配置文件,可如果你在格式化后顺手删了data目录,配置还在,数据没了。更稳妥的做法是把etc/hadoop整个目录复制一份到~/hadoop-conf-backup,重装时直接覆盖回去,省得重新对照参数。

第三条是虚拟机快照要用在关键节点上。基础环境配好(主机名、hosts、JDK、SSH 免密、防火墙关闭)之后打一个快照,配置文件写完再打一个。后面无论怎么折腾崩了,都能五分钟回到干净状态。我因为没打快照,重装过三次环境,每次都是两小时起步。

第四条是先确认单机正常,再怀疑集群。很多“集群问题”其实是某台机器单独就有问题,比如那台机器的 JDK 没装好、磁盘满了、内存不够。判断方法很简单,在那台机器上单独执行hdfs --daemon start datanode,看能不能起来,能起来说明是集群通信问题,起不来就是本机问题。

6. 参数调优与集群扩展的方向

6.1 内存与 vcore 的配置推导

YARN 的资源模型里,内存是硬约束,vcore 是软约束。所谓软约束,是指调度器按 vcore 做分配,但实际 CPU 使用率可能远低于申请值,因为它不做 CPU 隔离。所以内存要算准,vcore 可以适度超配。

以每台 8G 内存、4 核的虚拟机为例,我的计算过程是这样的:操作系统加基础服务预留 2G,Hadoop 守护进程预留 1G,容器可用内存 4G,剩余 1G 作为突发缓冲。这样yarn.nodemanager.resource.memory-mb配 4096。vcore 方面,4 核里有 1 核要分给系统和其他进程,所以cpu-vcores配 3 到 4 都合理,我一般配 4,允许轻度超配。

再往下推任务并发度:如果一个 MapReduce 任务申请 1024MB 内存和 1 个 vcore,那么单节点最多能同时跑 4 个容器。三台机器就是 12 个容器,减去 ApplicationMaster 占的 1 个,实际并行 11 个任务。这个数字决定了你的作业跑得快不快。如果发现作业并行度只有 1,那要么是输入文件只有一个块,要么是minimum-allocation-mb配得太大导致每个容器都占满内存。

6.2 副本数与块大小的取舍

副本数 3 是默认值,也是三节点集群的最优解。有人为了省空间把它改成 2,代价是一台机器故障时可能丢块,因为 HDFS 的副本放置策略是第一个副本在本节点、第二个在另一机架、第三个再换机架,两台机器时这个策略退化,容错性大幅下降。三台机器配 3 副本,正好每个节点一份,任何一台挂掉数据都还在。

块大小 128MB 之前算过,这里补充一个场景:如果你的集群主要存大量小文件(比如几 KB 的日志),块大小的意义不大,真正的瓶颈是 NameNode 的内存,因为每个文件、每个块都占一条元数据记录。这种情况要考虑合并小文件或者上 HDFS 的小文件归档方案,而不是调块大小。反过来,如果存的是超大文件,块调到 256MB 能减少 NameNode 压力,但会增加单块故障时的重传代价。

6.3 横向扩容与节点退役

扩容的流程比初次搭建简单得多:新机器配好主机名、hosts、JDK、SSH 免密,把 Hadoop 目录和配置从现有节点同步过去,然后在workers文件里加上新主机名,在新机器上单独启动 DataNode 和 NodeManager,最后刷新一下。刷新命令是hdfs dfsadmin -refreshNodes,它会让 NameNode 重新读取workers和dfs.hosts配置。

这里有个容易漏的步骤:workers文件改了之后,要分发到所有节点,不只是主节点。如果只有主节点有,从节点上的启停脚本还是按旧列表执行,会出现“启动时少一台、停止时多一台”的错位。

节点退役用dfs.hosts.exclude文件,把要下线的机器写进去,然后执行hdfs dfsadmin -refreshNodes,NameNode 会把该节点上的块逐步复制到其他节点,等副本数满足后再把节点标记为Decommissioned。这个过程可能持续几分钟到几十分钟,取决于数据量。在 Web UI 的Decommissioning Nodes里能看到进度。千万别在下线过程中直接关机,那会导致块丢失,需要手动修复。

扩容后的资源生效要重启 YARN,因为 NodeManager 的可用内存是在启动时读取的,改配置后不重启不生效。重启前先跑yarn node -list记录当前节点状态,重启后再跑一次对比,确认新节点已经加入。

最后说一个我在实际运维中体会最深的地方:完全分布式集群的价值不在于“配置项更多”,而在于它把分布式系统的真实约束摆在你面前。单机时代你可以无视时间、网络、磁盘、内存的差异,多机时代每一样都会以日志的形式找上门。三台虚拟机搭出来的集群跑不了生产负载,但它是理解副本机制、资源调度、故障恢复最便宜的教具。把这套流程走通、把上面那些坑逐个趟过之后,再去看 HA 架构、联邦机制、容器化部署,会发现它们解决的问题和你踩过的坑是一脉相承的。如果后面要往上加东西,我建议的顺序是先加 ZooKeeper 做 NameNode 高可用,再考虑机架感知和存储策略,这两步的收益比调一堆性能参数明显得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 20:24:15

数字孪生技术在智能网联汽车教学中的应用与实现

1. 数字孪生技术如何重塑智能网联教学场景当我在某职业院校首次看到学生通过3D建模的虚拟汽车底盘进行故障诊断时&#xff0c;教学场景的颠覆性变革正在发生。数字孪生&#xff08;Digital Twin&#xff09;作为工业4.0的核心技术&#xff0c;正在教育领域引发"静悄悄的革…

作者头像 李华
网站建设 2026/10/26 20:23:51

基于YOLOv10的昆虫识别系统开发与优化

1. 项目背景与核心价值昆虫识别检测系统在农业病虫害防治、生态监测、生物多样性研究等领域具有重要应用价值。传统人工识别方式效率低下且依赖专业知识&#xff0c;而基于深度学习的自动化识别方案能显著提升工作效率。YOLOv10作为YOLO系列的最新迭代版本&#xff0c;在检测精…

作者头像 李华
网站建设 2026/10/26 20:22:49

线性代数的几何视角:从空间变换到机器学习应用

1. 空间与移动&#xff1a;线性代数的几何视角 第一次接触线性代数时&#xff0c;很多人会被矩阵运算和行列式搞得晕头转向。直到有一天&#xff0c;当我尝试用几何视角理解这些抽象概念时&#xff0c;整个线性代数的世界突然变得清晰起来。空间与移动&#xff0c;这两个看似简…

作者头像 李华
网站建设 2026/10/26 20:19:25

VSCode插件离线安装与版本管理全攻略

1. 为什么需要离线安装VSCode插件在开发工作中&#xff0c;我们经常会遇到以下几种需要离线安装VSCode插件的情况&#xff1a;受限网络环境&#xff1a;很多企业的开发环境出于安全考虑会限制外网访问&#xff0c;特别是禁止连接微软的插件市场。我曾在一家金融机构工作时&…

作者头像 李华
网站建设 2026/10/26 20:19:22

Ubuntu22.04无人值守Crontab工具实战指南

1. 项目概述作为一名在Linux系统管理领域摸爬滚打多年的老运维&#xff0c;我深知定时任务管理对于系统自动化运维的重要性。今天要分享的这个Ubuntu22.04无人值守Crontab工具实战&#xff0c;正是我在实际生产环境中反复验证过的可靠方案。Crontab这个老牌定时任务工具&#x…

作者头像 李华
网站建设 2026/10/26 20:18:51

智能诊断做不深?根源不在AI,而在车云数据架构

做了五年车云相关的工作&#xff0c;从数据接入一路干到诊断算法落地&#xff0c;我越来越频繁地看到同一个现象&#xff1a;车企的智能诊断项目&#xff0c;绝大多数都卡在一个尴尬的位置——说没用吧&#xff0c;demo跑得挺好&#xff1b;说有用吧&#xff0c;真正上线却替代…

作者头像 李华