ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

HDFS NameNode格式化失败:URI has an authority component报错全解析与修复

HDFS NameNode格式化失败:URI has an authority component报错全解析与修复 解决NameNode格式化失败IllegalArgumentException: URI has an authority component这段时间在搭一套Hadoop测试集群本来打算半小时搞定环境结果卡在hdfs namenode -format这一步报了一个很典型的错误java.lang.IllegalArgumentException: URI has an authority component这个报错在Hadoop新手群里出现的频率极高但凡接触过HDFS初始化的人十有八九都撞见过。它本身不是一个复杂的故障但报错信息比较绕很多人第一次看到完全不知道在说什么。这篇文章就把这个问题的来龙去脉彻底讲清楚从报错原理、根因定位到完整修复流程一步不落顺便把格式化前后容易踩的连环坑也一并整理出来。先给结论URI has an authority component的意思是HDFS在解析你传入的文件系统URI时发现这个URI带了authority部分——也就是//host:port这一段——而HDFS内部很多接口在解析默认文件系统时不允许URI里出现这个部分。最常见的凶手就是core-site.xml里的fs.defaultFS配置或者是格式化命令里多带了一个参数。1. 报错的典型场景与报错原理解读1.1 这个报错长什么样我遇到的报错堆栈是这样的$ hdfs namenode -format WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable ERROR namenode.NameNode: java.lang.IllegalArgumentException: URI has an authority component at java.net.URI.checkPath(URI.java:1823) at org.apache.hadoop.fs.Path.initialize(Path.java:204) at org.apache.hadoop.fs.Path.init(Path.java:171) at org.apache.hadoop.hdfs.DFSUtil.getNamenodeServiceAddr(DFSUtil.java:558) at org.apache.hadoop.hdfs.DFSUtil.getNamenodeServiceAddr(DFSUtil.java:513) at org.apache.hadoop.hdfs.DFSUtil.getNsServiceAddr(DFSUtil.java:494) at org.apache.hadoop.hdfs.server.namenode.NameNode.getAddress(NameNode.java:446) at org.apache.hadoop.hdfs.server.namenode.NameNode.getAddress(NameNode.java:453) at org.apache.hadoop.hdfs.server.namenode.NameNode.init(NameNode.java:635) at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1538) at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1668)关键行是java.net.URI.checkPath和Path.initialize这两个方法在做URI路径合法性校验。HDFS初始化NameNode时需要从配置里读取NameNode的RPC服务地址而这个地址最终被封装成一个Path对象Path构造过程中会调用URI.checkPath做校验——如果URI带了authority直接抛异常。1.2 authority在URI里到底是什么要理解这个报错先得搞清楚URI的组成。一个标准的URI长这样scheme://authority/path?query#fragment以hdfs://localhost:9000/user/data为例scheme是hdfsauthority是localhost:9000path是/user/dataauthority就是双斜杠后面、路径前面的那一整段通常包含了主机名和端口号。生活中我们每天用的URL也一样https://www.example.com/page里www.example.com就是authority部分。Hadoop源码里Path类会对URI路径做一层校验。在URI.checkPath方法中JDK明确规定如果这个URI不是opaque类型也就是不是mailto:这种没有双斜杠的URI那么它的path部分必须以/开头并且不允许带authority。这个限制其实是JDK层面的不是Hadoop故意刁难你。1.3 为什么HDFS格式化要校验这个这里要理清一个容易混淆的点。HDFS平时连接集群用的地址明明就是hdfs://namenode-host:9000这种带authority的形式为什么到了-format这里反而不让带authority了问题出在参数传递链路上。执行hdfs namenode -format时NameNode会从core-site.xml里读fs.defaultFS拿到默认文件系统URI然后把这个URI解析成NameNode服务地址。在NameNode启动早期代码会把这个默认URI的字符串直接包成一个Path对象来做路径规范化。如果这个过程中URI被解析成了带authority的形式Path构造就把整个URI当成一个文件路径来处理于是触发了checkPath的拦截。换句话说这个报错的本质是Hadoop在启动过程中把一个不应该带//host:port的路径参数错误地赋予了带authority的URI。绝大多数情况下就是配置文件里的fs.defaultFS写得不规范或者格式化命令多传了一个参数导致NameNode把//host:port当成了路径的一部分。2. 根因排查最常见的三个配置错误既然知道了是URI解析问题接下来就逐一排查到底是什么配置导致了authority混入。根据我自己的排障经历以及在网上翻过的无数帖子这类问题基本集中在下面三个地方。按概率从高到低排列。2.1 core-site.xml里fs.defaultFS配错这是最最常见的原因没有之一。fs.defaultFS定义了HDFS的默认文件系统它的值必须是一个合法的HDFS URI格式是property namefs.defaultFS/name valuehdfs://localhost:9000/value /property但很多人会犯以下几种错写成hdfs://localhost:9000/末尾多了一个斜杠。URI的path为/这种情况通常没问题但如果后面拼接其他路径时容易产生hdfs://localhost:9000//xxx这种双斜杠路径在某些Hadoop版本里就会导致URI解析异常。写成hdfs://localhost:9000/user。这样整个/user被当成了默认路径NameNode解析服务地址时就会把/user也塞进去格式化时出现奇怪的路径错误。写成localhost:9000漏了hdfs://前缀。这种情况下Hadoop把它当成一个默认的file://路径然后整个字符串都被认为是路径的一部分格式化时直接报URI has an authority component。我曾经在这上面踩过坑检查了半天代码最后发现就是少写了三个字符。value标签里混入了空格或换行。比如value hdfs://localhost:9000 /valueXML里换行和缩进会被当成值的一部分虽然Hadoop会尝试trim但有些版本处理不干净一样会翻车。修复方法很简单把fs.defaultFS写成规范格式property namefs.defaultFS/name valuehdfs://localhost:9000/value /property改完配置后记得检查一下有没有被正确加载$ hdfs getconf -confKey fs.defaultFS如果输出是hdfs://localhost:9000说明配置没问题。如果输出带了一圈空格或换行就是配置文件写脏了。2.2 格式化命令带了多余的参数第二个高频原因是格式化命令的写法问题。hdfs namenode -format这个命令理论上只接受可选参数-force和-nonInteractive。但有些教程或者复制来的命令会写成这样hdfs namenode -format test这里的test会被当成额外的命令行参数传给NameNode。NameNode的启动类会把所有非-开头的参数视为URI或者路径的一部分结果就是格式化时把这些额外的参数当成了URI的authority来源直接触发IllegalArgumentException。我见过有人把集群名、目录名、用户名等等都跟在-format后面五花八门。正确做法是# 格式化首次 hdfs namenode -format -force # 或者加上非交互参数避免格式化过程中卡在确认提示 hdfs namenode -format -force -nonInteractive如果你确实想给集群起个名字那是在hdfs-site.xml里设置dfs.nameservices而不是往格式化命令后面怼参数。2.3 环境变量和配置文件加载异常第三种情况不太常见但一旦遇到就很隐蔽HADOOP_CONF_DIR环境变量指向了错误的目录导致Hadoop加载了一个不是你本意的core-site.xml。举例来说如果你机器上同时装了Hadoop 2.x和3.x或者曾经把某个发行版的配置目录写进了~/.bashrc那么执行hdfs命令时Hadoop可能加载了另一份配置。那份配置里的fs.defaultFS可能是测试环境的地址比如hdfs://192.168.1.10:8020于是本机格式化时就去解析这个远程地址URI校验自然就出问题了。排查方式$ echo $HADOOP_CONF_DIR $ which hdfs $ hdfs --config /path/to/your/conf namenode -formathdfs --config可以显式指定配置目录如果这样格式化成功说明就是环境变量指错了。我建议在正式格式化之前先执行hdfs getconf -confKey fs.defaultFS看一眼实际加载到的配置确认来源是否是当前项目的配置文件。3. 从定位到修复完整实操流程下面给出一套从零开始的完整排查修复流程按步骤操作即可。3.1 第一步确认报错上下文执行格式化命令把完整堆栈打出来hdfs namenode -format 21 | tee /tmp/namenode-format.log用tee把日志存下来方便后面翻查。然后定位到Caused by那一行确认是否是IllegalArgumentException: URI has an authority component。3.2 第二步检查三大配置文件重点检查core-site.xml的fs.defaultFShdfs-site.xml的dfs.namenode.name.dir和dfs.datanode.data.dir。用我前面提到的方法# 查看实际加载的默认文件系统 hdfs getconf -confKey fs.defaultFS # 查看NameNode元数据目录 hdfs getconf -confKey dfs.namenode.name.dir # 查看DataNode数据目录 hdfs getconf -confKey dfs.datanode.data.dir如果hdfs getconf输出的值和预期不符检查配置文件里的空格、标签闭合、XML注释是否异常。我在生产环境里遇到过一种情况core-site.xml里fs.defaultFS的值写的是hdfs://localhost:9000后面跟了一个肉眼几乎看不见的尾随空格getconf输出看起来正常但实际解析时这个空格导致URI拼接出错。排查到怀疑人生最后是拷贝配置到文本编辑器里开了显示空白字符才发现的。3.3 第三步清理残留元数据格式化之前务必确认NameNode元数据目录是干净的。如果之前初始化失败过或者目录里已经有半截子数据重新格式化可能遇到NameNode is already formatted或者集群ID冲突。稳妥的做法是# 假设name dir是 /data/hadoop/namenode rm -rf /data/hadoop/namenode/* # 如果datanode的数据目录也需要重建 rm -rf /data/hadoop/datanode/*注意这一步是销毁性操作。如果集群里有真实数据千万别执行请先备份current目录下的VERSION和fsimage_*文件。本地测试环境无所谓生产环境谨慎操作。有些同学会问能不能不清空直接重复格式化答案是NameNode第一次格式化后会在dfs.namenode.name.dir下生成current/VERSION文件里面记录了namespaceID和clusterID。如果不清空NameNode会提示already formatted并要求加-force但即使加了-force硬格式化新生成的clusterID和DataNode上已有的clusterID也会不一致后面DataNode注册时会失败。所以如果只是测试环境直接清空重来是最省心的。3.4 第四步重新格式化并验证清理干净后重新执行hdfs namenode -format -force -nonInteractive如果一切正常日志里会出现INFO namenode.NameNode: STARTUP_MSG: /************************************************************ STARTUP_MSG: Starting NameNode ... INFO common.Storage: Storage directory /data/hadoop/namenode has been successfully formatted. INFO namenode.FSImageFormatProtobuf: Saving image file /data/hadoop/namenode/current/fsimage.ckpt_0000000000000000000 using no compression INFO namenode.NameNode: SHUTDOWN_MSG: /************************************************************ SHUTDOWN_MSG: Shutting down NameNode at localhost/127.0.0.1 ************************************************************/注意Storage directory ... successfully formatted这一行看到它才算真正成功。格式化完成后检查一下元数据目录$ ls -l /data/hadoop/namenode/current/ -rw-r--r-- 1 hadoop hadoop 17 3月 16 10:00 VERSION -rw-r--r-- 1 hadoop hadoop 32 3月 16 10:00 fsimage_0000000000000000000 -rw-r--r-- 1 hadoop hadoop 62 3月 16 10:00 fsimage_0000000000000000000.md5VERSION文件里面记录了namespaceID、clusterID等信息这些信息后续DataNode格式化时会用到。3.5 第五步启动HDFS验证格式化完成后启动HDFSstart-dfs.sh然后检查进程状态jps正常情况下能看到NameNode和DataNode两个进程。如果DataNode没有起来查看日志$HADOOP_HOME/logs/hadoop-hadoop-datanode-*.log排查是不是clusterID不匹配的问题。4. 格式化成功之后别高兴太早的连环坑格式化只是第一步真正让新手崩溃的是格式化成功之后冒出的一堆新问题。这里把最常见的三个隐患提前摆出来省得到时候抓瞎。4.1 NameNode卡在安全模式格式化后第一次启动NameNode会进入安全模式。安全模式下HDFS是只读的不能创建目录或写入文件。如果执行hdfs dfs -mkdir /test报错Name node is in safe mode不用慌。安全模式期间NameNode会等待DataNode上报数据块只要数据块上报达到阈值会自动退出安全模式。可以用下面命令手动检查hdfs dfsadmin -safemode get如果想快速退出可以执行hdfs dfsadmin -safemode leave但如果每次重启都是卡在安全模式而且时间很长就要检查DataNode是否正常注册了。有一种常见情况是dfs.replication设置太高而DataNode数量不够导致安全模式阈值永远达不到。测试环境把副本数调成1可以显著缩短安全模式时间property namedfs.replication/name value1/value /property4.2 DataNode连不上NameNode格式化全部成功后启动集群发现NameNode进程在但DataNode进程反复退出日志里报连接拒绝或集群ID不匹配。这种情况90%是clusterID不一致。前面说了清空元数据目录重新格式化后NameNode会生成一个全新的clusterID但DataNode的dataDir里还留着上次的clusterID。两者对不上DataNode注册就会被拒。修复方式有两种。第一种是清理DataNode数据目录让它重新格式化rm -rf /data/hadoop/datanode/*然后重启DataNode。第二种是把DataNode的VERSION文件里clusterID改成和NameNode一致。第二种方式适合数据不能丢的场景本地测试就用第一种干净利落。4.3 客户端访问报错还有一种情况是集群起来了但在客户端执行hdfs dfs -ls /直接报java.net.ConnectException: Connection refused。这种多半是客户端拿到的fs.defaultFS配置里的端口和hdfs-site.xml里dfs.namenode.rpc-address的端口对不上。例如fs.defaultFS写了hdfs://localhost:9000但dfs.namenode.rpc-address配置的是hdfs://localhost:8020NameNode实际监听的是8020客户端连9000自然失败。解决这个问题的思路是让这三处保持一致。最省事的做法就是fs.defaultFS直接用dfs.namenode.rpc-address的值。5. 常见问题速查与排障思路梳理为了让大家以后遇到类似问题能快速定位我把整个排查过程整理成了一张速查表。现象可能原因快速定位方式修复动作格式化报URI has an authority componentfs.defaultFS没有hdfs://前缀或值里混入空格hdfs getconf -confKey fs.defaultFS修改core-site.xml写成规范URI重启命令格式化报URI has an authority component-format命令后跟了多余参数检查命令行参数只保留-force和-nonInteractive格式化报already formatted元数据目录没清空查看current/VERSION备份后清空dfs.namenode.name.dir格式化成功但DataNode起不来clusterID不一致对比NameNode和DataNode的VERSION文件清空DataNode数据目录或同步clusterID启动后卡安全模式数据块阈值达不到hdfs dfsadmin -safemode get降低dfs.replication或手动leave客户端连接拒绝端口配置不一致ss -lntpgrep java格式化时加载了错误配置HADOOP_CONF_DIR指向错误目录echo $HADOOP_CONF_DIR用--config显式指定目录这张表覆盖了HDFS初始化阶段绝大多数故障照着查基本能解决。当然Hadoop版本迭代频繁不同版本报错信息可能有差异比如Hadoop 3.x下URI校验的堆栈行号会变但核心逻辑没有区别。5.1 一个排查技巧用hadoop命令逐步拆解URI如果改了配置还是报同样的错可以用命令手动验证URI解析是否正常hadoop fs -ls hdfs://localhost:9000/如果这条命令报错说明URI本身有问题去查配置。如果命令正常那问题可能出在NameNode启动时从其他配置项读取了异常的URI比如dfs.namenode.servicerpc-address、dfs.namenode.http-address等逐一用hdfs getconf检查。5.2 从源码层面理解这个报错如果你跟我一样有刨根问底的毛病可以沿着这个路径去翻源码。hdfs namenode -format最终调用NameNode.main()它创建一个NameNode实例构造函数里调用getAddress()获取NameNode绑定地址。getAddress()内部拼接URI时会用到DFSUtil.getNamenodeServiceAddr这个方法会构造一个包含路径的地址字符串。如果配置里的URI带了不该有的路径或authority最后生成一个奇怪的地址再被Path类校验时就翻车了。源码里最关键的地方是Path.initialize调用uri.checkPath()而checkPath是JDKURI类自带的校验逻辑。所以这个错误的根子其实在JDK的URI路径校验规则上Hadoop只是不幸踩中了这个规则。理解了这一层以后再看到URI has an authority component第一反应就应该是某个地方的URI被当成了文件路径来解析去查URI是什么、从哪里来的。6. 一些排障之外的经验之谈最后聊一点个人经验。这类报错在网上搜索时最常见的回复是“改core-site.xml的fs.defaultFS”但这个答案太笼统因为很多人压根不知道自己改的就是错的。我的建议是在动手改之前先想清楚HDFS的URI体系是什么样的hdfs://是协议标识localhost:9000是服务地址两者一个都不能少也一个都不能多。理解了这个规则后面很多故障都能触类旁通。另外格式化的时机也是有讲究的。我见过有人在集群运行过程中因为改了一些配置就随手执行格式化结果把整个集群的元数据全清了。格式化是初始化操作不是重启操作初期搭建集群时用一次就够了之后如果配置有改动不需要重新格式化。如果真的需要重新初始化也要先停掉所有HDFS相关进程清空NameNode和DataNode的目录再统一格式化、统一启动。顺序乱了后面会出现莫名其妙的连接问题。这篇内容是根据我实际踩坑整理出来的希望能帮你少走弯路。按照上面的步骤走一遍大部分情况下都能顺利解决。如果按照步骤操作后问题依然存在也别急把完整的堆栈日志拿出来逐行看通常在Caused by那一行就能找到真正的线索。
RELATED READING

延伸阅读

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