1. 从单机到集群:Hadoop到底解决了什么运维难题
很多刚接触企业级运维的朋友会有个疑问:现在单机服务器动辄几十核、上百G内存,数据库性能也很强,为什么还要搞Hadoop这套分布式系统?我刚开始做运维的时候也有这个困惑,直到真正接手了一个日均新增数据量在TB级别的日志分析平台,才彻底理解了Hadoop存在的意义。
核心问题就一个:单机存储和计算存在物理上限。假设你有一台配置不错的服务器,8块16T的硬盘做RAID,可用容量也就100T出头。当业务数据每天以2T的速度增长时,不到两个月磁盘就满了。你可能会说,那就再加磁盘柜嘛。但磁盘柜扩展是有极限的,而且单机的IO带宽、内存容量、CPU核心数都会成为瓶颈。更致命的是,一旦这台机器宕机,整个数据服务就全挂了,这在企业生产环境里是不可接受的。
Hadoop的思路很直接:既然一台机器搞不定,那就用一堆普通机器组成集群,把数据切碎了分散存,把计算任务也分散跑。这就像搬家,一个人搬一天也搬不完,但叫上二十个朋友,每人搬几趟,半天就搞定了。Hadoop就是那个“叫朋友帮忙”的调度系统,它负责把一个大任务拆成无数小任务,分配到不同机器上并行执行,最后再把结果汇总起来。
具体来说,Hadoop主要解决三个层面的问题。第一是分布式存储,通过HDFS(Hadoop Distributed File System)把大文件切成固定大小的数据块,分散存储到集群中不同节点上,同时每个块保留多个副本,保证部分节点故障时数据不丢失。第二是分布式计算,通过MapReduce编程模型,把计算逻辑分发到数据所在的节点上执行,也就是“移动计算而不是移动数据”,大幅减少网络传输开销。第三是资源调度,通过YARN(Yet Another Resource Negotiator)统一管理集群的CPU、内存等资源,让多个计算任务能够共享同一个集群,提高资源利用率。
对于企业运维人员来说,理解这三层架构是基本功。你不需要成为大数据开发工程师,但必须清楚HDFS的副本机制怎么配置、YARN的资源队列怎么划分、NameNode和ResourceManager这些核心组件的高可用怎么做。这些直接关系到集群的稳定性、性能和故障恢复能力。我见过太多因为NameNode单点故障导致整个集群不可用的案例,也处理过因为YARN队列配置不合理导致关键任务排队几小时都跑不起来的问题。这些坑,本质上都是对Hadoop核心原理理解不够深入造成的。
接下来的内容,我会从运维视角出发,把Hadoop的工作原理、部署要点和YARN资源管理这三个核心板块拆开来讲。不会堆砌太多开发层面的API细节,重点放在集群规划、配置调优、故障排查这些运维日常真正用得上的东西上。
2. HDFS的存储逻辑与运维必须关注的配置项
2.1 数据块、副本与机架感知:HDFS如何保证数据不丢
HDFS最基础的概念是数据块(Block)。在Hadoop 2.x及之后的版本中,默认块大小是128MB,而Hadoop 1.x时代是64MB。这个值不是随便定的,它需要在寻址开销和传输效率之间做平衡。块太小,NameNode需要维护的元数据条目就多,内存压力大;块太大,最后一个块可能浪费大量存储空间,而且并行读取的粒度变粗,影响计算效率。
我通常建议在两种场景下调整块大小:如果集群主要存储大量小文件(比如几KB到几MB的图片、日志片段),默认128MB会导致每个文件都占用一个块,NameNode内存迅速耗尽。这时候可以考虑用HAR归档或者CombineFileInputFormat来合并小文件,而不是简单调小块大小。如果集群主要做视频、备份等大文件存储,可以适当调大到256MB,减少Map任务数量,降低调度开销。
副本机制是HDFS可靠性的基石。默认副本系数是3,意味着每个数据块会在集群中保存三份。这三份不是随便放的,而是遵循**机架感知(Rack Awareness)**策略:第一份放在客户端所在节点(如果客户端在集群内),第二份放在不同机架的某个节点上,第三份放在第二份所在机架的另一个节点上。这样即使整个机架断电,数据依然可用。
注意:机架感知需要运维人员手动配置每个节点的机架信息,否则Hadoop会默认所有节点在同一个机架(/default-rack),副本分布策略就退化成随机选三个不同节点,容灾能力大打折扣。
配置机架感知的步骤不复杂,但容易被忽略。你需要写一个脚本或者用Python实现一个映射逻辑,把节点IP映射到机架路径,然后在core-site.xml中配置net.topology.script.file.name指向这个脚本。脚本的输入是一批IP地址,输出是对应的机架路径,比如/rack1、/rack2。实测下来,这个配置对集群的写入性能和故障恢复速度都有明显影响,尤其是在跨机架带宽有限的环境中。
2.2 NameNode与DataNode:主从架构下的运维红线
HDFS采用主从架构,NameNode是主节点,负责管理文件系统的命名空间和元数据;DataNode是从节点,负责实际存储数据块。这个架构简单清晰,但NameNode天然是单点。一旦NameNode宕机,整个HDFS集群就无法读写,所有依赖HDFS的上层应用全部瘫痪。
解决单点问题有两种方案:HA(High Availability)架构和联邦(Federation)。HA通过部署两个NameNode(Active和Standby)实现热备,借助JournalNode集群同步元数据操作日志,借助ZooKeeper实现自动故障切换。联邦则是把命名空间水平切分,多个NameNode各自管理一部分目录,互不干扰,但配置和维护复杂度更高。
对于大多数企业集群,我推荐优先上HA方案。部署HA时,运维需要重点关注几个配置项。dfs.namenode.handler.count控制NameNode处理RPC请求的线程数,默认是10,对于大规模集群(超过100个DataNode)建议调到100以上,否则客户端请求会排队。dfs.namenode.safemode.threshold-pct定义DataNode上报数据块的比例达到多少才退出安全模式,默认0.999,意味着99.9%的块必须可用。如果集群刚重启,DataNode上报慢,NameNode会长时间停留在安全模式,导致无法写入。这时候可以适当调低这个值,但要注意数据完整性风险。
DataNode这边,dfs.datanode.du.reserved是个容易被忽视但很重要的参数。它指定每个DataNode磁盘上保留的预留空间,默认是0。如果不设置,DataNode会把磁盘写满,导致操作系统和其他服务无法写入日志,最终引发节点故障。我一般建议预留10%到15%的磁盘空间,具体取决于磁盘容量和业务写入速度。
2.3 小文件问题:运维最头疼的HDFS性能杀手
小文件是HDFS的天然克星。每个文件、每个块在NameNode内存中大约占用150字节的元数据。如果集群有1亿个小文件,NameNode就需要大约15GB内存来维护元数据,这还不包括其他开销。更糟糕的是,读取大量小文件会频繁创建和销毁Map任务,每个任务启动开销可能比实际处理数据的时间还长。
我在实际运维中处理过一个小文件泛滥的案例:某业务系统每小时生成数万个几KB的日志文件,直接写入HDFS。不到三个月,NameNode内存使用率飙升到90%以上,集群响应变得极慢。解决方案分三步走:首先在数据写入端做聚合,把每小时的小文件合并成一个较大的文件再上传;其次对历史小文件用Hadoop Archive(HAR)打包,减少NameNode中的文件条目;最后在计算层用CombineFileInputFormat,让一个Map任务处理多个小文件。
提示:HAR文件一旦创建就不能修改,而且访问HAR中的文件需要额外的解包开销,所以它更适合归档冷数据,不适合频繁读写的热数据。
另一个实用技巧是开启NameNode的异步编辑日志(Async Edit Log)。在HA架构下,Active NameNode写编辑日志到JournalNode是同步操作,如果JournalNode响应慢,会拖慢整个NameNode。开启异步写之后,NameNode先把日志写到本地内存队列,再由后台线程同步到JournalNode,能显著降低写入延迟。但要注意,异步模式下如果NameNode在日志同步完成前宕机,可能丢失少量最近的操作记录,需要根据业务容忍度权衡。
3. 集群部署实战:从裸机到可用Hadoop平台
3.1 部署前的规划:节点角色分配与网络设计
部署Hadoop集群之前,规划工作比安装本身更重要。我见过太多因为规划不当导致后期扩容困难、性能瓶颈无法定位的案例。规划的核心是角色分配和网络设计。
角色分配方面,一个典型的中等规模集群(20到50个节点)通常这样划分:2台机器做NameNode HA(同时跑ZKFC和JournalNode),3台机器做JournalNode(可以和NameNode复用,但建议独立),1到2台做ResourceManager HA,剩余节点全部做DataNode和NodeManager。ZooKeeper集群建议3到5个节点,独立部署或者和JournalNode复用。如果集群规模超过100个节点,建议把NameNode、ResourceManager、JournalNode、ZooKeeper全部分离部署,避免资源争抢。
网络设计上,Hadoop集群对带宽要求很高。NameNode和DataNode之间的心跳、DataNode之间的数据块复制、MapReduce的Shuffle过程都会产生大量网络流量。我建议至少使用万兆网卡,如果预算允许,存储网络和业务网络做物理隔离。另外,机架感知配置要和实际网络拓扑一致,否则副本分布策略会失效。
操作系统层面,有几个必须做的优化。关闭swap分区或者把vm.swappiness调到0,避免Java进程被交换到磁盘导致性能骤降。调整文件句柄数限制,ulimit -n至少调到65536,因为Hadoop会打开大量文件。关闭透明大页(THP),这个特性在某些内核版本下会导致Hadoop进程出现不可预测的延迟抖动。时间同步必须做好,所有节点用NTP或者Chrony保持时间一致,否则Kerberos认证和日志排查都会出问题。
3.2 核心配置文件拆解:每个参数背后的意图
Hadoop的配置文件不少,但运维日常打交道最多的就是四个:core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml。我挑几个关键参数说说背后的逻辑。
core-site.xml中的fs.defaultFS指定默认文件系统,HA模式下要写成hdfs://mycluster这样的逻辑名称,而不是具体的主机名。io.file.buffer.size控制读写缓冲区大小,默认4096字节,对于高吞吐场景建议调到131072(128KB),能明显提升顺序读写性能。
hdfs-site.xml中的dfs.replication是副本数,生产环境建议3。dfs.blocksize是块大小,前面已经讨论过。dfs.namenode.name.dir和dfs.datanode.data.dir指定元数据和数据存储路径,建议配置多个路径,分别挂载不同磁盘,Hadoop会自动做条带化写入,提升IO吞吐。dfs.datanode.failed.volumes.tolerated定义容忍多少个磁盘故障,默认0,意味着一个磁盘坏了DataNode就下线。我一般设为1或2,给运维留出更换磁盘的缓冲时间。
yarn-site.xml中的yarn.nodemanager.resource.memory-mb指定NodeManager可用的总内存,默认8192MB。这个值必须根据节点实际物理内存来设置,建议留出20%给操作系统和其他服务。yarn.scheduler.minimum-allocation-mb是单个容器的最小内存,默认1024MB,如果业务有大量小任务,可以调低到512MB,提高资源利用率。yarn.nodemanager.vmem-check-enabled控制是否开启虚拟内存检查,默认true,但在某些JDK版本下会误报内存超限导致容器被杀,我通常建议设为false,用物理内存检查代替。
mapred-site.xml中的mapreduce.map.memory.mb和mapreduce.reduce.memory.mb分别控制Map和Reduce任务的内存,默认都是1024MB。这两个值要和YARN的容器内存配置协调,否则会出现任务申请的内存超过容器上限而被拒绝的情况。
3.3 格式化与启动:那些文档里不会写的细节
第一次启动Hadoop集群时,格式化NameNode是必须的步骤,但这里有个大坑:格式化只能做一次。如果你格式化了NameNode,然后启动集群,再格式化一次,会导致NameNode的集群ID和DataNode的集群ID不一致,DataNode会拒绝启动。解决办法是删除所有DataNode的数据目录,重新格式化,或者手动修改集群ID。我建议在格式化之前,先确认所有配置无误,并且备份好dfs.namenode.name.dir目录。
启动顺序也有讲究。HA模式下,先启动JournalNode集群,再启动ZooKeeper集群,然后启动两个NameNode(先启动的那个会自动成为Active),接着启动ZKFC,最后启动DataNode。YARN这边,先启动ResourceManager,再启动NodeManager。如果顺序错了,比如先启动DataNode再启动NameNode,DataNode会不断重试连接NameNode,日志里全是连接被拒绝的报错,虽然最终能连上,但排查起来很烦人。
启动之后,用jps命令检查各节点进程是否齐全。NameNode节点应该有NameNode、ZKFC、JournalNode(如果复用);DataNode节点应该有DataNode和NodeManager;ResourceManager节点应该有ResourceManager。如果发现某个进程没起来,第一时间看日志,日志路径通常在$HADOOP_HOME/logs下,文件名包含进程名和用户名。
注意:如果集群开启了Kerberos认证,启动顺序和配置会更复杂,需要先确保KDC可用,并且所有节点的keytab文件正确分发。Kerberos的问题排查难度很高,建议先在非生产环境跑通再上生产。
4. YARN资源调度:让集群跑得更稳更快
4.1 ResourceManager与NodeManager:资源管理的双核心
YARN的核心思想是把资源管理和任务调度拆开。ResourceManager是全局资源管理者,负责整个集群的资源分配;NodeManager是单节点资源管理者,负责本节点上容器的生命周期管理。ApplicationMaster是每个应用的管理者,负责向ResourceManager申请资源,并与NodeManager通信启动容器。
这个架构的好处是扩展性好。ResourceManager只做资源分配,不关心具体任务执行,所以集群规模可以做得很大。NodeManager只管理本节点,压力分散。ApplicationMaster每个应用一个,故障只影响单个应用。
运维需要关注的是ResourceManager的HA配置。和NameNode HA类似,ResourceManager HA也是Active/Standby模式,通过ZooKeeper实现自动切换。配置项包括yarn.resourcemanager.ha.enabled设为true,yarn.resourcemanager.ha.rm-ids指定两个RM的ID,以及各自的地址。切换时,正在运行的应用不会中断,因为ApplicationMaster会重新向新的Active RM注册,但新提交的应用会短暂失败,需要客户端重试。
NodeManager这边,yarn.nodemanager.local-dirs指定容器本地工作目录,建议配置多个磁盘路径,避免单个磁盘IO瓶颈。yarn.nodemanager.log-dirs指定容器日志目录,同样建议多路径。yarn.nodemanager.resource.cpu-vcores指定可用的虚拟CPU核心数,默认8,需要根据物理CPU核心数设置,通常设为物理核心数的1到2倍。
4.2 调度器选型:容量调度器与公平调度器的取舍
YARN内置了三种调度器:FIFO、Capacity Scheduler和Fair Scheduler。FIFO基本没人用了,因为它是单队列先进先出,一个大型任务就能阻塞后面所有任务。生产环境主要用Capacity和Fair两种。
Capacity Scheduler的思路是预先划分队列,每个队列分配一定比例的集群资源。比如给“实时计算”队列分配40%资源,“离线分析”队列分配40%,“临时任务”队列分配20%。每个队列内部再用FIFO或FAIR策略调度。这种模式适合资源需求相对稳定的企业环境,能保证关键业务始终有资源可用。
Fair Scheduler的思路是动态分配,所有任务公平共享集群资源。如果一个任务单独运行,它可以占用全部资源;当有新任务提交时,系统会把资源重新分配,让每个任务都能获得大致相等的份额。这种模式适合任务数量波动大、资源需求不确定的场景。
我个人的经验是,如果公司有明确的业务线划分,且各业务线资源需求可预测,用Capacity Scheduler更稳妥。如果是一个公共计算平台,用户提交的任务五花八门,用Fair Scheduler更灵活。配置方面,Capacity Scheduler的capacity和maximum-capacity参数控制队列的保证资源和最大资源,Fair Scheduler的fair.share.preemption参数控制是否开启抢占,抢占能保证小任务不被大任务饿死,但会增加系统开销。
4.3 内存与CPU调度:避免容器被杀的配置技巧
YARN的内存调度是运维最容易踩坑的地方。每个容器申请的内存由yarn.scheduler.minimum-allocation-mb和yarn.scheduler.maximum-allocation-mb限定范围,实际分配值会向上取整到最小分配单位的整数倍。比如最小分配是1024MB,任务申请1500MB,实际会分配2048MB。这个取整逻辑会导致资源浪费,所以设置最小分配单位时要考虑业务任务的典型内存需求。
CPU调度方面,YARN默认只做内存调度,CPU核心数只是用来做资源隔离的参考。yarn.nodemanager.resource.cpu-vcores指定节点可用的虚拟核心数,yarn.scheduler.minimum-allocation-vcores和yarn.scheduler.maximum-allocation-vcores限定单个容器的核心数范围。默认情况下,YARN不强制限制容器实际使用的CPU,也就是说一个容器可以占用超过它申请的核心数。如果需要严格隔离,要开启yarn.nodemanager.resource.cpu-vcores的强制检查,并配合Linux的cgroups或者Docker容器做CPU限制。
容器被杀是运维常见问题,原因通常有三种:物理内存超限、虚拟内存超限、磁盘超限。物理内存超限是任务实际使用的内存超过了容器申请的内存,这个只能通过调大容器内存或者优化任务代码解决。虚拟内存超限前面说过,建议关闭虚拟内存检查。磁盘超限是容器本地工作目录写满,需要清理或者调大yarn.nodemanager.local-dirs所在磁盘的容量。
提示:排查容器被杀问题时,先看NodeManager日志中的“Container killed”关键字,后面通常会跟具体原因,比如“beyond physical memory limits”或者“beyond virtual memory limits”。根据原因对症下药,不要盲目调大所有内存参数。
4.4 队列配置实战:一个多业务共享集群的案例
假设我们有一个30节点的集群,总内存资源是30乘以64GB等于1920GB,需要支撑三条业务线:实时日志分析、离线报表生成、临时数据探索。实时日志分析要求低延迟,离线报表可以容忍较长等待,临时探索任务优先级最低。
我的队列规划是这样的:创建三个队列,分别命名为realtime、offline、adhoc。realtime队列分配40%的保证资源(768GB),最大资源80%(1536GB),这样在离线任务空闲时,实时任务可以借用更多资源。offline队列分配40%保证资源,最大资源80%。adhoc队列分配20%保证资源,最大资源40%。同时开启抢占,当realtime队列资源不足时,可以从adhoc队列抢占容器。
配置完成后,需要重启ResourceManager使队列生效。然后通过YARN的Web UI(默认端口8088)查看队列状态,确认资源分配符合预期。提交任务时,在命令行或者代码中指定队列名称,比如hadoop jar xxx.jar -D mapreduce.job.queuename=realtime。
这个配置方案运行了半年多,整体效果不错。实时任务基本没有因为资源不足而延迟,离线任务在业务低峰期能充分利用空闲资源,临时任务虽然优先级低,但也能在几分钟内拿到资源。唯一需要注意的是,抢占会导致被抢占的任务重新执行,所以adhoc队列的任务要设计成可重试的,避免因为抢占导致数据不一致。
5. 日常运维中的故障排查与性能调优
5.1 NameNode响应慢:从线程堆栈到JVM调优
NameNode响应慢是运维经常遇到的问题,表现是HDFS操作超时、客户端报“Call From xxx to xxx failed on connection exception”或者“Timed out after 30000ms”。排查思路分三层:网络层、NameNode进程层、JVM层。
网络层先看NameNode节点的网络流量和连接数,用netstat或者ss命令查看RPC端口(默认8020)的连接状态。如果大量连接处于TIME_WAIT或者CLOSE_WAIT,可能是客户端频繁创建连接没有复用,需要检查客户端的HDFS配置,开启连接池。
进程层看NameNode的RPC队列长度,通过JMX或者Web UI(默认端口50070)查看RpcQueueTimeAvgTime和CallQueueLength指标。如果队列长度持续大于0,说明RPC处理能力不足,需要调大dfs.namenode.handler.count。这个参数默认是10,对于超过100个DataNode的集群,建议调到100到200。计算公式可以参考:20 * log(number of DataNodes),比如100个DataNode,大约需要92个线程。
JVM层看垃圾回收日志。NameNode的堆内存建议至少8GB,大规模集群建议16GB到32GB。如果GC日志显示Full GC频繁(比如每小时超过一次),说明堆内存不足或者存在内存泄漏。可以用jmap和jstack分析堆转储和线程堆栈,定位是元数据过多还是某个操作导致对象无法回收。我处理过一个案例,NameNode每小时Full GC一次,每次持续十几秒,期间整个HDFS不可用。最后发现是某个客户端在疯狂创建小文件,导致NameNode元数据暴增。限制该客户端的写入速率并清理小文件后,GC频率恢复正常。
5.2 DataNode磁盘故障:在线更换与数据恢复
DataNode磁盘故障在规模化集群中是常态。一块16T的硬盘,在7乘24小时高负载写入下,平均两到三年就可能出现坏道。运维需要做的是快速发现、快速隔离、快速更换,把对集群的影响降到最低。
发现磁盘故障的途径有三个:DataNode日志中的“DiskErrorException”或“IOException”报错、HDFS Web UI中DataNode的卷故障状态、以及监控系统对磁盘SMART信息的告警。我建议在监控系统中配置磁盘SMART检测,提前发现亚健康磁盘,在彻底损坏前主动更换。
更换磁盘的流程要规范。首先,通过hdfs dfsadmin -report确认故障磁盘对应的DataNode和存储目录。然后,在DataNode节点上停止DataNode进程,卸载故障磁盘,更换新磁盘,格式化并挂载到原路径。接着,修改dfs.datanode.data.dir配置,移除故障磁盘路径(如果新磁盘挂载点不同),或者保持原路径不变。最后,启动DataNode进程,它会自动识别新磁盘并开始接收数据块。
数据恢复是自动的。DataNode启动后,会向NameNode汇报本节点的数据块列表。NameNode发现某些块的副本数低于设定值,就会触发复制任务,从其他副本所在的DataNode复制数据到新磁盘。复制速度取决于网络带宽和集群负载,通常几TB的数据需要几十分钟到几小时。期间集群整体可用,只是副本不足的块在复制完成前如果再次发生故障,就有丢失风险。所以更换磁盘要尽快,不要拖延。
注意:如果一次故障涉及多个磁盘或者多个节点,要评估数据丢失风险。如果某个块的三个副本全部丢失,NameNode会将该块标记为损坏,需要从备份恢复或者接受数据丢失。这也是为什么生产环境一定要有数据备份策略,HDFS的副本机制不能替代备份。
5.3 YARN任务排队:资源瓶颈定位与队列调整
YARN任务排队时间长,通常是因为集群资源不足或者队列配置不合理。排查的第一步是看ResourceManager的Web UI,确认是哪个队列在排队,以及集群整体资源使用率。
如果集群整体资源使用率已经超过80%,说明资源确实不够,需要考虑扩容或者优化任务资源申请。优化方向包括:合并小任务减少容器数量、调整任务内存申请避免过度分配、开启uber模式让小任务在ApplicationMaster内运行而不单独申请容器。uber模式通过mapreduce.job.ubertask.enable开启,适合输入数据量小、Map任务数量少的场景,能显著减少资源开销。
如果集群整体资源使用率不高,但某个队列排队严重,说明队列资源分配不合理。比如realtime队列只分配了20%资源,但实际业务量占了50%,那肯定排队。这时候需要调整队列的capacity和maximum-capacity参数。调整可以在线进行,通过yarn rmadmin -refreshQueues命令刷新队列配置,不需要重启ResourceManager。
还有一种情况是资源碎片化。集群总内存剩余很多,但都是零散的小块,无法满足一个大任务的内存需求。比如集群剩余10GB内存,但分散在10个节点上,每个节点只剩1GB,而任务需要2GB容器,那就无法调度。解决办法是开启资源预留,让大任务在某个节点上预留资源,等该节点上的小容器释放后,大任务再运行。资源预留通过yarn.scheduler.capacity.reservation-agent配置,能缓解碎片化问题,但会增加调度复杂度。
5.4 集群扩容:加节点不是插上网线那么简单
集群扩容看似简单,加机器、装系统、配环境、启动进程就行。但实际操作中有几个细节容易翻车。
第一是配置一致性。新节点的配置文件必须和现有集群完全一致,包括core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml,以及机架感知脚本、Kerberos keytab文件等。我建议用配置管理工具(如Ansible、SaltStack)统一分发配置,避免手工修改导致不一致。曾经有个案例,新节点的dfs.blocksize配置成了64MB,而集群其他节点是128MB,导致新节点上的数据块大小不一致,读取时出现校验错误。
第二是数据均衡。新节点加入后,HDFS不会自动把数据迁移过去,需要手动触发均衡。用hdfs balancer命令,可以指定阈值(比如10%),Balancer会把高利用率节点的数据块迁移到低利用率节点,直到各节点利用率差异小于阈值。Balancer运行时会占用网络带宽,建议在业务低峰期执行,并通过-D dfs.balancer.bandwidthPerSec限制带宽,避免影响正常业务。
第三是YARN资源更新。新节点的NodeManager启动后,ResourceManager会自动识别并纳入资源池。但如果你用了队列配置,需要确认新节点的资源是否被正确分配到各个队列。有时候因为队列的maximum-capacity限制,新节点的资源无法被充分利用,需要调整队列配置。
第四是监控和告警。新节点必须纳入现有监控体系,包括主机监控(CPU、内存、磁盘、网络)、进程监控(DataNode、NodeManager是否存活)、日志监控(是否有异常报错)。我见过新节点上线后没配监控,结果磁盘写满了没人知道,直到影响业务才被发现。
6. 写在最后:一些踩坑换来的经验
Hadoop集群运维这件事,说难不难,说简单也不简单。核心组件就那么几个,配置文件也就那么些参数,但真正把集群跑稳、跑快,需要的是对业务场景的理解和对细节的把控。
我最大的体会是:不要等到出问题才去看日志。日常巡检比故障排查重要得多。每天花十分钟看看NameNode的GC情况、YARN的队列积压、DataNode的磁盘使用率,能提前发现80%的潜在问题。我习惯在每天上班第一件事就是打开HDFS和YARN的Web UI,扫一眼关键指标,有异常就顺手处理掉,避免小问题拖成大故障。
另一个经验是:配置变更一定要记录。Hadoop的参数太多,今天调一个,明天改一个,过两个月自己都忘了改过什么。我建议维护一个配置变更日志,记录每次修改的参数、原因、时间和操作人。这样出问题时能快速回溯,也能避免多人协作时的配置冲突。
最后说一个容易被忽视的点:Hadoop的版本升级要谨慎。社区版本迭代快,但企业环境稳定优先。升级前一定要在测试环境充分验证,确认新版本对现有业务无影响,并且准备好回滚方案。我见过因为升级Hadoop版本导致HDFS API不兼容,所有上层应用全部报错的案例,回滚花了整整一个晚上。升级不是不可以,但要有计划、有测试、有退路。