☰
Ubuntu下Hadoop三节点集群搭建实战:从零到全通
2026/10/22 8:20:19 网站建设 项目流程

搞大数据这行,Ubuntu 下搭 Hadoop 集群几乎是绕不开的第一道坎。我自己前前后后搭过不下几十套环境,从单机伪分布式到三节点、五节点的正式测试集群都碰过,最大的感受是:真正耗时间的不是改配置文件,而是想不明白"为什么是这个值"和"为什么刚才还好好的现在突然起不来了"。这篇文章我把一套三节点集群从零到通的完整过程拆开来讲,每个配置项背后的逻辑、每次启动步骤的先后顺序、以及我实际踩过的坑,都会尽量说透。适合手里有几台 Linux 机器(物理机或虚拟机都行)、准备入门分布式的人,也适合看过官方文档但一直没跑通的朋友。整个流程照着一路做,大概一到两个小时,只要版本别乱选,基本能一次过。

1. 动手之前:版本选型是第一步,也是最容易翻车的一步

1.1 为什么推荐 Ubuntu 20.04 LTS + Hadoop 3.3.x

搭建 Hadoop 集群的第一步不是下载安装包,而是把版本组合定下来。很多人上来就装最新版 Ubuntu、最新版 Hadoop,结果一堆兼容性问题来回折腾,心态直接崩掉。

我个人的建议组合是:Ubuntu 20.04 LTS(或 22.04 LTS)、OpenJDK 8、Apache Hadoop 3.3.6。这套组合在社区里验证最充分,坑最少。

  • OpenJDK 8 是 Hadoop 官方文档里最保守的选择。虽然 Hadoop 3.3 也支持 JDK 11,但实际运行时我发现某些工具链和自带脚本对 JDK 11 的兼容性还是有点小毛病,JDK 8 最稳。
  • Hadoop 3.3.x 是 3.x 系列里经过最多生产验证的版本,3.4、3.5 我也用过,新特性确实多,但对于学习或者跑常规任务,没必要冒险。
  • Ubuntu 20.04 LTS 的 glibc、系统库和 Hadoop 原生命令(如压缩)配合得很好,装完基本不用额外编译。

注意:很多人喜欢用 CentOS 的思维来操作 Ubuntu,比如直接rpm -qa查包、改hostname的姿势不一样。其实 Ubuntu 下很多操作更简单,但需要换一套习惯。

1.2 三节点集群怎么规划

集群规划不需要很复杂,三台机器就够了:一台作为主节点(NameNode + ResourceManager),两台作为数据节点(DataNode + NodeManager)。我这里用 node1、node2、node3 来称呼它们。

硬件上,每台机器至少 4GB 内存、40GB 磁盘。如果是在虚拟机里跑,我强烈建议给主节点多分一点内存,至少 2GB 给 Java 堆,否则启动的时候会看到java.lang.OutOfMemoryError: Java heap space。磁盘方面,HDFS 副本数默认是 3,这就意味着你每存一份数据,实际会占用三份空间,所以磁盘规划一定要留足余量。

软件和网络方面,需要提前确认:

  • 三台机器之间能互相 ping 通,且走的是静态 IP,不能是 DHCP 随机分配的地址。
  • 已准备好统一的用户名(比如hadoop),我一般不建议直接用 root,后面讲原因。
  • 能访问外网,或者提前把安装包下载好再内网分发,Ubuntu 下用apt-get在线安装很省事。

2. 基础环境配置:这三步做好,后面少踩一半坑

2.1 静态 IP 与 hosts 映射

Hadoop 集群内部通信依赖主机名映射,所以三台机器的/etc/hosts要一致。以我的环境为例(IP 是虚构的,根据你自己的实际网段修改):

sudo cat >> /etc/hosts <<EOF 192.168.1.101 node1 192.168.1.102 node2 192.168.1.103 node3 EOF

这里有个细节很容易被忽略:/etc/hostname和/etc/hosts里的名字必须对上,同时把127.0.1.1映射的那行注释掉。Ubuntu 默认在/etc/hosts里加了127.0.1.1 你的主机名,如果不处理,NameNode、DataNode 之间通信时会走 loopback,导致跨节点连接失败。这个问题非常隐蔽,日志里报的却是Connection refused,排查起来极耗时间。

改完 hostname 后,执行hostname验证一下。或者干脆重启一次,让网络配置完全生效。

2.2 SSH 免密登录,一次配好省下无数密码

