1. 故障现场所有服务都活着唯独少了 SecondaryNameNode说实话Hadoop 集群里最容易被忽视的进程我觉得就是 SecondaryNameNode。它不像 NameNode 挂了全网瘫痪也不像 DataNode 少了就触发副本补偿它平时只是默默做 checkpoint存在感本来就低——所以当它真的没起来的时候很多人第一反应是“这玩意儿是不是本来就不用起”。我这次就踩了一个非常典型的坑一套刚搭好的测试集群执行完start-dfs.sh刷了一堆启动日志我以为一切正常结果jps一看NameNode、DataNode 都在SecondaryNameNode 连影子都没有。翻hadoop-hdfs日志一行刺眼的异常赫然在列java.lang.IllegalArgumentException: Does not contain a valid host:port authority: hdfs:看到这个报错我就明白了这跟内存不够、端口冲突统统无关是某个地址配置被解析成了一个残缺的 URI。今天就把这个问题的完整排查链路、根因原理和修复方法写出来希望能帮到同样被 “SNN 神秘失踪” 折磨的人。1.1 第一步看进程确认 SNN 确实缺失遇到“某某进程没起来”的问题第一步永远是确认进程状态别急着看配置。我在出问题的节点上跑了一下jps输出大概是这样的15678 NameNode 15992 DataNode 16321 ResourceManager 16634 NodeManager注意看NameNode 和 DataNode 都在ResourceManager、NodeManager 也在唯独少了SecondaryNameNode这一行。为了排除是jps偶尔抽风我又用ps确认了一遍ps -ef | grep -i secondarynamenode结果没有任何匹配的进程。这说明不是进程残留、不是假死而是 SecondaryNameNode 压根没活下来。确认了这一点下一步就直接去翻日志。1.2 第二步翻日志锁定异常发生的准确时间线HDFS 各组件的日志默认放在$HADOOP_LOG_DIR目录下如果不确定目录位置可以执行ls -l $HADOOP_LOG_DIR/ | grep -i secondary一般会看到类似hadoop-{用户名}-secondarynamenode-{主机名}.log的文件。我们这边 SNN 的日志文件名是hadoop-hdfs-secondarynamenode-test-node-01.log。用tail看末尾就能抓到启动失败的现场tail -n 200 $HADOOP_LOG_DIR/hadoop-hdfs-secondarynamenode-$(hostname).log日志末尾就是上面那段IllegalArgumentException而且能明显看到堆栈里出现了DFSUtil.getNamenodeServiceAddr和DFSClient.init。看到这两个类出现在报错堆栈里问题的方向基本就锁定了不是 SNN 专用的 checkpoint 模块出问题而是 HDFS 客户端在初始化 NameNode 地址时失败了。1.3 为什么这类问题容易被误判为“SNN 没被拉起”这里有个非常容易误判的点值得单独拿出来说start-dfs.sh启动 SNN 的行为依赖于$HADOOP_CONF_DIR/masters文件。如果masters文件不存在start-dfs.sh根本不会尝试启动 SecondaryNameNode这时候集群里没有这个进程是“正常”的但很多新手会以为是故障。如果masters文件存在但里面的主机名写错、或者 SSH 不通SSH 启动步骤会失败SNN 同样是没起来。我这次的情况属于第三种masters文件没问题SNN 确实被尝试启动了但在启动早期就抛了异常、直接退出。区分“没被拉起”和“拉起后退出”很简单就看有没有main线程的异常堆栈日志。有堆栈说明进程启动过是启动即崩没有堆栈说明压根没人叫它起床。2. 报错真相hdfs: 这个残体 URI卡在了 DFSClient 的 authority 校验上很多人在排查报错时习惯先搜“怎么解决”却很少问“这个报错到底是谁抛出来的、为什么这么写”。如果不把这一层想清楚改配置大概率是瞎试。所以这一节我们专门拆这个Does not contain a valid host:port authority: hdfs:。2.1 URI 的固定结构没有 host:port地址就无从谈起HDFS 里的地址有两种典型写法完整 URI 形式hdfs://nn-host:8020/由 schemehdfs、authoritynn-host:8020、path/组成。纯 host:port 形式nn-host:8020比如dfs.namenode.rpc-address的写法。报错里的hdfs:是URI.create(hdfs:)的结果——只有 scheme连双斜杠都没有更别提 authority 了。你可以自己在任意一台机器上用 Java 验证URI uri URI.create(hdfs:); System.out.println(authority uri.getAuthority()); // 输出 nullgetAuthority()返回null意味着整个 URI 里没有任何“主机名:端口”信息。这就好比一张快递单上只写了“快递:”两个字没有收件人、没有城市、没有街道快递公司的系统在录单阶段就直接拒收了根本不需要等快递员出门。2.2 DFSClient 构造器里那行硬校验这个报错在 HDFS 源码里非常明确。DFSClient构造时会拿用户配置里解析出来的 URI 去获取 NameNode 的地址核心逻辑简化下来是下面这个样子public DFSClient(URI nameNodeUri, Configuration conf, FileSystem.Statistics stats) throws IOException { this(DFSUtil.getNamenodeServiceAddr(nameNodeUri, conf), conf, stats); } // DFSUtil.getNamenodeServiceAddr 内部 private static String getNamenodeServiceAddr(URI uri, Configuration conf) { String authority uri.getAuthority(); if (authority null) { throw new IllegalArgumentException( Does not contain a valid host:port authority: uri); } // ... 后续逻辑 }注意最后一行异常消息直接拼接了uri的toString()所以当传入的 URI 是hdfs:时异常消息就会原样显示hdfs:。这种“启动早期校验”的设计其实很合理与其等到真正连接 NameNode 时才报一个莫名其妙的UnknownHostException不如在客户端对象创建的瞬间就告诉你“地址不完整别浪费时间了”。2.3 SNN 启动时为什么会被“客户端初始化”绊倒这里引出一个关键问题SecondaryNameNode 不是服务端进程吗为什么它会去创建DFSClient因为 SNN 的本质就是一个特殊的 HDFS 客户端。它的核心工作是周期性地从 NameNode 下载 Edits 日志、合并 FSImage、再把新的 FSImage 上传回去。整个流程里它必须作为客户端和 NameNode 建立 RPC 连接。在实际代码里SecondaryNameNode的构造函数会创建Checkpoint对象而Checkpoint内部会调用FileSystem.get(conf)来获取文件系统实例。当fs.defaultFS这个配置被解析成hdfs:这种残体时FileSystem.get走到DistributedFileSystem的初始化紧接着就触发DFSClient构造于是 authority 校验失败异常一路抛回main方法进程退出。所以整个现象链条是fs.defaultFS坏了 → SNN 创建 HDFS 客户端失败 → SNN 启动即退出 → 集群里没有 SecondaryNameNode。NameNode 和 DataNode 不受影响是因为它们启动时并不依赖fs.defaultFS去创建DFSClient。3. 逐步排查从 SNN 日志倒推出 fs.defaultFS 的问题知道了报错机制排查就有的放矢了。我们要回答的核心问题是运行时真正生效的fs.defaultFS到底被解析成了什么这里只靠眼睛看配置文件是不够的因为 Hadoop 的配置加载有多层覆盖逻辑。3.1 用 getconf 拿运行时值不要只盯着 xmlHadoop 提供了一条很实用的命令能直接读某个配置项的最终生效值hdfs getconf -confKey fs.defaultFS我当时执行的结果就是hdfs:看到这个输出基本就实锤了fs.defaultFS在运行时被解析成了hdfs:。但这里有个值得注意的点——这个值不一定是你写在core-site.xml里的那个值。Hadoop 的Configuration加载顺序大概是通过代码或类路径加载的各个 xml 配置文件同一个 xml 文件里如果存在多个同名property后出现的会覆盖先出现的-D等 JVM 系统属性参数可以覆盖 xml 里的值某些环境变量、启动脚本注入的内容也可能影响最终结果。所以遇到配置“看起来没问题行为却很怪”的情况优先用getconf打印运行时值不要对着文件猜。3.2 配置里的变量替换与空格陷阱我自己排查时第一件事是打开core-site.xml找到fs.defaultFS那一块看看有没有明显被写坏的内容。通常最直接的成因是手滑把value写成了property namefs.defaultFS/name valuehdfs:/value /property另一个很常见的场景是用了变量占位符但变量没有定义。比如模板里写的是property namefs.defaultFS/name valuehdfs://${nn.host}:8020/value /property如果${nn.host}没有被任何配置定义Hadoop 的Configuration会把未定义的变量替换成空字符串最终结果变成hdfs://:8020。这种结果是另一种报错形态但同样属于“地址残缺”家族。还有一种隐蔽情况是值的前后带了不可见字符比如value hdfs://nn-host:8020/value前面有个空格。Configuration.get返回的字符串不会自动 trim 前后空格URI.create对空格很敏感很容易抛出URISyntaxException。这些都属于配置被“污染”的类型。3.3 一条命令复现同类错误确认影响范围为了确认坏的配置不是只影响 SNN而是影响所有 HDFS 客户端我做了一个最小复现实验。在出问题的节点上直接执行hdfs dfs -ls /结果同样抛出java.lang.IllegalArgumentException: Does not contain a valid host:port authority: hdfs:这说明问题已经污染了全局 HDFS 客户端配置。反过来如果我在命令里显式指定完整的 URIhdfs dfs -ls hdfs://nn-host:8020/这条命令大概率能正常列出文件。因为命令行显式传入了完整 authority绕开了fs.defaultFS。这一步实验非常有价值它证明了 NameNode 的 RPC 服务本身是好的坏掉的是客户端侧的默认地址配置。4. 修复与验证改回合法的 host:port 并重启 SNN定位到根因之后修复本身并不复杂。麻烦的是很多人改完配置不验证重启之后发现还是不行又得回到原点。所以这一节我把改配置、同步、重启、验证的完整流程都过一遍。4.1 正确的 fs.defaultFS 写法与集群内同步当时我在core-site.xml里看到坏掉的配置直接改成合法的写法property namefs.defaultFS/name valuehdfs://nn-host:8020/value /property这里的nn-host要替换成你集群里实际运行 NameNode 的主机名端口要和dfs.namenode.rpc-address保持一致。如果记不清端口可以先执行hdfs getconf -confKey dfs.namenode.rpc-address拿到正确的 host:port 之后再往上填。改完之后绝对不要只改当前节点要把core-site.xml以及所有涉及地址的配置文件同步到所有 HDFS 相关节点上包括 NameNode、DataNode当然也包括 SNN 所在的节点。我当时的做法是直接用scp推送到各节点同时备份了一份修改前的版本方便回滚。4.2 重启 SNN 的推荐方式与验证清单配置同步完成后我执行了下面的命令重启 SNN。Hadoop 3.x 可以直接用hdfs --daemon stop secondarynamenode hdfs --daemon start secondarynamenode如果是老版本则用hadoop-daemon.sh start secondarynamenode这类传统脚本。如果测试环境允许直接sbin/start-dfs.sh一次性重启整套 HDFS 也没问题反正我对配置有把握。重启之后我按顺序做了四步验证缺一不可执行jps确认SecondaryNameNode进程已经出现。执行hdfs getconf -confKey fs.defaultFS确认输出的是hdfs://nn-host:8020而不是hdfs:。执行hdfs dfs -ls /确认全局客户端恢复正常。继续tailSNN 日志等待一个 checkpoint 周期看是否出现Checkpoint done、Uploading new image这类关键动作。4.3 别忘了先确认是否为 HA 集群这里额外提醒一种很容易让人白忙的情况如果你的集群配置了 HA比如有dfs.nameservices、多个 NameNode那么按照官方架构本来就不需要独立跑一个 SecondaryNameNode。NameNode 自身会承担 checkpoint 职责由dfs.namenode.checkpoint.period等参数控制。在这种 HA 集群里你看到没有 SecondaryNameNode 进程不等于故障。如果非要往masters文件里塞一个主机名去强制启动 SNN反而可能制造出各种奇怪问题。所以动手排查前先确认自己的集群是单 NameNode 还是 HA 模式别把正确架构当成错误来修。5. 举一反三HDFS 地址配置中容易踩中的相似坑处理完这个问题之后我顺手把集群里所有跟地址相关的配置全部过了一遍发现这类“地址残缺”问题其实是一个家族只是每种配置的报错形态不一样。把这一层搞明白以后就不会再被类似问题难住。5.1 哪些地址配置绝不能带 scheme 前缀先列一个我在实际运维中反复遇到的对照表配置项正确写法常见错误写法可能的报错方向fs.defaultFShdfs://nn-host:8020hdfs:、hdfs://authority 校验失败dfs.namenode.rpc-addressnn-host:8020hdfs://nn-host:8020解析异常/无法连接dfs.namenode.http-addressnn-host:9870hdfs://nn-host:9870解析异常/无法连接dfs.namenode.secondary.http-addressnn-host:9868hdfs://nn-host:9868解析异常/无法连接dfs.datanode.http.addressnn-host:9864hdfs://nn-host:9864解析异常/无法连接这里面有一个容易混淆的判断标准凡是最终要转成InetSocketAddress的配置一律只写 host:port不要画蛇添足加hdfs://凡是配置里要求填完整 URI 的比如fs.defaultFS才需要写hdfs://authority。很多人在dfs.namenode.rpc-address里写了hdfs://host:8020之后表面上看进程也能起来但某些内部解析逻辑会把hdfs当成主机名的一部分去处理导致后续 RPC 连接失败或者拿到的地址完全不对。这种问题比hdfs:报错更隐蔽因为不是第一时间崩溃而是运行一段时间后才暴露。5.2 变量替换、多环境注入导致地址残缺的常见场景我的个人体会是配置模板化是这类问题的重灾区。很多团队会用同一套配置模板同时管理开发、测试、生产环境靠${env.host}之类的占位符区分环境。一旦某个环境少定义了一个变量或者变量引用的 key 拼错了最终生成的配置里就会出现hdfs://:8020、hdfs:这种残体。还有一种常见场景是从别的集群直接拷贝配置拷完只改了fs.defaultFS的主机名却漏掉了dfs.namenode.rpc-address、dfs.namenode.http-address等配套项。于是出现“默认地址指向新集群实际通信地址指向旧集群”的割裂状态SNN 连不上 NameNode、客户端连不进集群各种诡异故障轮番来。所以我现在的习惯是任何地址相关的配置变更都要先列一份清单把所有带 host:port 的项都过一遍再决定改哪个。5.3 我实际踩过坑之后养成的排查习惯最后分享几个我自己沉淀下来的排查习惯不一定多高级但很管用改完任何地址配置第一件事永远是hdfs getconf -confKey 配置项确认运行时生效值而不是满足于“xml 文件里看着是对的”。看到Does not contain a valid host:port authority这个关键字第一反应不是去改别的配置而是顺着堆栈找到传入的 URI反推它来自哪一份配置、哪个变量。遇到“某进程没起来”先做三分类压根没被拉起、启动即退出、启动后失联。三类问题的排查路径完全不同乱了顺序就是浪费时间。检查同类配置项时顺手把空格、变量占位符、重复 property 这三个隐患一起扫掉很多倒霉问题其实是这三兄弟合伙搞出来的。这次 SNN 缺席的问题表面看是缺少一个进程实际是fs.defaultFS被解析成了hdfs:这种残缺 URI在 HDFS 客户端初始化的 authority 校验阶段就拦截了。只要你掌握了 URI 结构、校验位置、配置加载顺序这三件事以后再遇到类似的地址解析类报错基本都能一击命中。