背景:老集群上千节点,存储60PB数据,因为历史原因,节点配置不一;为了节省成本最早一批节点挂载了32块盘,单节点磁盘空间250TB左右,负载在150TB左右,存储block数240万块左右,集群带宽限制480GB/s,标准节点配置是12*8T,当前存储数据50-60TB,block 80万左右。
1.如何避免因节点掉线或退役产生网络风暴?
1.1区分节点掉线和节点退役的区别?
节点退役:首先正常将节点加入退役名单,先告诉namenode和yarn不要在往上提交新任务和写入数据了;然后等待节点上的数据块在集群中复制完成;这个时候该退役的节点是优先作为srcNode数据源的(优先选择退役中的节点作为复制数据源src,因为其无写入请求,负载低),其他节点从该退役节点复制数据块到其他节点的,所以这个时候该节点的负载会很高。所有数据块复制完毕,节点状态变成Decommissioned,可以从namenodeURL界面查看。注意数据节点退役数据开始复制的时间也是10分30s后,并不是因为是主动退役而提前,因为nannode和datanode永远都是维持着简单的主从关系,namenode节点不会主动向datanode节点发起任何IPC调用,datanode节点需要配合namenode完成的所有操作都是通过两者心跳应答时携带的DatanodeCommand返回的。
节点掉线:比如强制停止datanode,物理机挂了(比如负载高掉线,突发网络故障,硬件故障等),这些都属于节点掉线,一般默认10分30s后(主要受两个参数控制)namenode会检测到该节点通信异常掉线。然后namenode根据该节点的ip,查出该节点所有的blockid,以及对应副本所在机器,通过心跳机制安排数据复制,这时候数据的复制,数据源不在是掉线节点,而是多个副本之一所在的节点,同样这时候副本复制也遵循机架感知,副本搁置策略。
尖叫提示:节点掉线和退役两者的区别不仅是数据的复制方式不同,还有就是namenode对Under-Replicated Blocks 的数据复制策略也是不一样的(数据块block复制的等级分成5种);极端的例子比如单副本节点退役数据不会丢失,单副本节点掉线则会数据真的丢失;
1.2 如何处理节点掉线出现的各种风暴
几十T,甚至上百T,上百万block的节点掉线,会出现大量的RPC风暴,尤其对于大规模高负载集群来说对namenode是很大的挑战,不仅影响生产性能,也会存在很大的隐患,尤其是对于带宽有限制瓶颈的集群。一般来说namenode检测datanode是否掉线的值是10*3s(心跳时间)+2*5min(namenode检测时间,参数是:dfs.namenode.heartbeat.recheck-interval)=10分30s。如果在10min30s内的时间内带宽持续打满,RPC请求延迟,datanode和namenode节点通信不畅,很容易造成其他节点的持续掉线,形成恶性循环,这种情况应该如何避免?
1.21 先了解下datanode的block复制问题:
NameNode 维护一个复制优先级队列,对于副本不足的文件 block 按优先级排序,仅剩下一个副本的文件 block 享有最高的复制优先级。所以从这里看集群两副本的话,只要有一个block出现异常,就只剩一个副本,就是最高优先级的块复制,会风暴模式复制,控制不好很容易影响集群性能,甚至搞挂集群。所以一般不建议集群副本因子是2。
如下block待复制的5个优先级队列:其实就在UnderReplicatedBlocks的私有方法getPriority张中,长这样:
- L1(最高):有数据丢失风险的块,如:1.只有一个副本的块(尤其对于2副本的块,下线一台节点)或者这些块有0个active副本;2,单副本在正在退役的节点拥有的块。
- L2:block副本实际值远低于配置的值(比如3副本,缺失2个),即副本数不到期望值1/3时的块,这些block会被第二优先级复制。比如4副本的block,有3个丢失或者坏了,它就会比4副本block丢失2个的优先复制。
- L3:副本不足没有优先级L2高的那些副本,优先复制。第三优先级。
- L4:block满足要求的最小副本数。副本度需求度比L2-L3都低。
- L5已损坏的块,并且当前有可用的非损坏副本
1.2.2 控制节点掉线RPC风暴的参数
三个参数都是hdfs-site.xml中参数,具体可以参考apache hadoop官网,其实块的复制速度有两个方面决定,一是namenode分发任务的速度,二则是datanode之间进行复制的速度。前者可以理解成入口,后者可以当成出口。
1.入口参数:从namenode层面控制任务分发,这个参数修改必须重启namenode,不需要重启datanode.
dfs.namenode.replication.work.multiplier.per.iteration
这个参数apache hadoop默认值2,cdh集群默认值10
这个参数决定了当NN与DN进行心跳(3s)发送任务列表时,告诉每个DN可以进行复制的block数量。比如集群有500个节点,这个值设置为10,那么一次心跳namnode可以发送datanode复制的数据块数量是10*500=5000块。假如一个节点掉线/退役有800000块block需要复制,则namenode需要多长时间可以将待复制块的任务分发完给datanode呢。
极限计算的结果:
任务分发时间=待复制block总数/(集群活跃dn*参数值)*心跳时间
time=800000/(500*10)=160次心跳*3s/每次心跳=480s=8分钟
所以节点越多,会分发任务越快,分发速度跟节点数和这个参数都成正比
2.出口参数:相比上面从nanode任务分发控制,下面两个使用datanode层面控制,这两个参数也需要重启namenode
1.dfs.namenode.replication.max-streams
apache hadoop默认值是2,cdh集群默认20。
这个参数含义是控制datanode节点进行数据复制的最大线程数,从上面我们知道block的复制优先级分成5种。这个参数控制不包含最高优先级的块复制。即除最高优先级的复制流限制
2.dfs.namenode.replication.max-streams-hard-limit
这个值apache hadoop默认值2,cdh集群默认值40
这个参数含义是控制datanode所有优先级块复制的流个数,包含最高优先级;一般上面和上面两个参数互相的配合使用。
尖叫总结:前者参数控制datanode接受任务的频率,后者这两个参数进一步限制 DataNode 一次完成的最大并行线程网络传输量。具体上面参数的值设定的多少,取决于集群的规模和集群的配置,不能同一而论。一般来说从入口控制比较简单容易些。比如规模500台集群,dfs.namenode.replication.work.multiplier.per.iteration=10,那么集群一次心跳分发5000个block的量,假如集群文件存储全部打散在500台节点,每个节点同时复制10个block(实际会因为副本搁置策略,机架感知等并不会所有的节点都参与数据复制),每个block大小128Mb,则每个节点的网络负载是128*10/3=546Mb/s,那这时候你就要看下结合实际会不会有带宽瓶颈,这么大的网络IO会不会影响正常任务的计算,如果有的话,这个值就要调小点。
2.如何快速节点下线
如何让节点快速下线的本质其实就是提高副本的复制速度。主要还是上面三个参数控制.第一是控制namenode任务分发,其次控制datanode复制速率,前提是不影响正常生产任务的进行。集群规模越小,下线的越慢,比如因为分发的总数会慢很多。
比如一个500台的节点dfs.namenode.replication.work.multiplier.per.iteration=10,一个50台的节点这个值要设置成100,两者namenode任务分发的速度才可以一致。具体结合实际集群规模设置。
以实际经验使用值来看,80万block的节点,50T左右的数据,650台规模dn集群,数据复制时间大概也就十几分钟,整个节点下线的时间也就半小时吧,已经很快了,同时也没影响正常任务的调度,没有达到集群带宽的限制瓶颈,只使用带宽的总量的75%,平稳下线,又不影响任务。
dfs.namenode.replication.work.multiplier.per.iteration=4
dfs.namenode.replication.max-streams=20
dfs.namenode.replication.max-streams-hard-limit=40