云盘不是硬盘,所售即所得

一般来说,我们可以把排查问题的技术分成三层。底下是理论知识,上边是问题模式【注释1】,夹在理论知识和问题模式之间的是工具。而贯穿排查技术的是“用数据说话”。今天这篇文章,跟大家分享一例我与客户"用数据说话"的故事。

“您有问题截图吗?”,“没有,但我能背出来。”

客户直接通过钉钉联系我:“有一个奇葩问题要你看了”。通过简单的沟通,我了解到,客户的业务程序使用一个200G的云盘做数据盘,晚上11点30的时候,业务程序由于不能写入数据而报错。这里关键的问题在于,客户声称当时磁盘并没有用光。

因为踩过不少坑,所以就算是熟客,我还是很专(jing)业(ti)地问了一句,“您有当时问题现场的截图吗?”。客户的回答让我哭笑不得:“没有,但是我能背出来”。接着呢,客户可能觉得口说无凭,就画了下边这张图给我。这里我想说的是,我们平时在跟客户处理问题的时候,总会听到客户各种各样的描述,但是最关键的沟通,还是数据沟通,比如一个报错,或者一个性能截图。然而,客户画一个性能数据给我做,这真的是第一次。

云盘不是硬盘,所售即所得

从这张手写的使用情况的截图来看,文件系统vdb1共有197G的可用空间,其中被用掉的空间是186G。这里让人难以理解的地方在于,空间使用率居然已经是100%了。

云盘不是硬盘

要想给客户解释这些“不合理”的数字,我们需要从磁盘的最基本单位说起。不管是云盘,还是传统的硬盘,都是以块为最小单位,进行数据存取的。这一点类似于我们平时读书:数只能一页一页的读,我们只要读到某一页,就能看到一整页的文字。我们没有办法只打开某一行,或者某一个字去读书。一般而言,磁盘块的大小是固定的512个字节。那么,客户购买的200G的云盘,到底是多少个字节呢?

之所以有这个提问,是因为传统的硬件厂商在售卖硬盘的时候,使用的换算单位是1000。也就是说,200G的硬盘应该是200,000,000,000个字节。那么我们卖给客户的200G的云盘,也是这么大吗?其实阿里云在售卖云盘的时候,使用的换算单位是并不是传统的1000,而是1024。有一个简单的办法可以验证这件事情。我们可以读/sys/block/vdb/size文件,得到vdb这个云盘的总块数。以200G云盘为例,我们读到的块数是419430400,每块512字节,那么200G的云盘,实际上是214748364800个字节。【注释2】

云盘不是硬盘,所售即所得

分区管理成本

了解了磁盘的最基本单位之后,我们眼中的磁盘,差不多就变成下边这个样子了:一块磁盘由若干个512字节的块组成。

云盘不是硬盘,所售即所得

然而,习惯上我们不会直接的,从第一块到最后一块这样去使用磁盘。我们要先对磁盘进行分区。客户的200G数据盘只有一个分区,所以分区之后大概是下边这样的。前边的黑色块是分区管理成本。这部分应该首先从可用空间中减掉。客户的数据盘中,分区管理成本是1M。

云盘不是硬盘,所售即所得

 文件系统管理成本

分区之后,我们需要在分区之上,创建文件系统。文件系统是我们可以直接使用的存取数据的方式。在分区之上创建文件系统,有点类似于我们拿到一个空白的笔记本,对笔记本的使用做一些简单的规划,其中一部分用来写日记,一部分做计算等,然后根据不同的用途,编写一个目录。这里的“目录”,是文件系统的管理成本。这部分内容,也是需要从可用空间中减掉的。对于Linux文件系统来说,这部分开销最主要是inode结构。如下图,vdb1这个文件系统一共有13107200个inode,每个inode256个字节。那么这部分成本是不能忽略的一部分成本:3.125G。【注释3】

云盘不是硬盘,所售即所得

问题重现了一半

减掉分区成本和文件系统成本,200G的云盘,可用的空间就剩下197G了。这与Size的值相匹配。但这显然不能解释,为什么只用掉186G,空间使用率就到100%了。在百思不得其解之后,我决定自己动手试试,看能不能跑出客户看到的问题。我申请了一个云服务器,挂载了一个200G的云盘,然后连续创建1G大小的空白文件。在跑了大概30分钟以后,我看到下边的结果。结果证明,客户当时应该是真的看到了他手写的那个数据。

