先说个背景。平时遇到很多朋友学大数据第一反应就是把自己工作电脑当实验场直接在物理机上装 JDK、Hadoop。装完就后悔系统里其他 Java 项目依赖被环境变量搞乱HDFS 的临时文件把磁盘塞满想模拟多节点集群又找不出第二台机器最后只能重装系统。我的做法是凡是 Hadoop 这类“端口多、权限要求苛刻、依赖系统用户和主机名”的组件一律先扔进虚拟机里隔离着玩。虚拟机的好处是快照可以随时回滚配置乱了直接恢复不用心疼宿主机。今天这篇就是完整复现我在虚拟机里安装 JDK 和 Hadoop 的全过程从虚拟机的资源配置、Linux 系统注意事项到 JDK 安装、SSH 免密、Hadoop 五个配置文件的修改再到格式化 NameNode 和启动验证每个操作我都把背后的原因讲清楚。刚入门的同学可以当作保姆级手册照抄想快速搭一套离线实验环境的中级用户也可以直接按这个流程重跑一遍。1. 动手之前虚拟机资源规划1.1 为什么要把 Hadoop 装在虚拟机里Hadoop 不是一个单进程软件它一启动就是一堆 JVM 进程NameNode、DataNode、ResourceManager、NodeManager 各占一个进程还会在系统里创建自己的数据目录、日志目录对主机名、IP、用户权限都极其敏感。真装在物理机上一旦环境变量和系统自带的 Java 冲突或者某个端口被已有服务占用排查起来非常痛苦。而且安装过程免不了反复修改配置配置错了想回到之前的状态物理机上只能手动卸载重来卸不干净还会留下权限残留。虚拟机让我可以随时打快照。比如装完系统之后先打一个“干净系统”快照装完 JDK 之后再打一个“JDK 就绪”快照后面配 Hadoop 时不管怎么折腾都能一键回到之前的可用状态。之后做多节点集群实验也可以直接克隆快照改一下主机名和 IP 就能模拟出好几台机器这是物理机给不了的便利。1.2 给虚拟机分配多少配置才不卡分配原则是“够用就好”不要贪心。我的建议配置是CPU 给 2 核内存给 4GB磁盘给 50GB网络选 NAT 模式。这套配置跑 Hadoop 3.x 伪分布式没有任何压力而且占宿主机资源不多开个浏览器看 Web 管理界面也不会卡。为什么内存是 4GB 而不是更大Hadoop 里的每个守护进程默认堆内存经常是 1GB 左右NameNode 和 ResourceManager 分别独占 1GB 是很常见的事再加上 DataNode、NodeManager整个集群至少吃掉 2GB。给虚拟机 4GBJVM 进程有充足空间宿主机也不会因为内存被吃光而变卡。如果你只想做最基础的验证2GB 也能勉强启动但跑 WordCount 时容易把进程挤到交换分区去速度很难看。内存资源比较紧张的朋友至少保证 3GB再配合后面提到的 swap 配置。磁盘 50GB 足够因为 Hadoop 安装包、JDK、日志和数据目录加起来在 10GB 以内剩下的空间是给 HDFS 存测试数据用的。不需要一开始就给 200GB 这种大小快照和多份克隆加在一起会占掉宿主机大量存储。网络模式我优先选 NAT因为虚拟机需要访问外部网络下载安装包但不要直接暴露在局域网里。桥接模式会让虚拟机成为局域网里的一台独立设备安全性差一些学习场景没必要。1.3 安装 Linux 系统时的几个坑操作系统我用的是 RHEL 系发行版因为它稳定、社区文档多而且大数据生态对 RHEL 系的支持最成熟。Debian 系发行版也能装但下面的很多命令会略有差异。安装时有几个地方提前做好后面事半功倍。第一个是主机名。安装时就把主机名规划好比如 hadoop01不要用默认的 localhost.localdomain。Hadoop 内部会反复用主机名做节点身份标识后期分布式扩节点时也是靠主机名区分机器主机名乱写会导致服务启动失败。第二个是固定 IP。NAT 模式下虚拟机默认走 DHCP重启后 IP 可能会变导致你下一次登录连不上或者 Hadoop 配置里写死的地址失效。我在安装阶段就会改成静态 IP比如 192.168.x.x并且在 /etc/hosts 里加上一行“内网IP 主机名”的映射。这一步很多人忽略等到启动集群报错时才发现问题。第三个是分区规划。系统盘给 50GB 时我习惯把 /opt 单独分一个区预留 30GB 左右。JDK、Hadoop 这类软件按惯例都装在 /opt 下单独分区可以防止日志和数据写满根分区后无法登录系统。第四个是用户规划。从开始就创建一个普通用户用于安装和运行 Hadoop不要用 root。Hadoop 的启动脚本检测到 root 用户时会提示安全警告有些版本甚至直接拒绝执行后面我会专门讲为什么。安装完成进入系统后第一件事就是打个快照名称写成“初始系统-未安装软件”。这一步非常重要相当于给之后所有操作买了份保险。2. JDK 安装别让版本和路径坑到你2.1 JDK 版本怎么选Hadoop 3.x 官方要求 Java 8 或 Java 11社区最常见的选择还是 Java 8。不要追新不要装 Java 21 或者更晚的版本因为 Hadoop 依赖的很多第三方库并没有在新版本 JDK 上做过完整验证编译和运行阶段很容易出现莫名奇妙的错误。JDK 版本选 8能少踩一大半的坑。另外要分清 JRE 和 JDK。Hadoop 运行只需要 JRE但编译、调试、jps 查看进程这些操作都需要 JDK 里的工具所以直接安装完整的 JDK不要为了省空间只装 JRE。JDK 提供方选用 OpenJDK 或者各大发行版自带的二进制包都可以关键不在厂商在于版本和安装路径要稳定。2.2 检查、卸载系统自带的 Java很多 Linux 发行版会预装一个 Java 运行时版本通常很老路径也乱七八糟。安装自己的 JDK 之前先检查一下现有环境java -version如果提示命令不存在说明系统没有预装 Java可以直接跳过卸载步骤。如果看到输出的是老版本最好把自带的包清理掉。RHEL 系发行版检查 Java 相关安装包用rpm -qa | grep java把列出来的相关包用 yum remove 卸载。这一步不是强迫症而是为了避免两个 Java 同时存在于 PATH 里导致java -version显示和你预期不一致。我见过一个案例用户在 /etc/profile 里配置了 JAVA_HOME 指向新装的 JDK但 PATH 里老 Java 排在前面Hadoop 启动脚本找到的却是一直以来的旧版本排查了半天才发现是卸载不干净引起的。2.3 上传和解压 JDKJDK 的安装包如果直接在虚拟机里下载速度可能不尽如人意。我的习惯是在宿主机下载好 tar.gz 包再用 scp 上传到虚拟机里。假设你的虚拟机用户是部署用户目标机器 IP 是 192.168.x.x命令大概是scp jdk8uXXX-linux-x64.tar.gz 用户名192.168.x.x:/tmp/上传成功后把安装包移动到 /opt/java 目录并解压sudo mkdir -p /opt/java sudo mv /tmp/jdk8uXXX-linux-x64.tar.gz /opt/java/ cd /opt/java sudo tar -zxvf jdk8uXXX-linux-x64.tar.gz解压后目录名一般是 jdk1.8.0_XXX 这种形式建议给解压出来的目录做一个软链接减少后面配置环境变量时路径写错的概率sudo ln -s /opt/java/jdk1.8.0_XXX /opt/java/current这样 JAVA_HOME 可以直接指向/opt/java/current以后升级 JDK 只需要调整软链接不用改一大堆配置文件。2.4 配置 JAVA_HOME 并验证环境变量我写在 /etc/profile 里这样对所有用户都生效。用 root 权限编辑这个文件在末尾追加export JAVA_HOME/opt/java/current export PATH$PATH:$JAVA_HOME/bin export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarCLASSPATH 在新版 JDK 里已经不是必须项但对 Hadoop 老版本和后续编译类操作没有任何坏处加上更稳妥。保存退出后执行source /etc/profile用三条命令验证java -version javac -version echo $JAVA_HOME只要输出对应版本路径并且 echo 出来的路径和配置一致JDK 段就完成了。这里要注意一个非常细节的经验source 只对当前终端生效如果你用其他终端工具新开窗口环境变量依然会丢失。正规做法是退出当前登录再重新登录让 /etc/profile 被重新读取。我测试时经常碰到“刚才配置了为什么另一个窗口里 java -version 还是报错”的问题基本都是这个原因。3. SSH 免密登录不配完这步集群启动多半会卡壳3.1 Hadoop 为什么需要免密登录Hadoop 的启动脚本 start-dfs.sh 在启动时会遍历整个集群的主机列表通过 SSH 登录到每一台节点上执行远程命令。如果是单节点伪分布式它也会往 localhost 发 SSH 连接。如果每次连接都需要输入密码脚本会卡在交互输入上即使你手速再快Hadoop 的分布式服务也没法靠手动输密码完成多节点部署。配置好免密登录之后SSH 连接不再询问密码Hadoop 脚本就能一条龙把所有节点全部拉起来。所以即使你只搭一台虚拟机也必须把 SSH 免密配好否则后面的 start-dfs.sh 一定会在某个瞬间报错。3.2 检查和启动 SSH 服务安装 Linux 系统时如果勾选了开发工具组件系统通常已经装了 OpenSSH。检查一下服务状态systemctl status sshd如果提示没有安装在 RHEL 系发行版上用 yum 安装sudo yum install -y openssh-server openssh-clients sudo systemctl start sshd sudo systemctl enable sshd注意把端口默认的 22 端口记住后面测试免密登录时会用到。如果学习环境允许我一般也会顺手把防火墙关掉或放行 22 端口不然 SSH 连接会被拦截白白增加排查难度。3.3 生成密钥并分发到目标主机切换到之后要运行 Hadoop 的用户比如 hadoop 用户在这个用户下生成密钥对。这个细节必须强调密钥要生成在运行 Hadoop 的那个用户的家目录里不是 root 的家目录。很多人先在 root 下生成了一对密钥切到 hadoop 用户后还是提示要密码就是因为密钥放错位置Hadoop 启动脚本用的是运行用户自己的 ~/.ssh。生成密钥的命令su - hadoop ssh-keygen -t rsa -P -f ~/.ssh/id_rsa-P 表示私钥不设置口令免密登录本来就是不想再输密码给私钥加口令反而破坏初衷。生成后把公钥分发给目标主机。单节点场景需要分发给自己的主机名和 localhostssh-copy-id hadoophadoop01 ssh-copy-id hadooplocalhost如果是多节点就把每台机器的主机名都执行一遍ssh-copy-id hadoophadoop02 ssh-copy-id hadoophadoop03第一次分发时 SSH 会询问 host key 是否确认输入 yes然后输入目标机器的登录密码。执行完后验证一下ssh hadoop01 date ssh localhost date如果不用输密码直接返回当前时间免密登录就成功了。这一步一定不要跳过测试很多人在 ssh-copy-id 之后直接去启动 Hadoop结果因为 known_hosts 还没写对白白浪费一晚上。3.4 常见 SSH 权限问题SSH 对密钥文件的权限要求非常严格。~/.ssh 目录权限不能是 777否则 sshd 会因为“权限过于开放”而拒绝使用你的密钥。正确权限是chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys如果公钥已经分发但登录还是要求密码优先检查这两项权限绝大多数免密问题都是出在这。其次是确认跑启动脚本的用户是否真的拥有密钥也就是前面说的别在 root 下生成密钥却用 hadoop 用户启动。4. Hadoop 安装与核心配置4.1 下载 Hadoop 安装包Hadoop 官网提供源码和二进制包两种下载方式我们直接下二进制 tar.gz 包不需要自己编译。版本选择 3.3.x 系列的最新稳定版不要选 alpha、beta 或者 RC 版本。3.x 系列是当前主流生态兼容性最好很多常用组件也都在朝着 3.x 适配。下载完成后照例通过 scp 上传到虚拟机。安装目录放在 /opt/hadoop 下sudo mkdir -p /opt/hadoop cd /opt/hadoop sudo tar -zxvf /tmp/hadoop-3.3.x.tar.gz解压完成后给版本目录做一个软链接sudo ln -sf /opt/hadoop/hadoop-3.3.x /opt/hadoop/current后续 HADOOP_HOME 统一指向/opt/hadoop/current。这样不管是升级版本还是维护多套 Hadoop都只需调整软链接配置文件不用动。4.2 解压、做软链接、释放目录权限这里有个关键操作把 /opt/hadoop 整个目录的所有者改为运行用户。Hadoop 运行时要写入数据目录、日志目录以及 HDFS 的临时文件这些路径都在 /opt/hadoop 下。如果目录所有者是 root普通用户启动时就会碰到 Permission denied。sudo chown -R 用户名:用户名 /opt/hadoop等价于sudo chown -R hadoop:hadoop /opt/hadoop这个权限问题高发在“可以在 /opt 下创建目录但无法启动服务”的场景。很多人在 root 下解压然后切到普通用户执行 start-dfs.sh结果服务反复退掉日志里都是 java.io.IOException: Permission denied。原因就是 /opt/hadoop 里生成的日志和数据目录归属于 root普通用户根本没有写权限。4.3 配置 HADOOP_HOME 环境变量和 JDK 一样在 /etc/profile 末尾追加 Hadoop 相关环境变量export HADOOP_HOME/opt/hadoop/current export PATH$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export HADOOP_CONF_DIR$HADOOP_HOME/etc/hadoop保存后重新登录执行hadoop version能输出版本信息说明 Hadoop 的环境变量正常。PATH 里包含 sbin 目录是因为 start-dfs.sh、start-yarn.sh 这些启动脚本都在 sbin 下不加上这个目录就得每次写全路径。4.4 修改 hadoop-env.shHadoop 的配置文件都在$HADOOP_HOME/etc/hadoop目录下其中 hadoop-env.sh 是环境总入口。这个文件里最重要的一个设置就是 JAVA_HOME。编辑 hadoop-env.sh找到 JAVA_HOME 相关行把注释去掉并改成自己的路径export JAVA_HOME/opt/java/current最典型的错误就是只配置了全局环境变量却没有让 Hadoop 脚本感知到这个变量。Hadoop 启动时由自己的 shell 脚本拉起 JVM 进程它未必会继承你 /etc/profile 里的 JAVA_HOME。hadoop-env.sh 中显式指定是官方推荐的可靠做法也是排查“JAVA_HOME is not set”类报错的第一手药方。4.5 修改四个核心 XML 配置文件Hadoop 的配置主要在四个 XML 文件里。按配置文件的功能拆开来看core-site.xml 是全局核心配置决定默认文件系统地址和相关临时目录。配置示例configuration property namefs.defaultFS/name valuehdfs://hadoop01:9000/value /property property namehadoop.tmp.dir/name value/opt/hadoop/tmp/value /property /configurationfs.defaultFS 的值是 hdfs://主机名:9000主机名要和你机器的主机名完全一致。9000 是 NameNode 的 RPC 通信端口Hadoop 内部各个组件通过它交换元数据。hadoop.tmp.dir 指定元数据临时目录建议放到 /opt/hadoop/tmp 而不要用默认的 /tmp。原因很简单/tmp 在系统重启时可能被清空一旦清空你的 HDFS 元数据就没了格式化建好的分布式存储也跟着“失忆”。hdfs-site.xml 负责 HDFS 的存储参数。伪分布式环境下把副本数设成 1configuration property namedfs.replication/name value1/value /property /configuration如果你之后克隆成三节点集群需要把这个值改成 3。副本数是 HDFS 把数据块复制到几个 DataNode 上的策略一个节点上存三份副本没有意义既浪费空间又可能因为数据分散在自己身上导致启动异常。mapred-site.xml 在 Hadoop 3.x 里默认不存在需要手工创建内容是把计算框架指定为 YARNconfiguration property namemapreduce.framework.name/name valueyarn/value /property /configurationyarn-site.xml 配置资源调度相关的核心参数至少需要一个辅助服务定义configuration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property /configuration最后检查 workers 文件。在 Hadoop 3.x 中这个文件叫 workers老版本叫 slaves作用是列出所有作为 DataNode 和 NodeManager 的机器主机名。单机伪分布式就在文件里写自己的主机名hadoop01多节点时每行写一个节点主机名。启动脚本通过这个文件决定 SSH 到哪些机器上去启动 DataNode。4.6 格式化 NameNode 并启动集群首装 Hadoop 时必须初始化 HDFS 的元数据这一步叫格式化 NameNode。执行前确认数据目录没有重要数据然后运行hdfs namenode -format看到 “successfully formatted” 类似输出即说明成功。注意格式化只有首次安装时必须做你以后的任何重启都不要再执行格式化。数据节点如果格式化和元数据版本不一致启动会报 Incompatible clusterIDs 之类错误。启动 HDFS 和 YARNstart-dfs.sh start-yarn.sh我有意分开启动而不是用 start-all.sh因为分开执行时如果某个服务失败启动日志会被打印在当前终端定位更快。启动过程中如果 SSH 免密没配好脚本会卡在某台机器上要密码。如果一切正常几秒钟后所有进程就会起来。验证进程用 jps 命令jps伪分布式正常情况应该看到 5 个进程NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager。少于这 5 个说明对应的服务启动失败去日志里找原因。5. 验证安装与故障排查5.1 用 WordCount 做一次端到端验证进程全起来了不代表 Hadoop 真的可用。我每次部署完都要跑一个最简单的 MapReduce 作业做端到端验证。Hadoop 自带示例 jar在$HADOOP_HOME/share/hadoop/mapreduce/目录下文件名包含 hadoop-mapreduce-examples 和版本号。先在本地准备一个测试文件写入几行单词echo hello hadoop world hello hadoop /tmp/word.txt然后创建 HDFS 上的输入目录并上传hdfs dfs -mkdir -p /input hdfs dfs -put /tmp/word.txt /input/执行 WordCounthadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.x.jar wordcount /input /output如果 /output 目录已经存在任务会报错这时换个输出目录名或者先删掉旧目录hdfs dfs -rm -r /output任务跑完后查看结果hdfs dfs -cat /output/part-r-00000看到正确的单词计数说明从 HDFS 写到 MapReduce 再到 YARN 调度整条链路都是通的。5.2 看 Web 管理界面确认服务状态Hadoop 自带 Web UI比命令行直观得多。启用后不需要做额外配置直接在宿主机浏览器访问虚拟机的 IP 和端口。HDFS NameNode 的 Web 界面默认端口是 9870访问格式是http://虚拟机IP:9870。页面上能看到 HDFS 容量、存活节点数、每个 DataNode 的状态这是一个很好的健康检查入口。YARN ResourceManager 的界面在http://虚拟机IP:8088可以查看已经提交的任务、队列资源和节点状态。第一次访问时如果页面打不开先检查虚拟机和宿主机网络是否互通再检查防火墙是否放行端口。学习环境下我一般直接关闭防火墙省心sudo systemctl stop firewalld sudo systemctl disable firewalld5.3 常见问题排查速查表把我在部署中遇到的高频问题整理成一张表启动报错时可以直接对照症状可能原因解决建议启动脚本提示 JAVA_HOME is not sethadoop-env.sh 里的 JAVA_HOME 没配置或路径不对在 hadoop-env.sh 里显式 export JAVA_HOME指向 JDK 实际解压路径SSH 连接卡住要求输入密码免密未配置成功或密钥放错用户检查 ~/.ssh 权限、known_hosts用 ssh-copy-id 重新分发公钥Permission denied 错误/opt/hadoop 目录所有者不是当前运行用户chown -R 运行用户:运行用户 /opt/hadoopNameNode 启动失败NameNode 未格式化或临时目录意外丢失确认无重要数据后重新 hdfs namenode -formatIncompatible clusterIDs重复格式化导致元数据不一致删除 hadoop.tmp.dir 目录后重新格式化访问 9870/8088 端口打不开防火墙拦截或虚拟机重启后 IP 变化关闭防火墙或放行端口固定虚拟机 IPhdfs dfs 命令连不上集群fs.defaultFS 主机名解析异常检查 /etc/hosts 映射确保主机名指向内网 IP最后一条特别值得展开讲。Linux 很多发行版默认在 /etc/hosts 里把主机名映射到 127.0.0.1 或 127.0.1.1导致 Hadoop 组件用主机名回连本机时走的还是 loopback。在单机伪分布式时这往往不影响但后续扩展多节点时问题会集中爆发。你在排错时应该打开 /etc/hosts确保有一条类似192.168.x.x hadoop01的显式映射而不是只靠系统默认的 localhost 解析。5.4 看日志的思路排错时最有用的工具是日志目录$HADOOP_HOME/logs/。Hadoop 的每个服务都有一份独立日志命名大致是 hadoop-用户-服务名-主机名.log 和 .out。启动失败时优先看同名的 .log 文件用 tail 命令看末尾的堆栈信息tail -n 200 $HADOOP_HOME/logs/hadoop-hadoop-namenode-hadoop01.log堆栈里的异常类型远比报错提示更准确比如看到 java.io.IOException: Permission denied 就是权限问题看到 java.net.ConnectException 就是网络或节点连接问题。养成一启动失败就看日志的习惯很多所谓“玄学”问题其实五分钟内就能找到根因。6. 安装完成之后的几个建议6.1 保存快照留一条退路整个安装流程跑通后第一件事就是关机打快照。我给这个快照起名“Hadoop 单节点基础版”以后所有实验都从这个状态开始。为什么强调关机再打快照因为虚拟机的内存状态和磁盘状态如果不一致恢复后可能出现服务状态不稳。而完全关机后的快照相当于一个干净、可反复使用的“出厂镜像”。这个快照的用途不只是防手误。后续想练习多节点集群直接克隆这份快照就能得到一台已经装好 JDK 和 Hadoop 的机器只需要改主机名、IP、workers 配置一分钟就“多出”一个节点比自己从头装快太多。6.2 伪分布式扩展到真实多节点单节点伪分布式验证通过后扩展多节点是顺理成章的事。大致思路是克隆出 hadoop02、hadoop03 的两台虚拟机分别修改主机名、静态 IP确保 /etc/hosts 里三台机器的映射都存在。然后在主节点修改 workers 文件把 hadoop02 和 hadoop03 加进去。确保三台机器上的 hadoop 用户和目录权限一致重新配置 SSH 免密让主节点能免密登录到另外两台最后启动集群。你可以在 ResourceManager 的 Web 界面看到存活节点数变成 3在 NameNode 界面看到 DataNode 列表多了两台机器。扩展过程中最容易出错的是三台机器上的配置文件不一致。我的经验是以主节点为模板统一用一份配置分发过去不要各自手工改手工改容易漏。官方和很多发行版也支持配置分发功能但学习阶段用 scp 手动拷贝完全够用。6.3 内存、临时目录和 swap 的调优把 Hadoop 装起来只是第一步跑作业时资源依然可能不够。物理机内存只有 8GB 的用户虚拟机拿了 4GB再加上宿主机系统本身占用很容易在跑大任务时卡死。我的做法是给虚拟机加一个 2GB 的 swap 文件让 JVM 在物理内存紧张时可以缓冲。Swap 文件创建命令不复杂但注意不要分配在系统盘根目录避免挤占磁盘空间。另外如果跑 WordCount 遇到 OOM可以调整 YARN 里的两个参数yarn.nodemanager.resource.memory-mb 和 yarn.scheduler.maximum-allocation-mb。默认值对 4GB 虚拟机来说往往偏大我会手动调低到 2048 或者 1024。这个参数改完需要重启 YARN 才能生效。临时目录我用的是 /opt/hadoop/tmp再强调一次不要放在 /tmp。我踩过这个坑某次实验完成、虚拟机重启后NameNode 起不来了原因就是 /tmp 下的元数据在重启时被系统清理。把 hadoop.tmp.dir 固定在 /opt/hadoop/tmp 后即使虚拟机重启多次数据依然完好。最后我再分享一个小技巧。整个环境搭建完之后别忘了把 hosts 文件和核心配置文件再备份一份到宿主机。虚拟机快照虽然能恢复但你总会有“只想看某个配置之前写过什么”的时候本地留一份配置文件副本查问题能省很多力气。虚拟机里折腾 Hadoop本身就是一套“试错成本最低”的学习路径快照打好、配置清楚、日志看明白剩下的就是大胆跑任务、放心调优出了问题随时回滚重来。