Hadoop 启动脚本(start-dfs.sh、start-yarn.sh)是通过 SSH 远程登录到各节点执行命令的,所以必须配置免密。至少要做到 node1 能免密登录 node1、node2、node3。如果想让 node2、node3 也能互相免密操作,也可以一并配好,这样后续管理方便。

在 node1 上执行:

ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa ssh-copy-id node1 ssh-copy-id node2 ssh-copy-id node3

-P ''表示生成空口令的密钥对,省去每次输入的麻烦。ssh-copy-id会自动把公钥追加到目标机器的~/.ssh/authorized_keys。配置完成后,用ssh node2试一下,如果能直接进入 node2 的命令行,就说明通了。

实操心得:这里最容易被忽略的是权限问题。~/.ssh目录权限必须是 700,authorized_keys文件权限必须是 600,否则 SSH 会拒绝读取公钥。我就曾经因为复制文件时把权限搞乱了,折腾了半天才发现是权限不对。

小技巧:在.ssh/config里可以配置简化登录,但集群内部直接用主机名就行,不需要维护额外配置文件。

2.3 时间同步与防火墙,一劳永逸地处理掉

集群节点之间时间不同步,HDFS 和 YARN 都会出现奇怪的状态(比如任务卡住、Kerberos 认证失败等)。在 Ubuntu 上安装并启动 NTP 服务:

sudo apt-get update sudo apt-get install -y chrony sudo systemctl enable chrony sudo systemctl start chrony

实测下来chrony比传统的ntp更省心,配置默认就好。有时间强迫症的话,可以在三台机器上都执行chronyc tracking查看偏差。Hadoop 对时间精度的要求并没有那么苛刻,但偏差太大一定出问题。

防火墙方面,开发环境里最简单粗暴的方式是直接关闭或者放行所有节点通信:

sudo ufw disable

生产环境当然不能这么干,但如果是本地学习和刚开始搭测试集群,先关掉防火墙,能少一大半网络类疑难杂症。等集群稳定了再按需开放端口。

3. Hadoop 配置文件逐项拆解:每一行都要知道为什么

3.1 目录规划与环境变量

Hadoop 运行时会在本地磁盘写入大量数据,比如 NameNode 的元数据、DataNode 的数据块、日志文件等。我习惯把数据目录统一规划在/data下,和系统盘分离:

# 每台机器上执行 sudo mkdir -p /data/hadoop/{namenode,datanode} sudo mkdir -p /data/hadoop/logs sudo chown -R hadoop:hadoop /data

为什么要这样分?因为 NameNode 的元数据极其重要,如果和系统盘放在一起,一旦磁盘满或者系统重装,元数据就丢了。实际生产环境中,NameNode 目录建议放在单独挂载的磁盘或者 SSD 上,DataNode 目录可以放多块磁盘,分别挂载。

然后是环境变量文件~/.bashrc,三台机器都加上:

export HADOOP_HOME=/opt/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

执行source ~/.bashrc后,用hadoop version验证是否识别命令。如果java -version正常但hadoop version报错,多半是JAVA_HOME没配好,Hadoop 脚本对JAVA_HOME的依赖非常敏感。

注意:不要在图省事把HADOOP_HOME写在/etc/profile里然后只在一台机器上配置,要给三台都配。另外,我习惯用普通用户hadoop来跑所有 Hadoop 进程,因为官方很多脚本里的用户判断逻辑(比如权限检查)在 root 下容易触发异常。

3.2 core-site.xml 与 hdfs-site.xml:核心核心还是核心

进入$HADOOP_HOME/etc/hadoop/目录,接下来的所有配置都在这里。先看core-site.xml:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node1:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> </configuration>

fs.defaultFS是 HDFS 的入口地址,所有客户端(包括命令行工具和 YARN 任务)都会通过这个地址访问 NameNode。端口 8020 是 NameNode 的 RPC 通信端口,不要和后面看到的 9870(Web UI 端口)搞混。

hadoop.tmp.dir是很多临时文件的根目录,NameNode 和 DataNode 的元数据目录默认都会基于它展开。这个参数很多人忽略,导致数据落到了/tmp分区,重启后莫名其妙丢了,非常坑。现在我们在/data/hadoop/tmp下显式指定,一劳永逸。

接下来是hdfs-site.xml:

<configuration> <property> <name>dfs.namenode.name.dir</name> <value>/data/hadoop/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/data/hadoop/datanode</value> </property> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.http.address</name> <value>node1:9870</value> </property> <property> <name>dfs.permissions.enabled</name> <value>false</value> </property> </configuration>

