ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Hadoop伪分布式环境搭建:核心配置与踩坑实践

Hadoop伪分布式环境搭建:核心配置与踩坑实践 最近接了个活需要在一台开发机上快速验证一套 MapReduce 业务逻辑生产集群动不了公司也没有现成的多机测试环境。我当时的决定很干脆搭一个 Hadoop 伪分布式测试环境。别小看这个“伪”字它意味着 NameNode、DataNode、ResourceManager、NodeManager 这些角色全部跑在同一台机器上官方叫 Pseudo-Distributed Mode介于本地模式和全分布式模式之间。说实话网上关于 Hadoop 伪分布式的教程已经多到溢出了但我翻了一遍发现几个通病要么版本太老端口还停留在 50070、9001 这种远古阶段要么只给命令不给原理照着抄一遍然后迷茫要么上来就让你用 root、全默认配置结果任务一跑就内存爆掉DataNode 起不来还找不到原因。这篇文章我不会只贴命令而是把每个关键配置、每个启动步骤背后的逻辑讲清楚顺便把我踩过的坑都标出来。核心内容以 Hadoop 3.3.6 为基准版本差异我会单独说明。适合谁看刚开始接触 Hadoop 的初学者、正在做课程设计需要在单机环境跑通完整流程的学生、以及临时需要一套可复现测试环境来验证业务代码的开发者。看完之后你应该能从空目录开始一步步搭出可用环境跑通 WordCount并且知道出了问题去哪看、怎么看。1. 伪分布式到底在“测”什么先搞清定位再动手1.1 三种运行模式的关系别把伪分布式当“简化版”Hadoop 官方定义了三种运行模式很多人把它们理解成“低级、中级、高级”的递进关系这个理解方向没问题但容易忽略本质区别。本地模式Local Mode是 Hadoop 安装好之后默认的模式它不启动任何守护进程不需要配置 HDFS 和 YARN。当你直接运行hadoop jar xxx.jar input output时MapReduce 会在本地 JVM 里以单进程方式执行输入输出走本地文件系统。这种模式的好处是零配置、跑得快适合验证算法逻辑本身但缺点很明显完全没有分布式概念HDFS 读写、YARN 调度、shuffle 机制这些都体会不到。伪分布式模式Pseudo-Distributed Mode则完全不同。它把每个守护进程作为独立 Java 进程启动进程之间通过端口通信HDFS 和 YARN 都是真实工作的。所谓“伪”只是物理上所有进程挤在同一台机器上。通信机制、配置项、协议栈、日志格式和全分布式完全一致。拿排戏来类比本地模式是在脑海里对台词伪分布式是全体演员挤在一个房间里排练走位全分布式才是正式剧场里各就各位。全分布式Fully Distributed Mode就是在多台机器上分别部署角色一般至少三台起步涉及机架感知、跨节点网络传输、多副本分布等更复杂的机制。伪分布式真正的价值在于它是从“单机跑个 jar”到“多机集群调度”之间那个必经的过渡层。你在这套环境里养成的配置习惯、排错思路迁移到真实集群时基本都能沿用。1.2 伪分布式能测什么测不了什么搞清楚边界的最大好处是避免你把测试结论误用。伪分布式能覆盖的范围包括HDFS 的完整读写链路客户端通过 fs.defaultFS 与 NameNode 交互写数据时流式写入 DataNode读数据时按块读取。MapReduce 任务的完整生命周期Job 提交、ApplicationMaster 启动、Map 阶段、Shuffle、Reduce 阶段、结果写回。YARN 的资源调度逻辑Container 的申请、分配和释放。配置文件和启动脚本的正确性你改的任何参数在这套环境里的生效方式和真集群一致。业务代码的粗粒度验证数据量不大时跑通 MapReduce 任务逻辑没有问题。测不了或者测不准的也很明确多节点数据分布和机架感知单机上没有网络拓扑可言。副本容错伪分布式里副本数可以设成 3但三份副本都在同一块磁盘上数据节点挂掉的场景无法真实复现。高并发与资源隔离压力NodeManager 在单机上的资源上限就在那儿压测结论没有参考性。HA 高可用那需要多台 NameNode 加 ZooKeeper 组成伪分布式根本不涉及。我的建议是功能验证、机制学习、课程设计、代码冒烟测试伪分布式完全够用。但如果任务是评估生产容量或验证 HA 切换策略那就老老实实搭真正的集群或者直接用云厂商的托管集群别在这个环境上浪费时间。1.3 版本选型的底线建议我见过不少教程还在用 Hadoop 2.7.x然后配套 JDK 7这种组合在今天不仅维护困难而且和主流生态脱节。在 3.x 时代推荐选 3.3.x 分支比如 3.3.6原因很直接3.x 默认端口变了NameNode Web UI 是 9870不再是 50070、支持了 EC 纠删码、YARN 的 Timeline Service 也更完善。跟着新版本学习至少不会在起步阶段就接触已经废弃的配置项。对应的 JDK 用 Oracle JDK 8 或 OpenJDK 8 是最稳的Hadoop 3.x 也支持 JDK 11。但我不建议为了追求“新”去用 JDK 17 以上版本Hadoop 部分模块在 Java 17 下会碰到模块化相关的权限问题属于给自己找不痛快。硬件底线方面内存是最关键的。伪分布式至少 4G 内存才算舒服2G 也不是不能跑但必须在 YARN 配置里把容器内存调得很小否则 WordCount 都会 OOM。磁盘预留 10 到 20G 足够毕竟测试数据量不会太大。操作系统建议直接用 CentOS 7、Rocky Linux、Ubuntu 20.04 这类 Linux 发行版Windows 原生跑伪分布式的坑属于“可以跑但没必要”后面我会单独讲 Windows 开发机怎么和伪分布式配合。2. 环境准备里的隐形坑JDK、SSH 与运行用户2.1 JDK 版本不是越高越好装了还要指向对Hadoop 本身是用 Java 写的所以 JDK 是它的底座。大多数教程会让你“安装 JDK、配置 JAVA_HOME”然后就没有然后了但实际执行时最容易栽在两个地方一是 JDK 装错了目录二是在 hadoop-env.sh 里没写清楚 JAVA_HOME导致脚本用系统默认 Java 时出现诡异错误。安装步骤不复杂CentOS 系可以直接yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel注意必须装-devel版本否则缺少javac等编译工具后续打包代码或跑部分示例会报错。装完后找到路径update-alternatives --config java把返回的路径记下来通常是/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64。然后编辑hadoop-env.sh显式设置export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64为什么非要显式写一次因为 Hadoop 的启动脚本在读取 JAVA_HOME 时有自己的解析逻辑它和系统 PATH 里的 java 不一定一致。如果系统默认的java指向的是 JRE而不是 JDK 根目录脚本在一些需要搜 rt.jar、tools.jar 的场景下会直接失败。与其排查这种低级问题不如一开始就把环境变量写死。最后执行hadoop version验证。如果能看到 Hadoop 3.3.6 以及 Java 版本信息说明环境变量 OK。2.2 SSH 免密登录只有一台机器也要配这是伪分布式里最高频的初学踩坑点。很多人在配置 HDFS 时一步到位的命令是start-dfs.sh然后脚本就卡住或者报 SSH 连接错误。原因在于start-dfs.sh 脚本会通过 SSH 协议“登录”到core-site.xml里配置的节点然后把守护进程远程启动起来。即使节点就是 localhost脚本也照样走一遍 SSH 流程。这就好比你发快递给自己快递员也要先接单、取件、再配送流程不会因为你收件人和寄件人相同就省略。配置免密的完整流程# 切换到要运行 Hadoop 的用户比如 hadoop su - hadoop ssh-keygen -t rsa -P -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys # 验证 ssh localhost-P 表示密钥不带密码短语这是脚本自动化登录的前提。如果生成密钥时手贱输入了密码短语后面 ssh 登录依然要交互输入一样会卡住。如果ssh localhost提示 Connection refused先启动 sshd 服务如果提示 Host key verification failed删掉~/.ssh/known_hosts里对应的旧记录或者在生成密钥前先执行一下ssh-keyscan -H localhost ~/.ssh/known_hosts。这些细节不解决后面的所有启动都会连环失败。2.3 独立用户跑测试不是洁癖是止损我知道很多同学图省事直接 root 一把梭。伪分布式测试环境里短期看 root 确实能绕开一部分命令权限问题但代价是你后续排查问题时日志里会有大量 “Permission denied” 和诡异的目录归属错误你根本分不清是配置问题还是权限问题。我习惯的做法是新建独立用户useradd -m hadoop passwd hadoop然后把 Hadoop 安装目录的属主改给这个用户chown -R hadoop:hadoop /usr/local/hadoop以后所有操作都切到hadoop用户下执行。这样做的好处有三个一是日志目录的所有权限清晰可控二是 /tmp 下由 Hadoop 自动生成的临时文件不会和 root 的其他文件混在一起三是将来这个环境被销毁时删除/home/hadoop和/usr/local/hadoop就是全部清理不会有系统文件被我误删。3. 从零手写四个 XML一行行拆解配置意图3.1 下载与目录规划下载 Hadoop 可以从 Apache 官方镜像站找一个离你最近的源选择二进制压缩包hadoop-3.3.6.tar.gz。版本号以你实际拉到的为准3.3.x 之间小版本差异不影响本文中的配置。解压安装tar -zxvf hadoop-3.3.6.tar.gz -C /usr/local/ ln -s /usr/local/hadoop-3.3.6 /usr/local/hadoop用软链名的好处是将来升级 Hadoop 版本时只要切换软链接不用改一堆配置文件里的绝对路径。虽然测试环境很少考虑升级但这个习惯在真实运维里非常实用。然后规划数据目录mkdir -p /usr/local/hadoop/tmp/name mkdir -p /usr/local/hadoop/tmp/data chown -R hadoop:hadoop /usr/local/hadoop把 NameNode 元数据和 DataNode 数据单独放在tmp/name和tmp/data下是有意为之的。之所以不放在 Hadoop 安装目录里是因为测试环境经常需要整体重置集群只要把 tmp 目录删掉再重建就能做到“干净复位”。如果把数据目录混在安装目录中重置时很容易误删可执行文件。配置环境变量编辑~/.bashrcexport HADOOP_HOME/usr/local/hadoop export PATH$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64注意 PATH 里要同时包含 bin 和 sbin。bin下是hdfs、yarn、hadoop这类客户端命令sbin下是start-dfs.sh、stop-yarn.sh这类启停脚本。只配一个的话你会发现某个命令找不到。3.2 core-site.xml集群的“总入口”core-site.xml是 Hadoop 核心配置里面最关键的属性是fs.defaultFS。它决定了 Hadoop 客户端默认连接的 HDFS 地址可以理解成整个集群的根入口。configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property /configuration9000 是 NameNode 的 RPC 端口。客户端执行hdfs dfs -ls /时会通过这个端口向 NameNode 发起元数据请求。Hadoop 2.x 和 3.x 的 RPC 端口默认都是 9000这点没有变化。为什么这个配置这么重要因为如果你不配fs.defaultFSHadoop 默认走本地文件系统hdfs dfs命令会尝试访问一个本地文件系统很多新手装了 Hadoop 之后发现所有hdfs命令报错说“找不到文件”原因就是这里没有指向 HDFS。配置完成后hdfs命令才真正成为“HDFS 客户端”。3.3 hdfs-site.xml单副本测试的取舍hdfs-site.xml里重点配置三件事副本数、NameNode 元数据目录、DataNode 数据目录。configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/usr/local/hadoop/tmp/name/value /property property namedfs.datanode.data.dir/name value/usr/local/hadoop/tmp/data/value /property /configuration为什么副本数配 1伪分布式只有一台机器配 3 的结果就是在同一台机器的不同目录里塞三份相同的数据既没有容错意义又白白浪费磁盘。更麻烦的是有些 Hadoop 版本在单机多副本场景下会抛出“Not replicated yet”的警告反而干扰学习。测试环境里副本数1 是行业默认操作。为什么显式指定 name.dir 和 data.dirHadoop 默认的元数据路径是/tmp/hadoop-${user.name}。这个路径有隐患系统定时清理 /tmp 时你的集群元数据可能突然蒸发下次重启直接进入救火状态。显式指定目录既是好习惯也是给后续排查 clusterID 不匹配问题留出开关具体我在第 6 章讲。3.4 mapred-site.xml 与 yarn-site.xml计算框架如何挂到资源调度上mapred-site.xml里最关键的配置是计算框架名称configuration property namemapreduce.framework.name/name valueyarn/value /property /configuration如果不配这个属性MapReduce 默认以 local 模式运行任务不会提交给 YARN而是直接在本地跑。表面上 WordCount 一样出结果但你打开 YARN 的 8088 页面时会发现上面空空的根本没有任务记录。很多人以为“任务跑通了就是对的”其实跳过了整个资源调度环节。把 framework 名字配成 yarn是让 MapReduce 任务真正走进 YARN 的关键。然后是yarn-site.xmlconfiguration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.nodemanager.aux-services.mapreduce_shuffle.class/name valueorg.apache.hadoop.mapred.ShuffleHandler/value /property /configurationaux-services 是 NodeManager 提供给 MapReduce 框架的辅助服务其中mapreduce_shuffle负责 Map 端输出数据的 Shuffle 和排序。如果不配置任务提交后会在 ApplicationMaster 启动阶段直接失败日志里出现找不到 ShuffleHandler 之类的报错。如果机器内存偏小我强烈建议同时加上资源限制参数property nameyarn.nodemanager.resource.memory-mb/name value2048/value /property property nameyarn.scheduler.minimum-allocation-mb/name value256/value /property property nameyarn.scheduler.maximum-allocation-mb/name value2048/value /property然后在 mapred-site.xml 里同步限制容器内存property nameyarn.app.mapreduce.am.resource.mb/name value512/value /property property namemapreduce.map.memory.mb/name value512/value /property property namemapreduce.reduce.memory.mb/name value512/value /property这套参数在 2G 内存的机器上尤其重要。默认配置下YARN 会给容器申请 1G 以上的内存资源不足时 Container 会被直接 kill任务报错 OOM。调小之后虽然速度慢一点但至少能稳定跑完测试。3.5 环境变量与 hadoop-env.sh 的细节hadoop-env.sh位于$HADOOP_HOME/etc/hadoop/脚本里有很多环境变量预设但真正必须动的是 JAVA_HOME。即使你已经在系统层面配置了 java也建议在这里写一遍export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64 export HADOOP_HOME/usr/local/hadoop另外Hadoop 3.x 的运行脚本默认把 PID 写到/tmp这在重启时偶尔会因为 PID 文件过期导致误判进程已存在。如果你不想遇到这种问题可以在 hadoop-env.sh 里统一设置export HADOOP_PID_DIR/opt/hadoop/pids mkdir -p $HADOOP_PID_DIR这一步非必须但我在多次测试中吃过 PID 文件残留的亏写在这里给你避雷。4. 格式化、启动与健康检查稍不注意就留隐患4.1 格式化 NameNode只能做一次的动作首次启动 HDFS 之前必须格式化 NameNodehdfs namenode -format格式化的本质是初始化 NameNode 的系统目录生成current/VERSION文件里面包含一个全局唯一的clusterID。之后 DataNode 启动时会读取自己数据目录里的 clusterID并跟 NameNode 的 clusterID 比对不一致就拒绝注册。所以这里有一条铁律不要反复执行格式化命令。每一次 format 都会生成新的 clusterID但已经生成了数据的 DataNode 目录里的 clusterID 并不会自动跟着变结果就是 NameNode 正常起来了DataNode 却反复报IncompatibleClusterIDException整个集群看起来半死不活。如果确实需要重置集群正确做法是先停掉所有进程然后删除tmp/name和tmp/data目录再重新执行格式化。这样 NameNode 和 DataNode 拿到的是同一套新 ID才能愉快地合作。format 命令执行成功时屏幕上会出现 “Storage directory /usr/local/hadoop/tmp/name has been successfully formatted” 这样的提示。没看到这句话说明格式化失败先解决报错再继续不要稀里糊涂往下跑。4.2 首次启动的顺序与日志观察法格式化完成后按照固定顺序启动start-dfs.sh start-yarn.sh先启 HDFS再启 YARN这是因为 YARN 上的计算任务依赖 HDFS 提供数据。虽然也有start-all.sh一把梭的命令但我个人习惯是分开启动。分开的意义在于两个框架各自有一份日志分开启动能让我明确知道当前是哪个框架出了问题而不是面对一份混合日志里找线索。启动脚本执行完后第一件事永远是看进程jps正常情况下应该看到五个进程NameNodeDataNodeSecondaryNameNodeResourceManagerNodeManager少了哪一个就去$HADOOP_HOME/logs/下找对应的日志。日志文件的命名规则类似hadoop-hadoop-namenode-hostname.log用tail -n 200直接看末尾几百行。日志是你判断问题的唯一权威依据比拍脑袋猜配置靠谱一百倍。4.3 三分钟健康检查jps、Web UI、文件系统很多新手在确认进程都起来之后就急着跑任务。我建议你花三分钟做一套健康检查把环境的地基打牢。第一步已经做过jps 确认进程齐全。第二步打开 Web UI 验证页面服务NameNode UIhttp://localhost:9870ResourceManager UIhttp://localhost:8088注意 Hadoop 3.x 的 NameNode HTTP 端口已经从 50070 改为 9870。如果你用 2.x 的习惯访问 50070看到的只能是连接失败然后陷入“我哪配错了”的自我怀疑。第三步操作文件系统验证客户端链路hdfs dfs -ls / hdfs dfs -mkdir -p /test能正常返回列表、创建目录说明 HDFS 客户端与 NameNode 之间的 RPC 通信通畅整个 HDFS 链路已经可用。如果这三步都过基础环境就稳了接下来跑任务只是时间问题。4.4 端口清单与“看哪个日志”的判断Hadoop 3.x 常用端口如下端口进程作用9870NameNodeWeb UI9000NameNodeRPC客户端读写入口9864DataNodeHTTP UI / 数据传输9866DataNodeRPC8088ResourceManagerYARN Web UI8032ResourceManagerRPCApplicationMaster 调度8042NodeManager日志与 UI19888JobHistoryMapReduce 历史任务 UI排查时记住这个顺序先确认对应进程在不在再确认端口通不通最后看日志。具体来说客户端连不上 HDFS先检查 9000 端口是否监听。HDFS 网页打不开检查 9870。任务提交后一直卡着不动去 8088 看是不是 Application 一直处于 PENDING。任务运行中某个 Task 反复失败去 NodeManager 的日志目录抓详情。这套排查链路建立起来之后你会发现绝大多数问题在 10 分钟内就能定位而不是在配置文件和网上搜索里无限循环。5. 跑通一个完整的 MapReduce 测试从 WordCount 到排错5.1 用 hdfs dfs 完成基本文件验证进入测试环节先在本地准备一个小文件然后传到 HDFS 上mkdir -p ~/hadoop-test echo hello hadoop hello scala hadoop hdfs ~/hadoop-test/test.txt hdfs dfs -mkdir -p /user/hadoop/input hdfs dfs -put ~/hadoop-test/test.txt /user/hadoop/input/ hdfs dfs -cat /user/hadoop/input/test.txt这里提醒一个 HDFS 和 Linux 文件系统的差异HDFS 没有 “cd 到某个目录” 的概念你用什么路径访问就必须写全路径。如果你习惯了 Linux 终端的管理方式一上来执行hdfs dfs -ls input大概率会报错说找不到input。另外如果你用 hadoop 用户执行命令报权限不足多半是因为 HDFS 根目录里没有以这个用户名命名的用户目录。手动创建一下就行hdfs dfs -mkdir -p /user/hadoop hdfs dfs -chown -R hadoop:hadoop /user/hadoop5.2 WordCount 全流程执行记录文件上传完成后运行 Hadoop 自带的 WordCount 示例hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar \ wordcount /user/hadoop/input /user/hadoop/output执行过程中你会看到一行行 job 进度输出Map 和 Reduce 的百分比不断滚动。这里有一个容易踩的坑输出目录/user/hadoop/output在任务执行前必须不存在。MapReduce 框架不覆盖旧输出而是直接拒绝。如果你跑第二次记得换一个输出目录或者先执行hdfs dfs -rm -r /user/hadoop/output任务跑完后读取结果hdfs dfs -cat /user/hadoop/output/part-r-00000看到词频统计结果比如hadoop 2、hdfs 1说明整条链路已经全部打通客户端提交 Job → YARN 分配容器 → Map 读数据 → Shuffle 排序 → Reduce 聚合 → 写回 HDFS。这一步走通之后你可以在伪分布式上测试任何自己的 MapReduce 代码。如果你是测试自己的业务 jar命令格式可以换成hadoop jar /path/to/your-job.jar com.example.MainClass /input /output在测试阶段把代码打成带依赖的 jar 包提交是伪分布式环境里最直接的验证方式。5.3 测试中最容易遇到的三种报错第一个是FileAlreadyExistsException。报错信息里会明确说输出目录已存在。处理方式就是换目录或者删旧目录原因前面说过了框架设计如此不算 bug。第二个是Container killed on request或running beyond virtual memory limits。这个一看就是内存或虚拟内存超限在小内存伪分布式里尤其常见。处理分两步先调小 YARN 容器内存参数参考 3.4 节的配置如果还不行就在 yarn-site.xml 里关闭虚拟内存检查property nameyarn.nodemanager.vmem-check-enabled/name valuefalse/value /property第三步重启 YARN 让配置生效。注意改完 yarn-site.xml 之后必须执行stop-yarn.sh和start-yarn.sh只重启单个节点管理进程不吃配置。第三种是ConnectException或call from localhost to localhost:9000 failed on connection exception。这类报错的范围很广但排查思路不变先jps看 NameNode 进程在不在再netstat -an | grep 9000看端口听没听最后检查客户端的core-site.xml有没有被正确加载。客户端问题通常是环境变量不对服务端问题通常是数据目录权限不对对症下药就好。还有一种看着像权限问题实则也是权限问题的org.apache.hadoop.security.AccessControlException: Permission denied。本质是当前 Linux 用户对 HDFS 上的目标目录没有写权限按 5.1 里说的把用户目录建好、权限给足即可。6. 测试收尾与进阶停机顺序、数据残留与 Docker 化6.1 停止顺序和数据目录清理测试结束后停止集群的顺序和启动相反stop-yarn.sh stop-dfs.sh为什么先停 YARN因为 YARN 上可能还有 Application 正在跑如果你先把 HDFS 停了正在执行的 MapReduce 任务瞬间失去了文件系统依赖会抛出一堆莫名奇妙的 IOException日志特别难看。反过来先停 YARNApplication 会优雅结束HDFS 再从容退出一切都是干净的。如果你想彻底重置环境把测试期间产生的一切痕迹清掉执行rm -rf /usr/local/hadoop/tmp/name /usr/local/hadoop/tmp/data mkdir -p /usr/local/hadoop/tmp/name /usr/local/hadoop/tmp/data hdfs namenode -format注意清理之前先确认你已经拿到了所有需要的日志和测试结果。我见过有人图省事直接删掉临时目录然后在下次启动时报错时才想起来还没下载某个关键日志不得不重新造数据浪费半天时间。6.2 频繁格式化的后果clusterID 不匹配伪分布式里最经典、最典型的问题就是NameNode 起来了DataNode 却一直起不来日志里出现IncompatibleClusterIDException或者类似 storage directory with clusterID XXXX is incompatible with clusterID YYYY 的提示。根因就是我在 4.1 节说的每次hdfs namenode -format都会生成一个新的 clusterID而 DataNode 数据目录里的 clusterID 停留在第一次格式化时的值。两边对不上DataNode 拒绝注册。遇到这个问题的处理方案有两种。第一种是彻底重置rm -rf /usr/local/hadoop/tmp/name /usr/local/hadoop/tmp/data hdfs namenode -format start-dfs.sh用删目录重新 format 的方式让两边都拿到新 ID最省心。第二种是手工改 DataNode 的 VERSION 文件。先查看 NameNode 的 clusterIDcat /usr/local/hadoop/tmp/name/current/VERSION再修改 DataNode 数据目录里current/VERSION中的 clusterID改成 NameNode 的值然后重启 DataNode。这种方法适用于你不想丢掉已有 HDFS 数据的场景测试环境里一般用不上但理解原理很重要。6.3 Windows 下开发环境联调的思路很多人在 Windows 上用 IDEA 写 MapReduce 代码然后想让它直接连上一台 Linux 虚拟机里的伪分布式集群。这个场景其实不需要在 Windows 上完整安装 Hadoop否则会遇到 winutils.exe 缺失、native 库不兼容、路径分隔符混乱等一堆破事。更务实的做法是在 Windows 的 Java 工程里引入hadoop-client依赖然后在代码里把 HDFS 地址指到虚拟机的 IP 和端口也就是把fs.defaultFS的 localhost 换成虚拟机的 IP。这样本地代码通过客户端协议连接远程集群不用跑任何本地守护进程。需要额外注意的事Windows 主机访问 Linux 虚拟机里的 NameNode 时防火墙必须放行 9000、9870、8088 等端口。我见过大量“代码没问题但连不上”的场景最后都是防火墙和安全组挡路。如果只是验证业务逻辑这个方案比在 Windows 上硬装 Hadoop 舒服得多也能保留 IDEA 的断点调试能力。6.4 快速用 Docker 跑伪分布式的思路如果说还有一种更快的起环境方式那就是 Docker。社区里维护了不少单节点 Hadoop 镜像启动一个容器进去JDK、SSH、Hadoop 配置都已经提前弄好了。你直接执行hdfs dfs和hadoop jar就能测试。相关热搜词里有“hadoop的docker镜像”这确实是一条捷径但我建议的顺序是先手动搭一次伪分布式再拥抱镜像。因为镜像把 SSH 免密、文件目录权限、内存资源管理这些细节都隐藏了你只看到成功的结果看不到背后的机制。万一容器里任务报错你对日志目录、配置来源完全没有概念排查起来基本抓瞎。反过来等你手动搭过一遍再用 Docker 镜像就会清晰很多哦原来镜像里的 NameNode 端口映射到了宿主机某个端口原来这个目录挂载出来是拿来持久化它的数据目录的原来容器重启后 clusterID 保留在这个 volume 里。这些认知只有亲手搭过一遍才长得出来。最后说点实际的感受。我见过不少同学搭伪分布式包括我自己第一次也是栽在 SSH 免密和反复格式化这两个问题上。这两个问题的本质都是对“脚本会通过 SSH 启动本地进程”和“format 会生成新的 clusterID”这两个底层机制没有概念。把这两件事想清楚剩下的步骤其实非常固定准备环境、改四个 xml、格式化、启动、看 jps、跑 jar、看日志。等你从头到尾完整跑通一次 WordCount再把 DataNode 日志翻上几页后面接触真正的多机集群时心里会踏实很多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进