直接上手在 Ubuntu 上搭 Hadoop 集群,光看官方文档真的容易劝退。版本对不上、Java 路径写错、SSH 免密没配好,随便哪个坑都能卡你一整天。这篇我用自己实际部署的经验,从环境准备、配置文件逐项拆解,到格式化、启动、验证,把每一步为什么这么做讲清楚,最后附上高频踩坑记录,照着抄基本不会出大问题。
1. 搭建前的整体设计与环境准备
1.1 集群角色划分与硬件规划思路
Hadoop 集群最常见的部署方式是主从架构,一主多从或者多主多从。对于学习环境或者中小规模数据实验,一台主节点加两台从节点是最经济也最踩坑最全的组合。主节点跑 NameNode、SecondaryNameNode 和 ResourceManager,从节点跑 DataNode 和 NodeManager。也可以把主节点的 ResourceManager 单独拆到第三台机器上,但那样对机器数量和网络配置要求更高,不推荐新手一上来就搞复杂拓扑。
部署之前先把机器 IP、主机名、角色规划好。以三台为例:
| 节点 | 主机名 | IP(示例) | 角色 |
|---|---|---|---|
| 主节点 | hadoop-master | 192.168.1.101 | NameNode、ResourceManager、SecondaryNameNode |
| 从节点1 | hadoop-slave1 | 192.168.1.102 | DataNode、NodeManager |
| 从节点2 | hadoop-slave2 | 192.168.1.103 | DataNode、NodeManager |
主机名规范很重要,不要随便起名,后面 hosts 映射、HDFS 目录路径、日志输出里到处都会出现主机名,不一致就排查到哭。我自己习惯统一用hadoop-master、hadoop-slave1这种语义化命名,一眼能看出角色。
内存规划也要心里有数。NameNode 和 ResourceManager 分别在 JVM 堆内存上跑,DataNode 节点上同时跑 DataNode 和 NodeManager,内存紧张时很容易互相挤占。建议主节点至少 4GB,从节点至少 2GB,如果是虚拟机,每台 2GB 起步勉强能跑,但别指望跑大数据量任务。
1.2 系统环境、JDK 版本与 SSH 免密配置
Ubuntu 版本选择上,18.04、20.04、22.04 我都试过,Hadoop 3.3.x 在这几个版本上都能正常跑,前提是 Java 版本匹配。JDK 一定不要用最新版,Hadoop 对 Java 版本支持是滞后的。Hadoop 3.3.x 官方要求 Java 8 或 Java 11,实测 OpenJDK 8 最稳。
安装 Java 之前先更新系统源,用官方源可能比较慢,可以换成国内镜像源。这里我踩过一个坑,换源之后 Java 包版本不完整,导致编译环境缺失,后面配置hadoop-env.sh时 JVM 参数一直报错,所以建议先确认源可用再继续。
sudo apt update sudo apt install -y openjdk-8-jdk装完以后确认版本和路径:
java -version readlink -f $(which java)readlink -f拿到的路径后面配置JAVA_HOME要用。很多文章写/usr/lib/jvm/java-8-openjdk-amd64,这个在 18.04 和 20.04 上是对的,但 22.04 默认源里可能没有 openjdk-8,需要额外配置源。所以最好自己确认一下。
SSH 免密这块是整个安装过程中最容易被忽略又最容易出错的环节。Hadoop 启动时需要主节点通过 SSH 无密码登录到所有从节点,去拉起 DataNode 和 NodeManager 进程。如果免密配置失败,启动脚本不会直接报错,但你会发现从节点上 DataNode 进程根本不存在。
配置免密在三台机器上都要执行:
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa然后把主节点的公钥加到所有节点(包括主节点自己)的authorized_keys里:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys如果希望更省事,直接用ssh-copy-id:
ssh-copy-id hadoop-master ssh-copy-id hadoop-slave1 ssh-copy-id hadoop-slave2验证方式很简单,从主节点 SSH 到每个主机名,如果不要求输入密码,说明连通了。很多人在这一步被坑,原因往往是.ssh目录权限不对。authorized_keys文件权限必须是 600,.ssh目录权限必须是 700,否则 SSH 会忽略公钥文件。这个特性是 OpenSSH 的安全机制,权限太宽松反而拒绝认证。
注意:SSH 配置完成后,一定要用主机名而不是 IP 去测试连通性。因为 Hadoop 内部的进程间通信使用主机名解析,如果 IP 能连通而主机名不通,启动后依然会有一堆 connection refused。
2. Hadoop 版本选型与核心配置文件逐项拆解
2.1 版本选择与安装目录规划
Hadoop 版本选型直接影响后面配置文件的字段差异。Hadoop 2.x 和 3.x 在端口号、配置文件字段上区别很大,比如 NameNode Web UI 端口,2.x 是 50070,3.x 变成了 9870。如果照着 2.x 教程配 3.x,Web UI 永远打不开。建议直接用 Hadoop 3.3.x 系列,它已经非常稳定,社区里踩坑资料也齐全。
下载 Hadoop 时不要用 GitHub 上的 release 包,从 Apache 官方镜像站下载 tar.gz 包最稳妥。下载完解压到/opt目录,并做软链方便后续升级:
sudo tar -xzf hadoop-3.3.6.tar.gz -C /opt cd /opt && sudo ln -s hadoop-3.3.6 hadoop sudo chown -R hdfs:hdfs /opt/hadoop这里创建专门用户hdfs(也可以叫hadoop)而不是直接用 root,是安全考虑。Hadoop 的 DataNode 会以该用户身份写 HDFS 数据,权限管控上更清晰。如果你不想单独建用户,直接用自己的普通用户也行,但三台机器的用户名必须一致,否则跨节点通信时权限和路径会有各种诡异问题。
2.2 五个核心配置文件的配置逻辑
Hadoop 的配置集中在$HADOOP_HOME/etc/hadoop/目录下,核心就四个文件:core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml,加上workers(旧版本叫slaves)。
第一个core-site.xml,这个文件决定了集群的默认文件系统地址和临时目录。关键是fs.defaultFS这个属性,它指定了 NameNode 的 RPC 地址,也就是 HDFS 的入口。很多新手在这里写错端口,一旦端口不一致,客户端根本找不到 NameNode。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://hadoop-master:9820</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/data/tmp</value> </property> </configuration>端口 9820 是 Hadoop 3.x 的 NameNode RPC 默认端口,2.x 是 8020 或者 9000,这个细节很容易忽略。hadoop.tmp.dir是 NameNode 和 DataNode 存储元数据和数据块的根目录,不配置的话默认在/tmp下,系统一重启数据全没了,这是生产环境绝对要避免的。
第二个hdfs-site.xml,它控制 HDFS 自身的属性。dfs.replication决定每个块有几个副本,三台节点的集群设置成 2 比较合理。如果你保留默认的 3,而集群只有两个 DataNode,多余的副本会一直处于复制等待状态,空间浪费还容易误判为故障。
<configuration> <property> <name>dfs.replication</name> <value>2</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> <property> <name>dfs.namenode.http-address</name> <value>hadoop-master:9870</value> </property> </configuration>dfs.namenode.name.dir是 NameNode 元数据存储路径,dfs.datanode.data.dir是 DataNode 数据块存储路径。这两个目录在格式化之前要手动创建好,并且确保属主是 Hadoop 用户,否则启动时会因为目录权限问题直接失败。
第三个yarn-site.xml,它管理计算资源调度。核心配置是 ResourceManager 所在的主机名,以及是否启用 NodeManager 的辅助服务。很多人以为 YARN 和 HDFS 必须同机,其实可以分开部署,不过小集群放一起最省事。
<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>hadoop-master</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration>aux-services是 YARN 的 NodeManager 辅助服务配置,MapReduce 任务跑在 YARN 上时必须配置mapreduce_shuffle,否则提交作业时会报错“Shuffle handler is not enabled”。
第四个mapred-site.xml,这个文件原本在 Hadoop 2.x 中是不存在的,需要从模板复制出来,3.x 版本安装包里自带模板。配置文件内容很简单,就是告诉 MapReduce 使用 YARN 作为底层资源调度框架:
<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration>这四个文件的配置逻辑其实可以概括为:core-site.xml是全局入口,hdfs-site.xml管存储,yarn-site.xml管资源,mapred-site.xml管计算框架对接。底层关系上,MapReduce 任务提交到 YARN,YARN 从 HDFS 上读取输入数据,计算完再写回 HDFS。
2.3 环境变量与 workers 节点列表配置
配置完核心 XML 后,还要改两个文件。第一个是hadoop-env.sh,里面设置JAVA_HOME。这里必须写 JDK 的绝对路径,写JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64,不要在配置文件里写java命令,因为 Hadoop 启动脚本在某些环境下不会加载用户的~/.bashrc里的环境变量。
第二个是workers文件,这个文件列出了所有 DataNode 和 NodeManager 所在的主机名,一行一个,不含主节点。注意这个文件在 Hadoop 3.x 里叫workers,在 Hadoop 2.x 里叫slaves,版本变了很多人还在改那个旧文件,结果集群启动脚本扫描到文件名不对,DataNode 永远起不来。
# workers 文件内容 hadoop-slave1 hadoop-slave2环境变量配置写入/etc/profile.d/hadoop.sh,内容如下:
export HADOOP_HOME=/opt/hadoop export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin改完后source /etc/profile.d/hadoop.sh,用hadoop version验证环境变量是否生效。从节点上只需要安装 Hadoop 并配置相同的 XML 文件和环境变量,不需要执行格式化操作,这个后面细说。
3. 从格式化到启动的完整实操过程
3.1 格式化 NameNode 的时机与坑位
所有配置完成后,第一件大事就是格式化 NameNode。hdfs namenode -format这个命令会在 NameNode 的元数据目录下生成一个current/VERSION文件,里面是有集群唯一标识的clusterID。
格式化的时机很讲究。必须是首次启动集群之前格式化,而且只需要在一台机器上执行。如果集群曾经启动过,因为配置变化需要重新格式化,必须先清空 NameNode 和所有 DataNode 的数据目录,否则会出现clusterID不一致的问题。这个坑我印象太深了:DataNode 启动时会在上报心跳,NameNode 发现两边clusterID不匹配就直接拒绝注册,日志里疯狂刷Incompatible clusterIDs。
格式化之前,确认三台机器的所有数据目录都准备完毕:
hdfs namenode -format看到输出中Storage directory ... has been successfully formatted就说明成了。如果格式化了多次导致 ID 冲突,办法是同时删除namenode/current和所有 DataNode 的datanode/current目录,然后重新格式化。注意这种操作相当于清空整个集群的数据,生产环境不能随便做。
3.2 启动 HDFS 和 YARN 的具体步骤
启动顺序上,先启动 HDFS 再启动 YARN。HDFS 是存储层,YARN 的资源调度依赖 HDFS 提供输入数据和写入输出结果。
先在主节点执行:
start-dfs.sh这个脚本会读取workers文件,通过 SSH 到每个从节点启动 DataNode,本地启动 NameNode 和 SecondaryNameNode。启动过程中如果 SSH 免密配置正确,整个过程不需要输入密码。
启动后务必使用jps检查进程状态。
主节点应该看到:
- NameNode
- SecondaryNameNode
- ResourceManager
从节点应该看到:
- DataNode
- NodeManager
如果某个进程缺失,不要急着重启整个集群,先看日志。HDFS 日志在$HADOOP_HOME/logs/目录下,YARN 日志也一样。日志文件名一般是hadoop-hdfs-datanode-主机名.log、yarn-hadoop-resourcemanager-主机名.log这类格式。
HDFS 启动之后接着启动 YARN:
start-yarn.sh启动完以后,再次用jps确认 ResourceManager 在主节点,NodeManager 在每个从节点。
3.3 通过 Web UI 与命令行验证集群状态
进程全部拉起后,还需要验证功能是否正常。第一条路径是 Web UI:
- HDFS 管理界面:
http://hadoop-master:9870 - YARN 资源管理界面:
http://hadoop-master:8088
浏览器打开 HDFS 页面,能看到 Live Nodes 数量和集群存储容量。如果Live Nodes数字小于从节点数,直接看 DataNode 日志。YARN 页面的Active Nodes也是同理。
命令行验证用 DFS 命令。上传一个测试文件到 HDFS,再把它拉回来,对比校验值:
echo "hello hadoop cluster" > /tmp/test.txt hdfs dfs -mkdir -p /user/hadoop hdfs dfs -put /tmp/test.txt /user/hadoop/test.txt hdfs dfs -cat /user/hadoop/test.txt如果能看到内容输出,HDFS 的读写链路已经通了。再跑一个经典 WordCount 案例来验证计算链路,虽然在小集群上有点小题大做,但能同时打通 YARN、MapReduce 和 HDFS 全链路:
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /user/hadoop/test.txt /user/hadoop/output跑完后确认输出目录下是否有_SUCCESS文件,有就是作业成功。如果没有,查看 YARN 日志。
提示:Hadoop 3.x 会在
hdfs dfs命令时提示使用file://协议,如果你操作的是 HDFS 就不需要,只有操作本地文件系统时才需要。
4. 高频踩坑实录与排查思路
4.1 SSH 免密、主机名解析类的疑难杂症
这类问题最典型的症状就是start-dfs.sh执行到从节点时卡住或报Permission denied (publickey)。排查思路先看权限:
ls -la ~/.ssh/authorized_keys必须是-rw-------,.ssh目录是drwx------。另外检查一下三台机器的~/.ssh/known_hosts,有时候公钥信息过期会导致 SSH 连接时报 Host key verification failed。
如果权限没问题,手动 SSH 一次从节点,看是否有报错细节。顺便确认从节点的 SSH 服务已开启:
sudo systemctl status ssh主机名解析问题一般在/etc/hosts文件,三台机器都要添加相同的主机名映射:
192.168.1.101 hadoop-master 192.168.1.102 hadoop-slave1 192.168.1.103 hadoop-slave2这里有个常见的认知误区:只在主节点配置 hosts 是不够的。DataNode 启动时向 NameNode 注册,双方的通信需要各自解析主机名,如果从节点解析不了主节点的主机名,NameNode 地址就无法连接。我见过太多案例,主节点一切正常,从节点一直报 connection refused,一看 hosts 文件根本没改。
4.2 Java 版本不匹配与内存参数调优
Hadoop 的 Java 依赖非常严格。装了 Java 17 跑 Hadoop 3.3.x 在某些场景会报IllegalArgumentException,但错误信息又不够明显,很多人排查半天才发现是 Java 版本问题。强烈建议统一 OpenJDK 8,虽然不是所有场景都会出问题,但至少避开了大部分已知不兼容项。
内存参数的问题主要在hadoop-env.sh里的HADOOP_HEAPSIZE和HADOOP_OPTS。如果机器内存有限,默认的 1GB 堆内存可能让 JVM 直接 OOM。可以在hadoop-env.sh里设置:
export HADOOP_HEAPSIZE=512NameNode 和 ResourceManager 各自都在 JVM 上跑,如果机器只有 2GB 内存,三四个 JVM 进程会直接吃满内存,表现就是系统卡顿、进程被内核杀掉。可以把YARN_RESOURCEMANAGER_HEAPSIZE和YARN_NODEMANAGER_HEAPSIZE调小,或者用 swap 文件撑一下。
NodeManager 内存不足的问题通常体现为容器启动失败,YARN 页面里Diagnostics信息会写明内存不足。这时候要去修改yarn-site.xml中的yarn.nodemanager.resource.memory-mb,默认是物理内存的 80%,看起来很大,实际上每个容器一分配就容易超。
4.3 格式化之后 NameNode 起不来、DataNode 反复掉线
格式化后 NameNode 起不来的典型案例是目录权限。dfs.namenode.name.dir指向的目录不存在或者属主不是当前用户,都会导致启动失败。解决办法:
sudo mkdir -p /opt/hadoop/data/namenode sudo mkdir -p /opt/hadoop/data/datanode sudo chown -R hdfs:hdfs /opt/hadoop/data格式化完成后千万别急着 restart,先jps确认没有残留进程。如果有残留的 NameNode,先stop-dfs.sh杀掉再启动,不然新进程会因端口占用直接退出。
DataNode 反复掉线的场景,除了前面说的clusterID不一致,还有一个隐蔽的原因:DataNode 的dfs.datanode.data.dir磁盘空间不足。HDFS 默认有个dfs.datanode.du.reserved参数,预留空间不足会拒绝写入新块。小集群可能不明显,时间久了磁盘一满就容易误判成节点故障。
这块我个人的习惯是,先把日志打开,看 DataNode 上报失败的原始报错,是Incompatible clusterIDs这种明确的信息就按上面方法清理重启。如果日志信息很模糊,直接grep -i error过滤日志文件,往往能直接看到根本原因,比瞎猜效率高得多。
5. 个人实操心得与调优建议
5.1 几个值得花时间注意的细节
一个内容是,hostname和hostnamectl命令要配合使用。很多时候你改了/etc/hostname,但当前终端的主机名还是旧的,导致 Hadoop 各类日志里混淆。建议改完主机名后用hostnamectl set-hostname hadoop-master保存,然后重新登录。
还有一个容易被忽略的文件是/etc/hosts中不要写 IPv6 的::1映射和主机名关联,某些场景下 JVM 会优先解析到 IPv6 地址导致连接失败。可以在启动脚本前设置export HADOOP_OPTS="-Djava.net.preferIPv4Stack=true",或者直接在/etc/hosts中注释掉 IPv6 相关行。
配置文件分散在三台机器,维护时保持副本一致性很重要。我平时的做法是改完主节点配置后直接 scp 分发:
scp -r $HADOOP_HOME/etc/hadoop/* hadoop-slave1:$HADOOP_HOME/etc/hadoop/ scp -r $HADOOP_HOME/etc/hadoop/* hadoop-slave2:$HADOOP_HOME/etc/hadoop/不要手工一台台改,两个文件的内容写错一个标点就是半天白干。
5.2 集群关闭与重启的经验
关闭集群时顺序和启动正好相反,先关 YARN 再关 HDFS:
stop-yarn.sh stop-dfs.sh如果碰到进程明明停了但端口还占着,可以用lsof -i:9870找到残留进程手动 kill,但别一上来就 kill -9,先试试正常stop-*.sh脚本,避免元数据写入不完整。
重启集群时不需要重新格式化。反过来,如果有人误以为每次重启要hdfs namenode -format,集群数据就全丢了。只有第一次启动才需要格式化,重启只需要正常 start 脚本。
数据安全上,NameNode 的元数据目录最好做定时备份,最粗暴的办法是定期打包namenode/current目录到另一台机器。毕竟fsimage和edits文件承载了集群所有目录和文件的位置信息,一旦损坏,整个集群基本废了。小规模实验集群可能不会遇到这个问题,但养成备份习惯总没有坏处。
5.3 集群横向扩展的思路
三台集群跑通以后,如果想扩展到更多节点,流程很简单:新节点安装 JDK 和 Hadoop,配置相同 XML 和workers文件加一行新主机名,然后在新节点上手动启动 DataNode 和 NodeManager。不需要格式化,不需要动已有节点上的数据和元数据。
实际操作中我倾向于在新节点单独启动:
hdfs --daemon start datanode yarn --daemon start nodemanager这样不用重启整个集群,HDFS 和 YARN 会自动感知新节点加入。几秒钟后,HDFS Web UI 的 Live Nodes 应该就多了一台。这种滚式扩展方式对线上环境来说非常友好,不过新增节点时最好观察一下数据均衡状态,块分配不均衡时可以用hdfs balancer触发均衡任务,让它自动调配。
搭建 Hadoop 集群本身不是目的,能跑通一个完整的离线计算链路才是价值所在。我在实际操作中最深刻的体会是:配置文件字段和端口号一定要以你下载的版本为准,别拿老版本教程硬套新版本。另外,日志永远是第一排查入口,学会看logs目录下各种.log文件,比到处搜答案快得多。这套环境跑顺之后,后面玩 Hive、Spark、Flink 都只是顺着现有框架加组件的问题,地基打牢了,上层才能稳。