dfs.namenode.name.dir和dfs.datanode.data.dir分别指定 NameNode 元数据目录和数据块存储目录。小集群里我倾向于显式写死,不依赖默认路径,这样排查问题定位更快。

dfs.replication我设为 2,因为三台节点里一台挂了,副本数 2 仍然能保证数据可用。如果你只有三台机器且磁盘充足,也可以直接设 3。这个值可以从 hdfs-site.xml 改成更高,但必须在客户端配置里保持一致。

dfs.permissions.enabled设为 false,是为了开发测试方便,避免每次操作文件都要纠结 owner 和权限。生产环境建议恢复默认的 true 并配合 Kerberos/KNOX,但测试环境先跑通再说。

3.3 yarn-site.xml 与 mapred-site.xml:资源管理不能乱配

只配 HDFS 不够,跑 MapReduce(或者更上层的 Spark、Hive)还需要 YARN 来统一管理资源。先看yarn-site.xml:

<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>node1</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.aux-services.mapreduce_shuffle.class</name> <value>org.apache.hadoop.mapred.ShuffleHandler</value> </property> </configuration>

yarn.resourcemanager.hostname指向运行 ResourceManager 的节点,这里是 node1。mapreduce_shuffle是 MapReduce 在 YARN 上运行所需的辅助服务,没有它,Reduce 阶段无法从各个 NodeManager 拉取数据。

接下来mapred-site.xml只需要一个关键配置:

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

这行表明 MapReduce 任务跑在 YARN 框架上,而不是本地模式。如果忘了配,任务会默默以本地模式运行,看着跑完了但没有任何分布式效果,很不合预期。

关于资源调优,在小集群上我一般会在yarn-site.xml里设置每台 NodeManager 可用内存:

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

这是基于每台机器 4GB 内存、且 HDFS 和 YARN 共存的约束下做的保守设置。如果你机器内存大,可以适当调大,但一定要给操作系统和其他进程留出余量。

3.4 workers 文件与分发配置

Hadoop 3.x 里,集群的数据节点列表写在workers文件(2.x 时代叫slaves)。这个文件只需要在主节点上配置,写上所有 DataNode 对应的主机名:

# 编辑 $HADOOP_HOME/etc/hadoop/workers cat > workers <<EOF node2 node3 EOF

注意:不要把node1写进去。虽然主节点也可以当 DataNode 用,但在三节点集群里这样的混合角色会增加调试复杂度。如果你确定要让 node1 也参与存储,再写入node1。

配置文件全部写好之后,把整个$HADOOP_HOME目录同步到 node2 和 node3:

cd /opt tar -czf hadoop.tar.gz hadoop scp hadoop.tar.gz node2:/opt/ scp hadoop.tar.gz node3:/opt/ # 在 node2、node3 上解压 sudo tar -xzf hadoop.tar.gz -C /opt/

同步的时候要特别注意权限。如果 tar 包是用 root 打的,解压之后他机的目录 owner 会是 root,后面用hadoop用户启动会报权限不足。我在这一步吃过亏,最稳妥的做法是在每台机器上用同一个hadoop用户操作,保持目录权限一致。

4. 启动流程:格式化、启进程、页面验证,一条龙

4.1 首次启动:先格式化 NameNode,再按顺序启动

所有配置就位后,先别急着 start-dfs.sh。首次启动前必须格式化 NameNode,这一步相当于给 HDFS 文件系统初始化一个空的元数据仓库:

# 在 node1 上,使用 hadoop 用户 hdfs namenode -format

格式化完成会看到Storage directory /data/hadoop/namenode has been successfully formatted这样的提示。如果你在日志里看到类似ClusterID ... UUID ...的内容,说明成功了。

格式化的本质是往dfs.namenode.name.dir目录写入初始的fsimage元数据。这个操作只能做一次。如果集群运行后再格式化,会将已保留的数据视为另一个集群(clusterID 不一致),导致 DataNode 连接失败。所以格式化之前,确认数据可空,或者做好备份。这是我见过最多人踩的坑:集群跑了一阵,磁盘满了,有人去格式化,然后所有 DataNode 都连不上 NameNode,数据全部"丢失"。

格式化完成之后,依次启动:

start-dfs.sh start-yarn.sh

执行时脚本会通过 SSH 远程到各节点启动进程。如果看到需要输密码,先回看 2.2 节的免密配置。

