作者:徐凯
导读:企业IT部徐凯有着丰富的 Ceph 应用经验,曾荣获《2016 Ceph 中国社区十大贡献者》称号。本文将其在2017年7月24日在北京举办的 China Openstack Days 上分享的内容进行整理,内容主要介绍了虚拟机的 IO 流程分析方法,几个特殊的情形下的 IO 分析,以及最后根据 IO 分析得到的结果进行了 IO 优化的建议。
现在市场上,很多私有云的解决方案都是基于开源的 Openstack + Ceph 架构来做的,而我们的虚拟机通常会使用 qemu-kvm 来运行。虚拟机的资源一般都是存储在 Ceph 里面的,借助Ceph的块存储功能,通过调用 librbd 库向 Ceph 读写数据。
1、虚拟机 = 黑盒子 ?
对我们普通用户来说,一台台虚拟机就像一个个黑盒子,为什么这么说呢?因为我们知道虚拟机的数据都存储在 Ceph 集群中,并且也会不断地向 Ceph 集群发送读写请求。但是,我们并未深入地去剖析过一台虚拟机向 Ceph 集群发送的每一个读写请求是做什么的。虚拟机已经将这层请求封装好,那么现在,我们就要撕开这层包装,看个究竟!
2、为何 Ceph 集群总是有着稳定的高 ops
在集群搭建好投入生产一段时间后,随着业务的增长,虚拟机的数量会不断增加,整个 Ceph 集群 ops 也会从当初的几十逐渐增加到后面的几千。
但是有个奇怪的现象,不论是在白天(业务高峰期)还是夜晚(业务低谷期),Ceph 集群始终都会有稳定的四五千的ops,难道说,虚拟机产生的 IO 和实际的业务没有正比关系吗?
又或者,我们曾经咨询过某些高 IO 的虚拟机用户,据他们反馈,他们并没有在使用这些虚拟机,此时的虚拟机是处于静置状态,难道说没有任何人为操作的虚拟机也会产生很高的 IO 吗?
3、虚拟机的启动风暴
众所周知,在集群搭建完后,通常会做许多压力测试,比如,批量虚拟机的建立,启动,重启等操作。而当批量启动上百台虚拟机的时候,很容易造成瞬间的大压力,导致后端存储无法承受之。
这种瞬间压力,我们一般叫做虚拟机的启动风暴。可是,知道了虚拟机批量启动会带来风暴,那这场风暴里的每一个 IO 都是做什么用的呢?这在本文后面会对得到解答。
4、虚拟机的读写流程简析
本文的环境使用的是常见的 qemu-kvm 用来启动虚拟机。虚拟机使用的是 Ceph 中的 RBD 即块设备。通过调用 Ceph 提供的库 librbd 来向 Ceph 集群发送 IO。
在阅读完 librbd 的源码后,我发现每一个读写 IO 都会调用名为 aio_write 和 aio_read 的函数,而这些函数在运行时,会输出一行很有特征的日志文件。也就是说,每发生一个读写 IO,日志文件中都会记录下这个 IO 的具体信息,包括这个 IO 产生的时间,IO在整个块设备中的漂移量,IO 的大小。所以通过分析客户端的日志,就可以得到每一个和 Ceph 集群交互的 IO。
这里需要注意的一点是,本次关于 IO 的讨论,仅仅是从 librbd 层去分析,而这些 IO 经过 rbd_cache 之后以及经过 OSD 的 journal 而产生的合并效应,不在我们的讨论范围内。
5、librbd 的日志特征
如上图所示,通过过滤日志中的关键字: aio_write , aio_read , 可以得到上图中的 IO 记录日志。其中 off 代表这个 IO 在整个 RBD 中的偏移量,单位为字节。 len 代表这个 IO 的大小, 8192 也就是一个 8 kB的 IO。 同理,aio_read 的日志中最后的方括号内的两个数字分别代表了一个读请求的偏移量和IO大小,单位均为字节。
6、如何获取 librbd 的日志呢?
这里,我们推荐采用打开 socket 文件的方式,而不是简单的增加日志级别来获取。原因很简单,每一个 kvm 进程都会在虚拟机启动的时候加载 Ceph 的配置文件,并且在进程启动之后,无法再修改配置项了,除非你硬重启虚拟机重新加载,或者使用 socket 文件对绝大多数参数进行动态的修改。
另外一个要说的是,记录 aio_write ,aio_read 的日志等级很高,要开到20才能输出,而当日志等级开到20之后,日志的生成速度很快,大约一小时几个GB的样子,如果长时间打开很容易把服务器写满,通过 socket 的方式动态的开启和关闭日志,可以达到较为不错的使用体验。
7、块设备和日志的对应关系
在获取到了日志之后呢,我们可以将日志得到的偏移量加IO大小,和整个块设备对应起来,因为这个偏移量就是这个 IO 在整个 RBD 里面的偏移量,通过将虚拟机对应的 RBD MAP 到本地,然后将这个偏移量的数据读取出来,对读取的内容进行分析,就可以知道这个 IO 的功能了。
实际上,块设备本身是不支持多路径挂载写的,但是由于这里使用场景的特殊性,即尽管这个块设备已经被虚拟机实时调用,但是我们将其 MAP 到本地,不会对这个块进行任何写入操作,仅仅是读取操作,所以不会造成因为多路径同时写而产生的数据不一致问题。
8、一个有趣的 IO
在这次 IO 分析中,我一共采样了几千个 IO 的内容并对其进行分析。而在所有的 IO 之中,有一个十分有趣的 IO,它的偏移量为 15302053888, IO 的大小为 12kB,而它的内容的就是上面我们熟悉的开机界面,也就是说 CentOS在启动的时候会在一个固定的 12kB 的位置,将启动界面的内容写入到里面。
9、新建虚拟机静置时IO分析
现在,创建一台崭新的虚拟机,开机一段时间后,将该虚拟机静置在那,不执行任何指令,采样一小时的日志,并对 IO 进行分析。可以发现,有一个 crond 进程,每过10min会生成对应的日志文件,并记录到 /var/log/cron 和 autit.log 文件中。
同时,XFS相关的IO的产生时间和 crond 日志生成时间十分吻合,所以这里我进行了一个猜测,是不是生成日志文件会产生较大的 XFS 层的IO。于是将 crond 进程关闭,又采样了一小时的日志进行分析。
观察得到,除了不再生成相关日志外, XFS 层生成的 IO 也少了许多许多,说明 crond 定时写日志的操作会带来 XFS 层的连带写操作,产生一定的放大效应,放大比在1:2~1:10左右。
并且还有一个比较有趣的现象,在关闭 crond 的 20min后,不再有任何写 IO 请求,说明对于新建的虚拟机来说,crond 是唯一会带来 Ceph 集群 IO 负载的进程,其他所有进程在系统静置时都不会给 Ceph 集群带来 IO。
10、生成单文件的IO分析
这里,我简单执行了 echo 123456 > /root/testfile 这个写文件的操作。在生成的所有的15个 IO 中,只有一个是包含了实际文件内容的 IO 请求,也就是执行 echo 操作的写 IO。 其他的14个都是 XFS 文件系统相关的写 IO,说明写文件会带来很多 XFS 层的IO消耗。
在对更多的写操作进行统计后,发现写文件操作越频繁,XFS 的消耗越少,但是总体上保持着 1:2 ~ 1:10 的连带消耗。
11、引发的一些思考
之所以分析这些 IO,是为了给我们生产实践提供一些帮助,目前一个比较急需的值是:每种类型的虚拟机大概产生多少的 iops,比如常用的两种类型: 网站访问的虚拟机,跑数据的虚拟机。这两种虚拟机产生的 IO 请求往往是不一样的。 网站访问,可能 IO 会集中在某天的某个时间点,比如门户网站早上八点开启注册抢号功能。所以八点那会会产生一个小的峰值。而跑数据库的虚拟机可能会带来稳定的密集的小IO操作,所产生的iops要比网站的要高很多。
因此,我们可以对生产环境的虚拟机进行 IO 监测,当然还是建议使用测试环境观察稳妥些,对一段时间内的不同类型的虚拟机进行建模,获取到每种类型的虚拟机的 IO 模型,然后将不同的模型进行叠加,可以得到集群所有虚拟机 IO 叠加图形,只要保证叠加峰值不超过集群的总承载能力,就可以继续新增虚拟机,卖更多的服务。
由前面的分析可以看到, XFS 层会因为写文件操作,而产生大量的放大写操作,我们甚至可以大胆的从文件系统层面,根据这里的放大效应,对文件系统的写操作进行合并优化,使得放大比降低到1:2 甚至 1:1 以下。
总结:经过对多种情形的虚拟机的IO分析,我们可以大致的对虚拟机的IO有一个直观的认识,后面会对这些 IO 数据进行建模,将虚拟机的IO动态的展现出来。