云盘不是硬盘,所售即所得

但是,当我尝试继续往磁盘里写空白文件的时候,我并没有看到客户所说的,磁盘空间不足的报错,直到197G被全部用完为止。

云盘不是硬盘,所售即所得

工具df不生产数据

所有的秘密都藏在工具df里。工具df可以用来查看磁盘的容量以及剩余空间,但是就像Linux上很多其他工具一样,df并不会凭空的产生数据。df命令使用系统调用从内核拿到数据,然后加工并把相对可读的信息提供给用户。研究df的行为,我们可以使用strace。用strace追踪df的执行过程,我们发现与磁盘容量相关的数据均来自于系统调用statfs。

下边这条记录,是在重现环境中抓的。我们可以看到,statfs以文件系统挂载路劲为输入,输出一个包含此文件系统统计信息的结构体。

statfs("/mnt", {f_type=EXT2_SUPER_MAGIC, f_bsize=4096, f_blocks=51572986, f_bfree=2557603, f_bavail=0, f_files=13107200, f_ffree=13107176, f_fsid={3186720966, 3846989262}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_RELATIME}) = 0

在这个结构体中,f_bsize记录的是文件系统的块大小,这个和磁盘块大小是类似的概念。可以看到文件系统的块大小是4K,也就是说,1个文件系统块,需要使用8个磁盘块。f_blocks是文件系统总块数,df输出的文件系统的Size就是从这个值计算而来。

而跟这个“奇葩”问题相关的,是f_bfree和f_bavail这两个值。这两个值从命名来看都可以解释为可用空间,但意义却大不一样。根据定义,f_bfree是文件系统的可用块数,这个值是df输出的Size和Used的差值。而f_bavail,是“非特权”用户的可用块。Linux在创建文件系统的时候,会预留一部分磁盘空间,专门给root使用,默认情况下,这部分空间是磁盘空间的5%。f_bavail记录的是,从非特权用户视角看,磁盘的可用空间。

明白了上边几个数据项的含义之后,这个问题就变得很清楚了。之所以Used才到186G的时候,空间使用率就已经到了100%,是因为df计算使用率,用的是f_bavail,而不是f_bfree。换句话说,df使用的是非特权用户的视角。因为客户业务进程使用的并不是root账户,所以业务进程在使用到186G的时候报出磁盘空间不足的错误就不足为奇了。

大图

最后我们来看一下大图。200G的云盘,分区和文件系统,会用掉3G的管理成本。剩下197G就是df输出的Size值,也就是真正的可用磁盘空间。因为文件系统会预留5%的空间给特权用户使用,这大概是10G,所以对非特权用户来说,可用空间需要再减掉10G,所以大概就剩187G。

云盘不是硬盘,所售即所得

后记

在排查问题的过程中,我们必然会用到各种各样的数据和指标,毫不夸张地说,我们分析问题,解决问题的过程,就是收集数据,研究数据,以及使用数据和客户沟通的过程。而基础排查和进阶排查的一个最很重要的区别,就是我们能不能从数据本身出发,一层一层深入到数据产生的源头当中去,深入到加工数据的代码当中去,理解数据产生的原因。以今天这个问题为例,当我们弄清楚磁盘容量这些指标的计算方法之后,这个问题就变得非常的简单,但是,如果我们不能这样一步一步的深入去探究,那么这个问题就是显得奇葩而不可解释。

 

  • 注释1:问题模式这个概念和设计模式类似。设计模式是最佳实践,是软件开发领域,典型问题和经典解法的总结。而问题模式,也是类似的概念。当我们处理过大量问题之后,会自然而然的遇到一些典型的重复的场景,当然也会使用一些重复的处理方法。

  • 注释2:云盘和硬盘换算单位的差别,严格来说,是GiB和GB的差别。云盘使用的是GiB,而硬盘使用的是GB。但是使用中我们比较少对这两者做区分。

  • 注释3:文件系统还有其他一些管理开销,比如superblock等,因为占比很小,这里忽略掉了。

上一篇:阿里云&数数科技联合打造新一代游戏数据分析系统正式上线


下一篇:分布式实时日志分析解决方案ELK部署架构