
HDFS 集群跑久了几乎每个运维团队都会遇到同一个问题当初为了扛住业务高峰堆上去的机器现在成了机房里最安静的一排——CPU 常年个位数磁盘写满一次就再没动过电费和维保账单却一分不少。节点缩容这件事说起来就是把机器下线真动手的时候才发现HDFS 不是把机器一关就完事的系统副本怎么迁、数据怎么均衡、NameNode 的元数据怎么更新、下线过程中业务读写会不会抖每一步都有坑。这篇就把我在几个生产集群上做 DataNode 缩容的完整思路拆开讲从判断该缩哪些节点到 decommission 的底层流程再到缩容后的收尾验证尽量把能踩的坑都提前标出来。1. 先想清楚缩容不是删机器是一次有状态的数据搬迁很多人对缩容的第一反应是挑几台空闲的 DataNode关机、从配置里删掉、重启集群。这个做法在小数据量、低副本、无业务的测试环境里可能侥幸跑通但在生产环境里基本等于主动制造数据丢失事故。原因很直接HDFS 的每个数据块默认有 3 个副本分散在不同 DataNode 上你直接关掉一台机器它上面承载的那部分副本就瞬间不可用了。如果恰好某个块的另外两个副本所在的节点也有问题磁盘故障、网络分区、正在重启这个块就进入副本不足甚至块丢失状态读请求直接报错。所以缩容的本质是一次受控的、可回滚的数据副本迁移过程。HDFS 为此专门设计了 decommission退役机制让 NameNode 主动把待下线节点上的数据块复制到其他节点等副本数满足要求后再把这个节点标记为已退役此时关机才是安全的。理解这一点后面所有的操作步骤就都有了逻辑主线我们做的每一件事都是在保证任意时刻每个数据块的可用户副本数不低于副本因子。1.1 缩容前必须先回答的三个问题动手之前我习惯先把三个问题问清楚这三个答案直接决定了缩容方案怎么设计。第一个问题缩几台、缩哪些不是所有看起来闲的节点都适合下线。要综合看磁盘使用率、节点上的块数量、机架分布。如果某个机架上只有两台 DataNode你缩掉一台这个机架就只剩单副本一旦机架级故障比如整排机柜断电数据就有风险。所以缩容选节点时机架感知的均衡比单机负载更重要。第二个问题集群当前的健康水位如何缩容过程中会有大量数据搬迁会占用网络带宽和磁盘 IO。如果集群本身磁盘使用率已经到 85% 以上缩容搬迁很可能把剩余节点写爆。我的经验阈值是缩容前集群整体磁盘使用率控制在 70% 以下给搬迁留出足够余量。同时要确认没有块处于 under-replicated副本不足状态否则先修复再缩容。第三个问题业务能接受多大的性能抖动decommission 会触发大量块复制默认不限速的话能把万兆网卡打满影响正常读写。所以要么在业务低峰期做要么配置带宽限速。这个问题没有标准答案但必须提前和业务方对齐别等出问题了再解释。1.2 一个容易被忽略的前置检查副本因子与机架配置在真正执行 decommission 之前有两项配置必须核对否则缩容过程会卡住或者结果不符合预期。第一项是副本因子。用hdfs dfsadmin -report或者查看hdfs-site.xml里的dfs.replication确认当前副本数。如果集群是 3 副本缩容后剩余节点数必须能支撑每个块放下 3 个副本且尽量跨机架。假设你有 3 个机架、每个机架 4 台 DataNode缩掉某个机架的 2 台后还剩 2 台勉强能维持机架内副本但如果继续缩就会破坏机架级冗余。第二项是机架感知脚本。检查net.topology.script.file.name是否配置正确用hdfs dfsadmin -printTopology看输出是否符合实际的机架分布。我见过有集群这个脚本压根没配所有节点都被当成同一个默认机架这种情况下 decommission 虽然能跑完但副本分布完全没有机架冗余可言缩容等于把风险悄悄放大了。提示缩容前把hdfs dfsadmin -report和hdfs fsck / -files -blocks -locations的输出各存一份作为缩容前的基线快照。后面验证时对比着看比凭记忆靠谱得多。2. decommission 到底在后台做了什么搞清楚 decommission 的内部流程你才能在它卡住的时候知道卡在哪。这块机制其实不复杂但很多人只记住了操作命令没理解状态机出问题时只能干等。2.1 从服役到退役的状态流转一个 DataNode 在 NameNode 眼里有几个关键状态In Service正常服役、Decommissioning退役中、Decommissioned已退役、Entering Maintenance进入维护等。当你把一个节点加入 exclude 列表dfs.hosts.exclude并让 NameNode 重新加载配置后这个节点就从 In Service 进入 Decommissioning。进入 Decommissioning 后NameNode 会做两件事第一停止向这个节点分配新的写请求后续的新块不会再落到它上面第二扫描这个节点上所有的块对每个块检查其他节点上的副本数是否满足副本因子如果不满足就触发复制任务把缺失的副本补到其他健康节点上。这个过程是持续进行的直到这个节点上所有块的外部副本数都达标节点才会被标记为 Decommissioned。这里有个关键细节判断标准是其他节点的副本数是否足够而不是这个节点上的数据是否被搬空。也就是说如果一个块在这个节点上有副本但在其他节点上已经有 3 个副本了那这个副本根本不需要搬NameNode 直接认为它可以丢弃。所以 decommission 完成后节点上往往还残留着一些数据这是正常的不代表没搬干净。2.2 为什么有的节点几小时就退役完有的要跑好几天decommission 的耗时取决于两个因素待搬迁的数据量和复制带宽。数据量好理解节点上块越多、块越大需要补的副本就越多。带宽这块HDFS 默认对复制流量是不限速的但实际吞吐受限于源节点磁盘读、目标节点磁盘写、网络带宽三者中的短板。我遇到过最典型的情况是一个节点 decommission 卡了两天没动静查下来是目标节点所在机架的网络交换机端口跑满了复制任务排队。还有一种情况是小文件过多块数量巨大但单块很小NameNode 的复制调度成为瓶颈表现为 decommission 进度条走得很慢。这种时候调大dfs.namenode.replication.work.multiplier.per.iteration控制每轮心跳触发的复制任务数能加快调度但要配合监控别把 NameNode 压垮。可以用hdfs dfsadmin -report观察 Decommissioning 节点的 Blocks 和 Under replicated blocks 变化也可以用hdfs fsck / -list-corruptfileblocks之外的方式重点看 NameNode 的DecommissioningNodes指标。生产上我一般会盯 NameNode UI 的 Datanodes 页面退役中的节点会显示进度百分比比命令行直观。2.3 带宽限速别让搬迁把业务拖垮如果集群和业务共用网络decommission 期间一定要限速。HDFS 提供了dfs.datanode.balance.bandwidthPerSec这个参数但它主要作用于 balancer 工具decommission 的复制流量走的是 DataNode 之间的正常传输通道受dfs.datanode.max.transfer.threads和网络层限速影响。更稳妥的做法是在交换机或主机层面做流量整形或者干脆把 decommission 安排在业务低峰窗口。我个人的习惯是如果集群规模在几十台以内、业务对延迟敏感就选凌晨窗口做不折腾限速如果集群上百台、缩容周期长就在 DataNode 所在主机上用 tc 做出口限速把复制流量控制在总带宽的 30% 左右。这个比例不是拍脑袋是留足业务峰值余量后的经验值。3. 一步步执行从配置 exclude 到节点真正下线理论讲完进入实操。下面这套流程我在多个集群上验证过按顺序走基本不会出问题。需要说明的是不同发行版的配置文件名和路径略有差异命令以通用 HDFS 为准。3.1 第一步准备 exclude 文件并核对内容exclude 文件是一个纯文本文件每行一个主机名或 IP列出要退役的节点。这个文件的位置由dfs.hosts.exclude指定通常在 NameNode 的配置目录下。# 假设 exclude 文件路径为 /etc/hadoop/conf/dfs.exclude # 先备份原文件 cp /etc/hadoop/conf/dfs.exclude /etc/hadoop/conf/dfs.exclude.bak.$(date %Y%m%d) # 追加待退役节点一行一个 cat /etc/hadoop/conf/dfs.exclude EOF datanode-05.example.internal datanode-06.example.internal EOF # 核对内容确保没有误加仍在服役的节点 cat /etc/hadoop/conf/dfs.exclude这里有个血泪教训exclude 文件里千万不要留空行或注释行。有些版本的 Hadoop 对空行处理不严格可能把空行当成一个无效主机名导致 NameNode 加载配置时报错或者行为异常。另外主机名必须和hdfs dfsadmin -report里显示的完全一致大小写、域名后缀都要对上否则 NameNode 认不出这个节点decommission 根本不会触发。3.2 第二步刷新 NameNode 配置观察状态变化配置改完后不需要重启 NameNode用 refresh 命令让它重新加载即可hdfs dfsadmin -refreshNodes执行后立刻用hdfs dfsadmin -report观察待退役节点应该从 Live datanodes 移到 Decommissioning datanodes 段落。如果节点还在 Live 里没动八成是 exclude 文件路径配错、主机名不匹配或者dfs.hosts.exclude压根没在hdfs-site.xml里配置。这时候别急着往下走先把配置问题解决。确认节点进入 Decommissioning 后就可以开始盯进度了。我一般会开一个循环脚本每隔几分钟打印一次状态while true; do echo $(date) hdfs dfsadmin -report | grep -A 3 Decommissioning datanodes sleep 300 done3.3 第三步判断退役完成的信号而不是干等怎么知道一个节点退役完了最直接的信号是hdfs dfsadmin -report里它从 Decommissioning datanodes 消失出现在 Decommissioned datanodes 段落。但更严谨的做法是结合 fsck 验证对退役节点上曾经承载的块做抽查确认副本数达标。# 查看集群整体健康重点关注 under-replicated blocks 是否为 0 hdfs fsck / -blocks | tail -20如果输出里 Under-replicated blocks 是 0Missing blocks 是 0说明集群副本健康。这时候退役节点上的数据已经不再承担可用性职责可以安全关机。需要提醒的是退役完成不等于数据搬空。前面说过NameNode 只保证其他节点副本足够节点上残留的副本会被逐步清理但这个过程可能滞后。所以关机前不用纠结节点上还剩多少数据只要集群 fsck 健康、节点状态是 Decommissioned就可以动手。3.4 第四步关机、清理配置、更新集群清单节点标记为 Decommissioned 后操作顺序建议是先停 DataNode 进程再关机最后从 exclude 文件里移除因为节点已经不在集群里了留在 exclude 里没意义反而会让配置越来越乱。# 在待下线节点上停止 DataNode hdfs --daemon stop datanode # 确认进程已退出 jps | grep -i datanode关机之后记得同步更新几样东西集群的节点清单文档、监控系统的采集目标、CMDB 或资产管理系统里的记录。这些收尾工作看起来琐碎但漏掉监控目标会导致告警一直响漏掉资产记录会让下次盘点对不上账。我见过有团队缩容后忘了删监控结果告警风暴持续了一周才有人发现。4. 缩容过程中最容易翻车的几个场景流程本身不复杂真正让人头疼的是各种意外。下面这几个场景都是我或同行实际遇到过的提前知道能省不少时间。4.1 退役卡在 99% 不动多半是某个块的副本补不上decommission 进度长时间停在接近完成的位置是最常见的假死现象。根因通常是某个块的副本无法复制到目标节点。可能的原因包括目标节点磁盘满、目标节点网络不通、副本放置策略找不到合适的机架、某个块本身已经损坏。排查思路是先用 fsck 找出副本不足的块hdfs fsck / -blocks | grep -i under-replicated如果输出里有具体的块 ID可以用hdfs fsck /path/to/file -files -blocks -locations定位到文件和所在节点然后检查这些节点的磁盘和网络状态。有时候问题出在副本放置策略上比如集群只剩两个机架而策略要求跨三个机架放副本就会一直找不到合适位置。这种时候要么调整副本因子要么补充机架硬等是等不出来的。4.2 缩容后集群磁盘使用率飙升搬迁的隐性成本decommission 是把副本从待退役节点复制到其他节点这意味着集群总数据量在缩容期间是增加的退役节点上的数据还没删新副本已经写进去了。如果集群原本磁盘使用率就高缩容期间很容易触发磁盘满告警甚至导致写入失败。这就是为什么前面强调缩容前使用率要控制在 70% 以下。如果实在没法降下来可以考虑分批缩容一次只退役一两台等副本清理、使用率回落后再退役下一批。分批的代价是周期变长但安全性高得多。我一般建议单批次缩容不超过集群总节点数的 10%这个比例在多数场景下能把使用率波动控制在可接受范围。4.3 误把仍在服役的节点加入 exclude一次惊险的回滚这个坑我必须单独说因为它的后果很严重。有一次操作时复制粘贴出错把一台核心 DataNode 的主机名加进了 exclude 文件refresh 之后这台节点立刻进入 Decommissioning开始往外搬数据。好在发现得早监控告警提示某节点进入退役状态及时把它从 exclude 文件移除并再次 refresh节点才回到 In Service。这个经历说明两件事第一exclude 文件修改后一定要 diff 确认别嫌麻烦第二refreshNodes 之后要立刻看 report确认只有预期内的节点状态变化。如果误操作发现得晚节点已经搬走大量数据回滚虽然也能让它重新服役但期间造成的副本迁移和网络开销是实打实的损失。注意从 exclude 文件移除节点后同样需要执行hdfs dfsadmin -refreshNodes才能生效。移除后节点会从 Decommissioning 回到 In Service但已经迁走的副本不会自动迁回需要靠后续的 balancer 重新均衡。4.4 缩容后没有重新均衡数据分布歪了decommission 只保证副本数达标不保证数据分布均匀。退役节点上的数据被复制到其他节点时NameNode 是按副本放置策略选目标节点的可能造成某些节点块数偏多、某些偏少。缩容完成后集群虽然健康但热点节点可能成为新的瓶颈。所以缩容收尾时建议跑一次 balancerhdfs balancer -threshold 5 -policy datanode-threshold 5表示磁盘使用率差异在 5% 以内就停止这个值太小会导致 balancer 反复微调、耗时很长太大又达不到均衡效果5% 到 10% 是常见区间。balancer 同样会占用带宽建议在业务低峰执行或者配合限速参数。5. 缩容后的验证清单与长期观察节点下线不代表事情结束缩容对集群的影响需要观察一段时间才能确认没有遗留问题。我习惯按下面这个清单逐项过一遍。5.1 短期验证副本健康与读写正常缩容完成后 24 小时内重点看三件事。第一hdfs fsck /的输出里 Missing blocks 和 Under-replicated blocks 必须都是 0Corrupt blocks 也应该是 0。第二抽查几个核心业务表的读写确认延迟没有明显上升。第三看 NameNode 的 RPC 队列和 DataNode 的心跳确认没有节点因为负载变化出现异常。可以用一个简单的脚本把关键指标拉出来对比缩容前的基线# 集群容量与使用率 hdfs dfsadmin -report | grep -E Configured Capacity|DFS Used%|DFS Remaining # 块健康 hdfs fsck / -blocks 2/dev/null | grep -E Total blocks|Missing blocks|Under-replicated把这两组数据和缩容前的快照对比容量减少是预期的但使用率的变化幅度要心里有数。如果使用率涨幅远超退役节点数据量 / 剩余节点总容量的估算值说明可能有副本异常膨胀需要进一步排查。5.2 中期观察热点转移与性能基线缩容后一到两周重点观察热点是否转移。原来退役节点承担的那部分读请求现在分散到了其他节点如果某些节点本身负载就高可能成为新的热点。看 DataNode 的磁盘 IO、网络吞吐和请求队列对比缩容前的分布确认没有节点持续跑满。另一个容易被忽略的点是机架级带宽。缩容如果集中在某个机架剩余节点之间的跨机架流量会增加交换机端口利用率可能上升。这个指标在主机监控里看不到需要网络侧的监控配合。我一般会在缩容后一周内找网络同事确认一下相关链路的利用率图个安心。5.3 长期视角把缩容纳入容量规划单次缩容做完更值得思考的是怎么让下次缩容更从容。我的做法是把节点生命周期纳入容量规划定期比如每季度评估一次集群资源利用率识别长期低负载的节点提前规划退役批次而不是等到机器快过保了才临时抱佛脚。同时缩容经验要沉淀成文档和脚本。exclude 文件的修改、refreshNodes、进度监控、fsck 验证、balancer 收尾这套流程完全可以脚本化把人工操作降到最低。脚本化不仅提效更重要的是减少误操作——前面说的误加节点那种事有了带确认步骤的脚本就能避免大半。6. 几个参数与策略层面的取舍心得最后聊几个偏配置层面的经验这些内容在官方文档里往往一笔带过但实际用起来差别很大。6.1 副本因子不是越高越安全要看集群规模有些团队为了更安全把副本因子调到 4 甚至 5结果缩容时发现剩余节点根本放不下这么多副本decommission 直接卡死。副本因子的选择要和集群规模匹配小集群10 台以内3 副本已经足够大集群可以考虑 3 副本加机架感知没必要盲目加副本。缩容前如果发现副本因子偏高可以先评估是否降到 3再执行退役能显著降低搬迁压力。6.2 维护模式Maintenance和退役Decommission别混用HDFS 还提供了 maintenance 模式用于临时下线节点做硬件维护。它和 decommission 的区别在于maintenance 模式下NameNode 允许副本数暂时低于副本因子有一个dfs.namenode.maintenance.replication.min的下限适合短时间维护而 decommission 是永久下线要求副本数严格达标。临时维护用 maintenance永久缩容用 decommission用错了要么搬迁量白白增加要么维护期间数据风险上升。6.3 缩容窗口的选择比技术细节更影响体验技术方案再完善如果选在业务高峰做缩容照样会被投诉。我的经验是缩容窗口优先选业务最低谷的连续时间段确保有足够时间完成搬迁和验证。如果集群大、单批缩容耗时长就拆成多个小批次每批都在低峰窗口内完成。宁可多花几个晚上也别在一个窗口里硬塞太多节点。这套流程走下来HDFS 节点缩容其实没有想象中那么可怕核心就是把数据搬迁这件事交给 decommission 机制人要做的是选对节点、控好节奏、盯紧状态、验证到位。真正容易出问题的从来不是命令本身而是对机制理解不到位导致的误操作和误判。把前面那些坑提前在脑子里过一遍动手的时候心里就有底了。