全部启动后,在主节点执行jps查看 Java 进程:

jps

正常情况 node1 上应该看到NameNode、ResourceManager、SecondaryNameNode(如果你启用了,默认在hdfs-site.xml里配置的辅助节点上),node2 和 node3 上应该看到DataNode、NodeManager。

如果某台机器上DataNode缺失,优先去$HADOOP_HOME/logs目录下看hadoop-hadoop-datanode-主机名.log的报错信息,而不是盲目重启。

4.2 页面验证与基础命令

进程起来之后,用浏览器验证 Web 界面:

  • NameNode Web UI:http://node1:9870
  • YARN ResourceManager Web UI:http://node1:8088

如果打不开,第一反应不是去看防火墙,而是先确认端口是否被监听:

netstat -tlnp | grep 9870

如果端口没监听,说明对应的进程根本没起来,回到日志排查;如果端口在监听但页面访问不了,再检查网络和防火墙。

命令行层面,可以用:hdfs dfsadmin -report查看节点与容量情况。如果 DataNode 正常注册,你会看到三台机器的容量信息,Live nodes 数量为 2(因为这里 node2、node3 是 DataNode)。接着找一个本地文件上传到 HDFS 试一下:

hdfs dfs -mkdir -p /user/hadoop/test echo "hello hadoop" > /tmp/test.txt hdfs dfs -put /tmp/test.txt /user/hadoop/test/ hdfs dfs -cat /user/hadoop/test/test.txt

到这里,HDFS 链路已经通了。再跑一个简单的 MapReduce 作业验证 YARN:

hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi 4 10

能正常算出近似 π 值,就说明从 HDFS、YARN 到容器调度的全链路都通了。

5. 实操中最烦的五个问题与排查思路

5.1 DataNode 一直起不来

这是新手遇到最多的问题。现象的典型代表是:node1 上 NameNode 正常,但两台 DataNode 的进程要么起不来,要么起来几秒就挂掉。

