【背景】
回家路上,接到运维兄弟的电话,说一线上环境,某个DN异常了,原因是有个磁盘写满了,他准备将这个盘剔除出去,重启下DN,问我数据会不会丢失。
我说数据不会丢,我们的数据都是多副本的。
这位兄弟再三确认不会丢数据后,挂了电话就去操作了。
可我回头一想,磁盘异常导致DN异常倒是碰到过,磁盘写满也能导致DN异常?
带着疑问做了一番梳理,接下来我们就来聊聊。
【DN运行过程中磁盘写满会怎样】
首先,DN运行时,单块磁盘写满,是否会引发问题?
答案是不会的,DN内部处理写block逻辑时,会过滤可用空间不满足条件的磁盘(准确的说法应该是卷目录,但一般而言DN会配置多个卷目录,不同的目录位于不同的磁盘上,为了方面理解,后面都先称为磁盘)。
例如:先将某个磁盘写满(如下图所示)
此后,继续向hdfs写入文件,发现写入的文件都正常,已经写满的磁盘不会继续存储新的数据
【DN写block时的磁盘分配策略】
DN中磁盘分配有两种策略:轮询分配和按可用容量分配。
轮询分配
顾名思义,就是从配置的磁盘中,轮流选择一个作为block的存储位置。
当然,选择的磁盘的时候,会进行一次容量的判断,如果不满足条件,则跳过该磁盘。
对应源码为RoundRobinVolumeChoosingPolicy类的chooseVolume函数中。关键代码如下所示:
while (true) {
final V volume = volumes.get(curVolume);
curVolume = (curVolume + 1) % volumes.size();
long availableVolumeSize = volume.getAvailable();
if (availableVolumeSize > blockSize) {
return volume;
}
if (availableVolumeSize > maxAvailable) {
maxAvailable = availableVolumeSize;
}
if (curVolume == startVolume) {
throw new DiskOutOfSpaceException("Out of space: "
+ "The volume with the most available space (=" + maxAvailable
+ " B) is less than the block size (=" + blockSize + " B).");
}
}
按可用容量分配
该策略先按照一定的计算方式将所有磁盘划分为高可用容量和低可用容量两部分(先计算所有磁盘中的最小可用容量Min,对于可用容量小于等于Min加指定容量大小的磁盘就属于低可用容量)。
然后根据block数据的大小,从中选择不同的部分,最后再轮询选择一个具体的磁盘作为最终存储block的路径。
相比轮询策略而言,该策略有了一定优化,能一定程度保证磁盘间存储空间的平衡。
核心代码在AvailableSpaceVolumeChoosingPolicy类中(篇幅原因,就不全部贴出来了,可自行查看)
【磁盘写满可能存在的潜在问题】
既然单个盘写满不会引发什么问题,那是不是意味着没什么隐患,完全不用管了呢?
显然不是这样的,磁盘写满可能存在一些潜在问题。
例如,如果dn直接使用系统盘(根目录所在盘)作为数据存储路径,那么系统盘写满,可能导致部分命令无法执行,从而引发一些问题。
如果NodeManager和Datanode运行在一个结点上,并且配置了相同的磁盘目录,那么单个磁盘写满,可能导致NodeManager处于unhealth状态,从而引起该结点无法运行yarn任务(原因可以从这篇文章中找答案)。
【处理和规避方法】
对于磁盘写满,我们该如何处理呢?
在Hadoop3.0版本之前,我们只能停止DN,然后将一部分block数据从写满的磁盘目录移动到另外一个磁盘的相同目录下,最后再启动DN完成数据加载。
例如,如下图所示,两个目录均有数据
将一个目录的block数据移动到另外一个目录下(两个目录在不同的磁盘上),这样DN重新启动后,其存储的block数据仍旧不变。
这里有几个注意事项:
hadoop3.0之后的版本,自带提供了disk balance 命令行工具,可以对dn磁盘进行均衡处理,这里不展开介绍,有兴趣的可以看看官网文档。
上面讲了磁盘已经写满要如何处理,那么更好的一种方式是事前进行规避。
一种简单有效的方式是,通过对磁盘空间进行一定预留,确保不会写满,hadoop也确实提供了相应功能的配置:
dfs.datanode.du.reserved
该配置作用于每个存储block的磁盘,进行一定空间的预留。
【总结】
回过头,再看运维兄弟给我反馈的问题,说磁盘写满导致DN异常,应该是不会出现这种情况的。
第二天重新找该兄弟进行了确认,确认了是磁盘故障,而不是磁盘写满导致。