1. 为什么JDK和Hadoop的安装顺序不能颠倒很多人拿到一台干净的Linux机器第一反应是先把Hadoop压缩包传上去解压结果跑到hadoop version的时候直接报JAVA_HOME is not set然后又回头装JDK装完发现Hadoop的配置文件里写的路径和实际JDK路径对不上来回折腾两三遍。这个顺序问题看起来是小事但它背后反映的是一个依赖链的逻辑Hadoop的所有守护进程NameNode、DataNode、ResourceManager、NodeManager本质上都是Java进程启动脚本第一件事就是去读JAVA_HOME环境变量找不到就直接退出。所以JDK必须先于Hadoop安装并配置好环境变量这不是建议是硬性前提。我这次搭建的是一个三节点的小集群一台做Master同时跑NameNode和ResourceManager两台做Slave跑DataNode和NodeManager。操作系统用的是CentOS 7系列Hadoop版本选的是3.x稳定版JDK用的是OpenJDK 8。为什么不用JDK 11或17因为Hadoop 3.x虽然官方声明支持JDK 11但在实际跑MapReduce任务和YARN调度的时候JDK 11下某些反射相关的API会有警告甚至异常尤其是涉及到Kerberos认证和某些序列化框架的时候。JDK 8是目前生产环境验证最充分的版本没有特殊需求不要给自己找麻烦。关于上传方式这次用的是rz命令也就是lrzsz工具包提供的ZModem协议上传。为什么不用scp或者wget因为在很多内网环境里机器不通外网scp需要你知道目标机器的密码或者配置免密而rz直接在终端里弹出文件选择框拖进去就传完了对于传几个安装包这种场景来说是最省事的。当然rz有个坑它默认走的是终端会话传大文件比如超过4GB的时候可能会超时或者中断但JDK和Hadoop的压缩包一般也就几百MB完全够用。下面我会把整个流程拆成几个部分先讲清楚安装前的环境准备和检查项然后详细走一遍JDK的安装和环境变量配置接着是Hadoop的rz上传、解压、目录规划和核心配置文件修改最后讲首次格式化和启动集群时最容易踩的几个坑。每一步我都会解释为什么这么做以及我实际踩过哪些坑。2. 安装前的环境检查与rz工具准备2.1 确认系统版本和已有Java环境在动手之前先确认几件事。第一看系统版本cat /etc/redhat-release确认是CentOS 7还是8这影响后面yum源的选择。第二检查是否已经装了Javajava -version和which java有些系统镜像自带OpenJDK但版本可能不对比如自带的可能是JDK 11你需要决定是卸载还是保留。我的习惯是如果自带版本不是8就直接卸载干净再装避免JAVA_HOME指向混乱。# 查看系统版本 cat /etc/redhat-release # 查看是否已有Java java -version which java rpm -qa | grep -i jdk如果发现有自带的OpenJDK用rpm -e --nodeps卸载掉。注意--nodeps是强制卸载不检查依赖因为有些系统组件会依赖默认JDK但你自己装了JDK 8之后这些依赖自然满足所以强制卸载是安全的。2.2 安装rz/sz工具并验证rz命令不是Linux自带的需要安装lrzsz包。在CentOS上直接yum install -y lrzsz就行。如果你的机器不通外网那就需要先配置本地yum源或者用离线rpm包安装。装完之后在终端输入rz如果弹出一个文件选择窗口说明工具正常。# 安装lrzsz yum install -y lrzsz # 验证rz是否可用 rz --version这里有个细节rz上传的文件默认会放在你当前所在的工作目录下。所以上传之前先cd到你规划好的目录比如/opt/software这样文件直接落到目标位置省得后面再mv。另外如果你用的是某些终端工具比如Xshell、SecureCRTrz的弹窗行为可能不一样有的会在终端下方弹出文件选择栏有的会弹独立窗口但操作逻辑是一样的。注意rz上传过程中不要关闭终端窗口也不要在传输过程中按CtrlC否则文件会传一半断掉解压的时候会报gzip: stdin: unexpected end of file。如果真断了重新传一遍就行覆盖原文件。2.3 规划目录结构在传文件之前先把目录规划好。我的习惯是/opt/software放所有安装包JDK压缩包、Hadoop压缩包/opt/module放解压后的软件目录/opt/module/jdk1.8.0_xxxJDK安装目录/opt/module/hadoop-3.x.xHadoop安装目录为什么用/opt而不是/usr/local其实两者都行/opt在FHS标准里就是给第三方软件用的/usr/local更多是给手动编译的软件。我选/opt纯粹是个人习惯而且路径短后面写配置文件的时候少打几个字。# 创建目录 mkdir -p /opt/software mkdir -p /opt/module目录建好之后cd /opt/software然后执行rz把JDK和Hadoop的压缩包都传上去。传完之后用ls -lh看一下文件大小确认传输完整。JDK 8的压缩包大概180MB左右Hadoop 3.x的压缩包大概300-400MB如果大小明显不对说明传断了重新传。3. JDK安装与环境变量配置的完整链路3.1 解压JDK并确认目录名传完JDK压缩包之后解压到/opt/module目录下tar -zxvf /opt/software/jdk-8uXXX-linux-x64.tar.gz -C /opt/module/解压完ls /opt/module看一下目录名通常是jdk1.8.0_XXX这种格式。记住这个目录名后面配JAVA_HOME要用。如果你觉得名字太长可以改个短名比如mv jdk1.8.0_XXX jdk8但我不建议改因为后面如果装多个版本容易搞混保持原始目录名最稳妥。3.2 配置JAVA_HOME环境变量环境变量的配置位置有几种选择/etc/profile全局所有用户生效、~/.bashrc当前用户、/etc/profile.d/下新建一个sh文件全局但更规范。我选的是在/etc/profile.d/下新建my_env.sh这样做的好处是第一不会污染/etc/profile这个系统文件第二以后要加其他环境变量比如Hadoop的直接往这个文件里追加就行第三系统升级或者别人接手的时候一看就知道这是自定义的环境变量。# 创建环境变量文件 vim /etc/profile.d/my_env.sh写入以下内容# JAVA_HOME export JAVA_HOME/opt/module/jdk1.8.0_XXX export PATH$PATH:$JAVA_HOME/bin保存退出后source /etc/profile.d/my_env.sh让配置生效。然后验证echo $JAVA_HOME java -version javac -version如果java -version能正常输出版本号说明JDK安装成功。如果报command not found检查两件事一是JAVA_HOME路径是否写对二是PATH里是否包含了$JAVA_HOME/bin。还有一个容易忽略的点如果你是用ssh登录的/etc/profile.d/下的脚本在非交互式shell里可能不会自动加载但java -version这种交互式命令是没问题的。提示JAVA_HOME的路径一定不要写到bin目录比如写成/opt/module/jdk1.8.0_XXX/bin这是新手最常犯的错误。JAVA_HOME指向的是JDK的根目录PATH里才需要加bin。3.3 验证JDK安装是否真正可用光看java -version还不够因为有些系统自带的Java可能还在PATH里。用which java确认一下当前用的是不是你刚装的那个which java # 应该输出 /opt/module/jdk1.8.0_XXX/bin/java如果输出的是/usr/bin/java说明系统自带的Java优先级更高需要检查/etc/profile.d/my_env.sh里的PATH是否放在了$PATH前面或者直接用alternatives命令切换。不过更彻底的办法是卸载系统自带的Java避免混淆。另外写一个简单的Java测试程序验证编译和运行都正常# 创建测试文件 cat /tmp/Test.java EOF public class Test { public static void main(String[] args) { System.out.println(JDK install OK: System.getProperty(java.version)); } } EOF # 编译运行 cd /tmp javac Test.java java Test如果输出JDK install OK: 1.8.0_XXX说明JDK的编译和运行环境都没问题。这一步很多人跳过但实际上一旦后面Hadoop跑任务报Java相关的错你至少能确定JDK本身是好的排查范围就缩小了。4. Hadoop的rz上传、解压与目录规划4.1 用rz上传Hadoop压缩包并校验完整性JDK搞定之后cd /opt/software执行rz上传Hadoop压缩包。传完之后用ls -lh看文件大小再用md5sum或者sha256sum校验一下如果你有官方提供的校验值的话。没有校验值也没关系至少确认文件大小和官方下载页面标注的一致。cd /opt/software rz # 选择Hadoop压缩包上传 # 查看文件 ls -lh hadoop-3.x.x.tar.gz # 校验可选 md5sum hadoop-3.x.x.tar.gz4.2 解压Hadoop到指定目录tar -zxvf /opt/software/hadoop-3.x.x.tar.gz -C /opt/module/解压完之后ls /opt/module确认目录名通常是hadoop-3.x.x。同样不建议改目录名保持原始名称。4.3 配置HADOOP_HOME和PATH继续编辑/etc/profile.d/my_env.sh追加Hadoop的环境变量# HADOOP_HOME export HADOOP_HOME/opt/module/hadoop-3.x.x export PATH$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin注意这里PATH里加了两个目录bin和sbin。bin目录下是hadoop、hdfs、yarn这些命令sbin目录下是start-dfs.sh、stop-dfs.sh、start-yarn.sh这些集群管理脚本。两个都要加否则你启动集群的时候还得敲全路径。source /etc/profile.d/my_env.sh之后验证hadoop version如果输出Hadoop版本信息说明环境变量配置成功。如果报JAVA_HOME is not set回去检查JDK的环境变量是否生效或者hadoop-env.sh里是否需要显式指定JAVA_HOME后面会讲。4.4 目录结构规划与数据存储路径Hadoop默认会把数据存到/tmp目录下但/tmp在很多系统上重启会被清空而且空间可能不够。所以需要在Hadoop的配置文件里显式指定数据目录。我的规划是/opt/module/hadoop-3.x.x/data/namenodeNameNode元数据/opt/module/hadoop-3.x.x/data/datanodeDataNode数据块/opt/module/hadoop-3.x.x/logs日志目录默认就有# 创建数据目录 mkdir -p /opt/module/hadoop-3.x.x/data/namenode mkdir -p /opt/module/hadoop-3.x.x/data/datanode为什么把数据目录放在Hadoop安装目录下因为这样所有东西都在一个地方备份和迁移的时候直接打包整个Hadoop目录就行。当然生产环境一般会把数据目录挂载到独立的磁盘上但测试环境这样搞最省事。5. 核心配置文件修改从hadoop-env.sh到workers5.1 hadoop-env.sh中JAVA_HOME的显式指定虽然你在/etc/profile.d/my_env.sh里配了JAVA_HOME但Hadoop的启动脚本在某些情况下比如通过ssh远程启动守护进程时可能读不到这个环境变量。所以最保险的做法是在hadoop-env.sh里再显式指定一次vim $HADOOP_HOME/etc/hadoop/hadoop-env.sh找到export JAVA_HOME这一行改成export JAVA_HOME/opt/module/jdk1.8.0_XXX这一行默认是注释掉的取消注释并填上你的JDK路径。这一步非常关键很多JAVA_HOME is not set的报错都是因为这里没配。5.2 core-site.xmlNameNode地址和临时目录core-site.xml是Hadoop的核心配置主要配两个东西NameNode的RPC地址和Hadoop的临时目录。vim $HADOOP_HOME/etc/hadoop/core-site.xml在configuration标签内加入property namefs.defaultFS/name valuehdfs://master:9820/value /property property namehadoop.tmp.dir/name value/opt/module/hadoop-3.x.x/data/tmp/value /propertyfs.defaultFS指定了HDFS的默认文件系统地址master是你Master节点的主机名需要在/etc/hosts里配好映射。端口9820是Hadoop 3.x的默认NameNode RPC端口Hadoop 2.x是8020别搞混了。hadoop.tmp.dir是Hadoop的临时目录很多其他目录比如NameNode的edits日志默认都基于这个目录所以一定要配到一个空间足够且不会自动清理的位置。5.3 hdfs-site.xml副本数和数据目录vim $HADOOP_HOME/etc/hadoop/hdfs-site.xml加入property namedfs.replication/name value2/value /property property namedfs.namenode.name.dir/name value/opt/module/hadoop-3.x.x/data/namenode/value /property property namedfs.datanode.data.dir/name value/opt/module/hadoop-3.x.x/data/datanode/value /propertydfs.replication是副本数我设的是2因为我有2个Slave节点。如果你只有1个Slave设成1否则会一直报副本不足的警告。dfs.namenode.name.dir和dfs.datanode.data.dir分别指定NameNode和DataNode的数据存储路径就是前面创建的目录。5.4 yarn-site.xmlResourceManager和NodeManager配置vim $HADOOP_HOME/etc/hadoop/yarn-site.xml加入property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.resourcemanager.hostname/name valuemaster/value /property property nameyarn.nodemanager.env-whitelist/name valueJAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME/value /propertyyarn.nodemanager.aux-services必须配成mapreduce_shuffle否则MapReduce任务跑不起来。yarn.resourcemanager.hostname指定ResourceManager所在的主机名。yarn.nodemanager.env-whitelist是Hadoop 3.x新增的配置用来把JAVA_HOME等环境变量传递给容器不配的话NodeManager启动容器时可能找不到Java。5.5 mapred-site.xml指定MapReduce运行框架vim $HADOOP_HOME/etc/hadoop/mapred-site.xml加入property namemapreduce.framework.name/name valueyarn/value /property property nameyarn.app.mapreduce.am.env/name valueHADOOP_MAPRED_HOME/opt/module/hadoop-3.x.x/value /property property namemapreduce.map.env/name valueHADOOP_MAPRED_HOME/opt/module/hadoop-3.x.x/value /property property namemapreduce.reduce.env/name valueHADOOP_MAPRED_HOME/opt/module/hadoop-3.x.x/value /propertymapreduce.framework.name设成yarn表示MapReduce任务跑在YARN上。后面三个环境变量配置是Hadoop 3.x里容易漏掉的不配的话任务提交到YARN后会报找不到HADOOP_MAPRED_HOME。5.6 workers文件指定Slave节点vim $HADOOP_HOME/etc/hadoop/workers写入你的Slave节点主机名每行一个slave1 slave2这个文件在Hadoop 2.x里叫slaves3.x改成了workers。启动start-dfs.sh和start-yarn.sh的时候脚本会读这个文件然后通过ssh到各个节点启动对应的守护进程。所以前提是你已经配好了Master到Slave的ssh免密登录。6. 首次格式化与集群启动的踩坑记录6.1 NameNode格式化的正确姿势配置改完之后第一次启动前需要格式化NameNodehdfs namenode -format格式化的时候注意看输出如果看到Storage directory /opt/module/hadoop-3.x.x/data/namenode has been successfully formatted说明格式化成功。如果报错常见原因有两个一是目录权限不对二是dfs.namenode.name.dir路径写错。注意格式化只能执行一次。如果你后面改了dfs.namenode.name.dir或者集群ID冲突需要重新格式化那必须先把所有节点的data目录清空否则DataNode的clusterID和NameNode对不上DataNode启动后会立刻退出。我踩过这个坑当时改了配置重新格式化但没清DataNode的数据目录结果DataNode一直报ClusterId mismatch排查了半天。6.2 启动HDFS和YARN的顺序格式化完成后启动HDFSstart-dfs.sh这个脚本会在Master上启动NameNode和SecondaryNameNode然后通过ssh到workers文件里列出的Slave节点启动DataNode。启动完成后用jps检查# Master上应该看到 # NameNode # SecondaryNameNode # ResourceManager如果也启动了YARN # Slave上应该看到 # DataNode # NodeManager如果也启动了YARN如果某个进程没起来去对应节点的logs目录下看日志通常日志里会写清楚原因。最常见的原因是JAVA_HOME没配好或者ssh免密没配通。然后启动YARNstart-yarn.shYARN的启动脚本会在Master上启动ResourceManager在Slave上启动NodeManager。启动完之后用jps确认。6.3 常见报错与排查思路报错一JAVA_HOME is not set这个前面说过检查hadoop-env.sh里的JAVA_HOME是否配了以及/etc/profile.d/my_env.sh是否source了。报错二ssh: connect to host slave1 port 22: No route to host说明Master到Slave的网络不通或者ssh服务没启动。先ping slave1看网络再ssh slave1看免密是否配好。报错三DataNode启动后立刻退出去DataNode的日志里看如果是ClusterId mismatch说明NameNode格式化后clusterID变了需要清空DataNode的数据目录重新启动。如果是Permission denied检查数据目录的权限。报错四hdfs dfs -ls /报Connection refused说明NameNode没起来或者RPC端口不对。用jps确认NameNode进程在不在用netstat -tlnp | grep 9820确认端口是否监听。6.4 验证集群是否真正可用进程都起来之后做几个验证# 查看HDFS根目录 hdfs dfs -ls / # 创建测试目录 hdfs dfs -mkdir -p /test # 上传一个本地文件 echo hello hadoop /tmp/test.txt hdfs dfs -put /tmp/test.txt /test/ # 查看文件内容 hdfs dfs -cat /test/test.txt # 跑一个MapReduce示例 hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.x.x.jar wordcount /test /output如果wordcount能跑完并输出结果说明整个集群的HDFS和YARN都正常工作了。这一步跑通之后后面再装Hive、HBase、Spark之类的组件就是水到渠成的事。7. 多节点同步与后续扩展的实操建议7.1 用scp或rsync同步配置到Slave节点如果你是在Master上改好了所有配置需要把Hadoop目录同步到Slave节点。最直接的方式是用scpscp -r /opt/module/hadoop-3.x.x slave1:/opt/module/ scp -r /opt/module/hadoop-3.x.x slave2:/opt/module/但这样每次改配置都要重新传一遍比较麻烦。更好的方式是用rsync做增量同步rsync -av /opt/module/hadoop-3.x.x/ slave1:/opt/module/hadoop-3.x.x/rsync只会传有变化的部分速度快很多。另外环境变量文件/etc/profile.d/my_env.sh也需要同步到各个Slave节点否则Slave上的hadoop命令用不了。7.2 时间同步和主机名映射集群环境里有两件事必须做第一所有节点的主机名和IP映射要配在/etc/hosts里否则hdfs://master:9820这种地址解析不了。第二所有节点的时间要同步否则YARN的任务调度和HDFS的日志时间戳会乱。时间同步可以用ntpdate或者chronyd内网环境找一台机器做时间服务器就行。# /etc/hosts 示例 192.168.1.100 master 192.168.1.101 slave1 192.168.1.102 slave27.3 关于rz上传的补充经验rz虽然方便但有几个场景不适合用第一传大文件超过2GB容易断建议用scp或者rsync。第二批量传多个文件的时候rz -y可以覆盖同名文件但不会提示确认用的时候注意。第三有些终端工具对rz的支持不好传完之后文件名可能变成乱码这时候用rz -bbinary模式试试。另外如果你经常需要传文件可以在~/.bashrc里加一个别名alias uploadcd /opt/software rz -y这样以后直接敲upload就能上传到指定目录并覆盖同名文件。7.4 集群启动脚本的封装每次启动集群都要敲start-dfs.sh和start-yarn.sh还要jps检查比较繁琐。可以写一个简单的脚本封装一下#!/bin/bash # cluster-start.sh echo Starting HDFS... start-dfs.sh echo Starting YARN... start-yarn.sh echo Checking processes on master... jps echo Checking processes on slave1... ssh slave1 jps echo Checking processes on slave2... ssh slave2 jps把这个脚本放到/usr/local/bin下并加执行权限以后直接敲cluster-start.sh就能一键启动并检查所有节点的进程状态。停集群的脚本同理把start换成stop就行。这套流程走下来从一台干净的CentOS机器到能跑MapReduce任务的Hadoop集群大概需要30-40分钟其中大部分时间花在传文件和改配置上。真正容易出问题的不是安装步骤本身而是环境变量、配置文件里的路径、以及节点之间的网络和免密登录。我个人的经验是每改完一个配置文件就source一下环境变量并验证不要等所有配置都改完再一起测否则出了问题你不知道是哪个文件改错了。另外格式化NameNode之前一定要确认所有配置都改好了因为格式化之后如果改dfs.namenode.name.dir就得清数据重新格式化之前跑的测试数据全丢。