排查思路按顺序走:

  1. 打开对应日志,先看 Exception 关键字。最常见的是Incompatible clusterIDs,原因就是格式化导致 NameNode 的 clusterID 变了,但 DataNode 的dfs.datanode.data.dir里还保留着旧的 clusterID。
  2. 解决方法是把 DataNode 的数据目录清掉,sudo rm -rf /data/hadoop/datanode/*,然后重启 DataNode。但前提是你要清楚这个目录里没有不可恢复的重要数据。
  3. 其次是权限问题。日志中看到Permission denied,检查/data和$HADOOP_HOME目录属主是否为hadoop用户。

我见到过有人反复格式化 NameNode 来解决问题,这实际上是给下一次事故埋雷。正确做法是先记录 clusterID,再清理 DataNode 数据。

5.2 HDFS 一直处于安全模式

安全模式的表现是:文件只能读取不能写入,Web 界面显示Safe mode is ON,命令行执行hdfs dfsadmin -safemode leave也没用,因为离开安全模式需要 NameNode 确认数据块都满足健康比例。

这种情况大多数是 DataNode 没有上报足够的数据块,原因可能是 DataNode 未启动,或者副本数配置和实际节点数不匹配。

排查办法:

hdfs dfsadmin -safemode get hdfs dfsadmin -report

如果-report里 Live nodes 个数小于预期,先解决节点注册问题。如果节点数是正常的,可以等待 NameNode 自动离开安全模式,也可以手动执行hdfs dfsadmin -safemode force -leave强制离开(测试环境常用)。

安全模式本身是 HDFS 的自我保护机制,不是故障。只要数据块健康度恢复到阈值(默认 99.9%),它就会自动退出。

5.3 虚拟机内存不够导致的 OOM

我刚开始搭集群时,每台虚拟机就 2GB 内存,结果一启动start-dfs.sh就有人挂掉。Java 进程默认的 -Xmx 动不动就 1G,三台机器同时启动,2GB 内存根本兜不住。

解决方案:

  1. 把每台机器的物理内存调到 4GB 以上。虚拟机设置里面直接改,成本很低。
  2. 调整 Hadoop 启动脚本中的堆内存参数,编辑$HADOOP_HOME/etc/hadoop/hadoop-env.sh:
export HADOOP_HEAPSIZE_MIN=512 export HADOOP_HEAPSIZE_MAX=1024 export HADOOP_NAMENODE_OPTS="-Xmx1024m $HADOOP_NAMENODE_OPTS"

所以如果资源紧张,可以显著调低这两个进程的堆内存,避免整体内存超限。

5.4 任务挂起,卡在 ACCEPTED 或 RUNNING

YARN 任务一直不执行,多半是资源不足。比如内存都被其他进程占了,NodeManager 空闲资源为零,任务排不上队。

在 ResourceManager 页面的Active Nodes标签页里查看可用内存,如果总是 0MB,说明要么 NodeManager 没正常上报资源,要么资源被其他任务占满。如果集群没有其他大任务,那就是yarn.nodemanager.resource.memory-mb设置过大,操作系统实际空闲内存根本达不到这个值,导致无法分配新容器。

比较隐蔽的原因还有容器内存超出限制导致 NodeManager 反复杀进程。日志里如果出现Container killed on request. Exit code is 143,就要检查yarn.scheduler.maximum-allocation-mb和每个任务的容器内存是否匹配。

5.5 临时文件清理与重启注意

集群运行久了,/tmp下的临时文件、yarn.nodemanager.local-dirs里的中间结果、以及日志文件会把磁盘塞满。尤其是日志,Hadoop 默认不清理,跑几个任务就能看到logs目录巨大。

我习惯配一个简单的定时清理脚本,把超过 7 天的日志压缩后保留,测试环境直接删掉也没关系。另一个经验是重启集群的规范顺序:

stop-yarn.sh stop-dfs.sh

不要直接kill -9进程,除非内存耗尽真的没响应了。非正常停止可能导致 HDFS 进入安全模式或者 edit log 损坏。

6. 集群日常维护与调优的一点体会

6.1 磁盘与容量监控

小集群最容易忽略的其实不是配置,而是磁盘容量。HDFS 默认副本数如果设为 3,三台机器每台 40GB,实际可用存储空间就只有 40GB 左右,跑两个真实数据集就满了。所以日常运维要养成hdfs dfsadmin -report看习惯。

另外一个细节是df -h和 HDFS 的报告结果不一样。HDFS 的报告看的是数据块占用,而系统磁盘还包含日志、临时目录等,所以要两个都关注。我碰到过一次系统磁盘 100% 被日志占满,但 HDFS 层面完全正常,任务却起不来的情况。

6.2 常用运维命令速查

整理一下平时用得最多的命令,方便对着操作:

场景命令
检查集群节点状态hdfs dfsadmin -report
创建目录hdfs dfs -mkdir -p /tmp/test
上传文件hdfs dfs -put local_file /tmp/test/
下载文件hdfs dfs -get /tmp/test/file ./
查看文件列表hdfs dfs -ls /tmp/test
删除文件hdfs dfs -rm -r /tmp/test
查看集群文件总数hdfs dfs -count /
安全模式状态hdfs dfsadmin -safemode get
强制离开安全模式hdfs dfsadmin -safemode force -leave
查看日志tail -f $HADOOP_HOME/logs/hadoop-hadoop-*.log
重新加载配置hdfs dfsadmin -refreshNodes

6.3 一个容易被忽略的调优点

在测试环境跑得好好的集群,一上真实的稍大数据就慢,这个现象很常见。除了资源大小之外,我强烈建议把 HDFS 默认块大小调大。默认dfs.blocksize是 128MB,如果你的数据文件都是一两个 GB 的大文件,其实问题不大。但如果你大量的小文件(比如几 KB 的日志),NameNode 内存会被海量文件元数据拖垮,任务调度也会变慢。

处理办法有两种:一是尽量将小文件合并成大文件;二是针对大文件场景可以调高块大小。但这属于业务层面的取舍,没有统一答案。

另外,NameNode 的堆大小对集群稳定性影响极大。有些问题看起来是 DataNode 掉线,实际是 NameNode 内存被撑爆后触发了 Full GC,导致所有节点心跳超时。所以我会在hadoop-env.sh里单独给 NameNode 设置稍大的堆内存,给 DataNode 保持常规设置,这个区分对待的做法实测下来很有效。

搭 Hadoop 集群这件事,只要把版本、网络、配置、启动顺序这四件事顺序捋清楚,就能少走非常多弯路。这里面的每个步骤我在实际项目中都至少踩过一遍,尤其是格式化这个操作,真是一念之差数据全无。现在每次配置新集群,我第一件事就是确认是不是第一次格式化,第二件事一定是确认目录权限和 clusterID,这两件事没问题,集群基本就成功了一半。希望这篇记录能让你在搭集群的时候少掉一些头发。

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

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

立即咨询