# 05 | 云硬盘:云上IO到底给不给力? 你好,我是何恺铎。 通过前几讲的学习,我想你对云虚拟机应该有了不少的了解,也对如何根据实际情况来选择和运用虚拟机,有了一定的认识。在前面的学习过程中,我也留下了许多伏笔。其中之一,就是云虚拟机的重要组件:**云硬盘**。 那么今天这一讲,我们就来深入讨论一下这个话题,来帮助你了解不同云硬盘的差别,以及如何在实际场景中挑选最合适你的硬盘型号。 ## 云硬盘是什么? 云硬盘,又叫做“云盘”或者“云磁盘”,就是云虚拟机上可以挂载和使用的硬盘。这里,它既包含了用于承载操作系统的系统盘,也包括了承载数据的数据盘。 在云计算的领域,有时,我们还会把云端磁盘服务叫做块存储(Block Storage),因为它们与Linux操作系统中的块设备相对应,是云上提供的“裸盘”,可以格式化并且施加文件系统。 既然是硬盘,那么它就与我们通常的认知相一致,当然是带有数据持久化功能的。这在专业上被称为“**非易失性存储**”(Non-ephemeral Storage),也就是说**写入的数据不会丢失**。即便所在虚拟机重启、关机甚至下线删除,这块云硬盘只要还存在,其中的数据也并不会被擦除。 事实上,云厂商对于云盘,不仅仅会保障数据的顺利写入,一般还会帮你在存储端同步和保留至少三份副本的数据。所以说,云硬盘的冗余度和可用性是非常之高的,一般极少发生云硬盘数据丢失的情况,你大可放心地使用。 重要提示:尽管云硬盘有良好的存储冗余,但你不能仅仅依赖它的可靠性。从数据的层面来看,你必须进行额外的备份。2018年7月曾有创业公司因为云厂商故障,丢失了在云硬盘上的所有重要数据,一时成为业界的热点新闻,这个教训是非常深刻的。所以,你应当通过定期为云磁盘创建快照、异地备份数据文件等方式,来保护你的关键数据。 云硬盘与传统磁盘的真正差异在于,绝大多数的云硬盘都是**远程**的。我们都知道,在经典计算机的体系结构中,硬盘是通过本地机器内部主板的高速总线,与CPU、内存等部件相连接;而在云端,你的硬盘则很可能并不在宿主机上,而是在专用的磁盘服务器阵列中,**两者是通过数据中心内部的特有IO线路进行连接**。没错,这也正是**计算存储分离架构**的一种体现。 理解了这样的一个结构,你就能明白,有些云上的“IO优化实例”(AWS上称为EBS-Optimized)是指什么了。它就是指云虚拟机与云硬盘之间的网络传输,进行了软硬件层面的优化,这样可以充分地发挥所挂载磁盘的性能。现在较新型号、较强性能的云虚拟机,一般都自动启用了这个优化。 ## 云硬盘的性能等级 你可能听说过一些,网上对于云硬盘性能方面的质疑。这在云计算发展的早期尤其多见,甚至成为了很多人反对上云的主要原因之一。 不错,云硬盘的确有多副本写入的开销,同时也比较依赖于远程传输。所以,早期云硬盘的确存在一些性能上的短板。不过,那都是老黄历了。 当下的云硬盘经过了多次的软硬件迭代,尤其是SSD的迅速发展,吞吐量和随机读写能力等各项性能指标都已经不再是问题了。在现代云计算中,已经发展出了基于不同存储介质的、丰富的性能等级选择,你已经能够找到单盘IOPS在数十万量级甚至达到百万的云硬盘产品了。 所以,现在的云硬盘,性能上已经非常“给力”了。**你更多的是要考虑如何根据应用场景,选择合适介质的硬盘等级,同时权衡好相应的成本。** 那么下面,我们就分别来看一看主流云硬盘的不同性能等级,以及它们对应的磁盘类型和存储介质。 **第一个等级的云硬盘,是基于传统HDD硬盘构建而成的**。这类云盘的性能一般,最高IOPS大概在**数百**左右。在很多的云上,已经不把它作为推荐的选择了。但它并非一无是处,**成本低**就是它的最大优势,在不注重性能的测试环境,或者是个人自用的服务器,它就是一个很好的选择。 **第二个等级,往往是基于混合硬盘,也就是结合HDD和SSD硬盘构建的云硬盘**。它会综合发挥SSD的性能优势和HDD的容量优势。比如它可以用SSD部分来承载热点区域数据,或是作为缓存,来提高响应性能。在这个等级下,典型的IOPS为**数千**左右,是很多云上创建硬盘的**默认选项**,比较适合像是操作系统启动盘这样的常规负载。 **第三个等级的云硬盘,它的存储介质就是纯SSD硬盘了**。虽然贵一些,但一分价钱一分货,这个等级下的云硬盘能够提供非常稳定的IO能力,IOPS通常能够**上万**,也有相当不俗的吞吐量和较低的访问延时。你可以用它来承载生产环境中重要的关键业务应用,或是各类数据库等IO密集型应用。 **第四个等级,也是当下业界的最高等级,就是进一步优化增强的最新SSD云盘**。它一般会采用更新一代的企业级闪存硬件,配合自研或改进后的底层传输协议,和虚拟化技术栈的优化来提供服务。因此它能够达到惊人的性能水平,满足我们最为苛刻的性能场景需求,比如承载SAP HANA(SAP的高性能计算平台)、高并发OLTP数据库等等。这类SSD云盘的IOPS通常能够**突破十万以上**。 各个云对于不同等级云硬盘的命名方法各有不同,我把相应的产品类型和名称整理成了一个表格,方便你去了解和查询: ![](https://static001.geekbang.org/resource/image/97/13/97150c100ba7b7d25dd5750e1c01ad13.jpg) 当然,这个表格只是一个大致的划分,仅供你作为参考。在具体的情况中,云和云必然存在一些差异,也会有一些各自的产品特点,建议你在使用时针对性地确认。比如说,AWS的gp2通用型SSD类型,它具有比较宽广的性能指标范围,还具备I/O积分和性能突增机制(与性能突增VM实例的CPU类似),可以提供比较高的峰值性能,应用场景是相当广泛的。 除了云盘性能等级之外,**还有一个影响云盘性能的重要因素,就是这块云硬盘的容量**。不论是哪种磁盘类型,它的容量大小几乎都与性能正向相关。同等的性能等级下,云硬盘的容量越大,一般来说它的性能就越高,直到达到这个等级的上限。这是由云上磁盘能力共享的底层设计所决定的。 **所以在某些时候,你可能需要刻意地增大所申请的云硬盘的容量,以获取更高的性能,即便这些额外的空间不一定能被用上。** 好了,对于云盘性能的讨论就先到这里。在上面的性能讨论当中,我们主要通过**IOPS**来进行衡量。事实上,衡量IO性能还有吞吐量、访问延时等其他的重要指标。这些指标同样会由磁盘的类型和大小所决定,你可以查询云厂商文档来确认。这里限于篇幅,我就不详细展开了。 ## 云硬盘实战 接下来,让我们进入实战环节,一起学习一下云硬盘的使用。在这个过程中,你也能真实地感受一下不同性能等级的区别。 这里,可以继续沿用我们在[第2讲](https://time.geekbang.org/column/article/206296)中创建的阿里云虚拟机,目前它是默认挂载了一个**40G的高效云盘**作为系统盘。 **我们可以先用lsblk和df命令查看一下磁盘的情况:** ``` [root@my-ecs-vm1 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT vda 253:0 0 40G 0 disk └─vda1 253:1 0 40G 0 part / [root@my-ecs-vm1 ~]# df -hT -x tmpfs -x devtmpfs Filesystem Type Size Used Avail Use% Mounted on /dev/vda1 ext4 40G 1.6G 36G 5% / ``` 通过命令的输出,可以清晰地看到,这台机器有一块40G的系统盘,挂载在根目录下。 **然后,我们可以使用fio工具来测试一下这块系统盘的性能表现。**我们通过fio在系统盘上创建一个1GB的文件,接着进行4K大小的随机读取实验。 ``` [root@my-ecs-vm1 ~]# fio --name=mytest1 --filename=~/testfile1 --rw=randread --refill_buffers --bs=4k --size=1G -runtime=10 -direct=1 -iodepth=128 -ioengine=libaio mytest1: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=l ibaio, iodepth=128 fio-3.7 Starting 1 process mytest1: Laying out IO file (1 file / 1024MiB) Jobs: 1 (f=1): [r(1)][100.0%][r=8560KiB/s,w=0KiB/s][r=2140,w=0 IOPS][eta 00m:00s] mytest1: (groupid=0, jobs=1): err= 0: pid=1324: Sat Jan 25 17:03:53 2020 read: IOPS=2154, BW=8619KiB/s (8826kB/s)(84.9MiB/10090msec) slat (nsec): min=2529, max=38138, avg=3080.22, stdev=575.39 clat (usec): min=444, max=102701, avg=59394.84, stdev=46276.36 lat (usec): min=448, max=102705, avg=59398.39, stdev=46276.34 clat percentiles (msec): | 1.00th=[ 3], 5.00th=[ 3], 10.00th=[ 4], 20.00th=[ 4], | 30.00th=[ 4], 40.00th=[ 5], 50.00th=[ 96], 60.00th=[ 97], | 70.00th=[ 99], 80.00th=[ 99], 90.00th=[ 100], 95.00th=[ 100], | 99.00th=[ 101], 99.50th=[ 102], 99.90th=[ 102], 99.95th=[ 102], | 99.99th=[ 103] bw ( KiB/s): min= 8552, max=10280, per=100.00%, avg=8645.20, stdev=384.80, samples=20 iops : min= 2138, max= 2570, avg=2161.30, stdev=96.20, samples=20 lat (usec) : 500=0.01%, 1000=0.03% lat (msec) : 2=0.50%, 4=36.26%, 10=3.74%, 100=57.13%, 250=2.34% cpu : usr=0.50%, sys=1.19%, ctx=20986, majf=0, minf=161 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=99.7% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.1% issued rwts: total=21741,0,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=128 Run status group 0 (all jobs): READ: bw=8619KiB/s (8826kB/s), 8619KiB/s-8619KiB/s (8826kB/s-8826kB/s), io=84.9MiB (89.1MB ), run=10090-10090msec Disk stats (read/write): vda: ios=21399/2, merge=0/1, ticks=1266052/242, in_queue=1039418, util=81.1 ``` 实际命令输出的结果比较长,这里我们主要关注下IOPS的部分。你可以看到,平均IOPS的数值都在2100左右,这个跑分的成绩,和我们当初建立这块高效云盘时提示的性能目标值“2120”相当一致。 ![](https://static001.geekbang.org/resource/image/83/ec/8396f50d9acd70d7e4186dbeee124fec.jpg) 如果高效云盘还不够满足你的业务要求,你可以随时为机器添加更高规格的硬盘,这也是云硬盘的灵活性所在。 **接下来,我们就来试一下动态挂载新硬盘的过程。** 首先来到这个虚拟机的“本实例磁盘”管理界面,选择“创建云盘”,这里我们选择一块300G的SSD云盘,按照提示,这样我们就能够拥有1万的IOPS。 ![](https://static001.geekbang.org/resource/image/3f/6c/3fb419666c386c7582b17821bd3efc6c.jpg) 之后,按照提示确认创建即可。OK,阿里云很快地为我们创建好了磁盘,但此时这块SSD磁盘的状态为“未挂载”,我们可以通过界面操作,把它挂载到正在运行中的目标虚拟机里。 挂载完成后,磁盘的状态开始变为“使用中”,说明磁盘已经“上线”。这时,我们再在Linux操作系统中用lsblk命令查看: ``` [root@my-ecs-vm1 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT vda 253:0 0 40G 0 disk └─vda1 253:1 0 40G 0 part / vdb 253:16 0 300G 0 disk ``` 你可以看到,磁盘中已经出现了一个新的块设备vdb。 这时,我们需要将这块磁盘进行格式化,并创建ext4文件系统: ``` [root@my-ecs-vm1 ~]# mkfs.ext4 /dev/vdb mke2fs 1.42.9 (28-Dec-2013) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 19660800 inodes, 78643200 blocks 3932160 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=2227175424 2400 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000, 7962624, 11239424, 20480000, 23887872, 71663616 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done ``` 好,还差最后一步,我们要在/mnt下,创建一个data目录,并将这个新的块设备挂载到该目录。 ``` [root@my-ecs-vm1 ~]# mkdir /mnt/data [root@my-ecs-vm1 ~]# mount /dev/vdb /mnt/data/ ``` 终于大功告成。我们再次使用fio工具,来测试下这块SSD盘4K随机读方面的能力。**和前面不同的是**,这回我们要把测试文件路径定位到“/mnt/data”目录,因为这个目录指向的是刚刚创建的新硬盘: ``` [root@my-ecs-vm1 ~]# fio --name=mytest2 --filename=/mnt/data/testfile2 --rw=randread --refill_buffers --bs=4k --size=1G -runtime=10 -direct=1 -iodepth=128 -ioengine=libaio mytest2: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=l ibaio, iodepth=128 fio-3.7 Starting 1 process Jobs: 1 (f=1): [r(1)][100.0%][r=41.1MiB/s,w=0KiB/s][r=10.5k,w=0 IOPS][eta 00m:00s] mytest2: (groupid=0, jobs=1): err= 0: pid=1302: Sat Jan 25 16:59:30 2020 read: IOPS=10.6k, BW=41.2MiB/s (43.2MB/s)(415MiB/10067msec) slat (usec): min=2, max=445, avg= 3.10, stdev= 1.49 clat (usec): min=828, max=77219, avg=12115.14, stdev=20941.23 lat (usec): min=841, max=77222, avg=12118.74, stdev=20941.22 clat percentiles (usec): | 1.00th=[ 2737], 5.00th=[ 3326], 10.00th=[ 3523], 20.00th=[ 3687], | 30.00th=[ 3785], 40.00th=[ 3884], 50.00th=[ 3949], 60.00th=[ 4047], | 70.00th=[ 4146], 80.00th=[ 4359], 90.00th=[56361], 95.00th=[71828], | 99.00th=[73925], 99.50th=[73925], 99.90th=[74974], 99.95th=[76022], | 99.99th=[76022] bw ( KiB/s): min=41916, max=43600, per=100.00%, avg=42464.60, stdev=724.17, samples=20 iops : min=10479, max=10900, avg=10616.15, stdev=181.04, samples=20 lat (usec) : 1000=0.02% lat (msec) : 2=0.17%, 4=55.50%, 10=29.30%, 20=0.83%, 50=3.47% lat (msec) : 100=10.71% cpu : usr=3.24%, sys=5.58%, ctx=96090, majf=0, minf=163 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=99.9% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.1% issued rwts: total=106300,0,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=128 Run status group 0 (all jobs): READ: bw=41.2MiB/s (43.2MB/s), 41.2MiB/s-41.2MiB/s (43.2MB/s-43.2MB/s), io=415MiB (435MB), run=10067-10067msec Disk stats (read/write): vdb: ios=105123/3, merge=0/1, ticks=1265938/41, in_queue=1266532, util ``` 上面的测试结果表明,这块SSD盘对/mnt/data目录中文件的4K随机读,成功地达到了1万IOPS的标称水准。也就是说,新创建的SSD磁盘性能还是相当给力的。 在实际的使用场景中,你就可以把一些读写较为密集的负载,比如数据库的数据目录,配置到这个SSD盘对应的目录下。 好了,通过上面的实验,相信你对云盘的挂载和使用有了比较直观的认识。**云盘的热挂载特性让它使用起来特别灵活方便,而且大小性能任你调度。挂载后的云硬盘真正使用起来,和你熟悉的硬盘操作也并没有什么两样。** ## 认识和使用本地磁盘 前面我们对于云虚拟机的硬盘作了许多讨论,都是围绕着“远程硬盘”这个产品形态来展开的。的确,远程云硬盘的好处很多,是计算存储分离架构的体现,也是云虚拟机硬盘的主流方式。 不过,有时我们还是会有点怀念“本地磁盘”,也就是直接位于宿主机上的硬盘。因为看似传统的本地硬盘,与远程硬盘相比起来,还是会有它自己的优点。毕竟它和计算单元离得近,而且没有三副本的负担,所以往往性能不俗,价格又相对便宜。 **那么,云上能否使用本地磁盘呢?** **答案是肯定的。**而且本地磁盘一般不需要自行创建,只要你选择了带有本地磁盘的机型,启动后,该型号对应大小和规格的本地磁盘就会自动被挂载。 你应该还记得,我在介绍虚拟机型号的[第3讲](https://time.geekbang.org/column/article/208288)中,提到了“本地存储”系列的虚拟机吧?那些正是自带有大容量、高性能本地磁盘的虚拟机型号。它们或是配备了高性能的本地NVMe SSD磁盘,或是装备有高吞吐的先进HDD,数量可能还不止一块。妥善使用这些本地磁盘,在合适的场景下,能够帮你发挥很大的作用。 比如,你要在云上用虚拟机自己搭建一个经典的Hadoop集群,要用虚拟机的磁盘组合成HDFS(Hadoop的分布式文件系统),并希望使用MapReduce或Spark等支持数据本地性(Data Locality)的计算框架。这时,你就应该考虑使用带有本地磁盘的机型了。 所以,当一些应用软件系统本身考虑到了硬件的不可靠性,设计了上层的存储冗余机制时,你就可以考虑采用本地磁盘。因为这种情况下,本地磁盘的可靠性缺陷得到了弥补,**它的相对高性能和低成本就成为了优势**。这时如果选用三副本的远程云硬盘,反倒显得有些笨重了。 还有一类,对数据丢失不敏感的**临时性存储**的场景,也是本地磁盘可以发挥的舞台。这些场景包括操作系统的pagefile或swap分区,以及数据库的硬盘缓存区(如SQL Server的Buffer Pool Extension)等等。 不过,我还是要提醒你本地磁盘的缺点,它在本质上,还是易失性(Ephemeral)的存储,**当机器关机或删除,以及出现硬件故障时,本地磁盘上的数据就可能损坏或丢失。**这一点我们必须牢记,不适用的场合必须使用更可靠的远程云硬盘。 ## 课堂总结与思考 今天我们围绕云硬盘,进行了一系列的讲解,可以简单总结如下: * 云硬盘是云虚拟机的主要持久化存储,与宿主机往往是分离的; * 云硬盘支持动态添加和删除,使用起来灵活方便; * 云硬盘一般提供多种性能等级,最终性能会受存储介质和容量大小的共同影响; * 部分虚拟机型号会自带高性能的本地磁盘,在可以容忍数据丢失风险时,是你值得考虑的一个选择。 最后,我还想补充一点,**云硬盘的付费模式,同样有按量付费和包年包月之分。**在很多的云上,你能够为一块云盘启用包年,长期租用的确定性也能够给你带来折扣,这和虚拟机资源的包年包月是一样的。 **今天,我想和你讨论的问题如下:** * 我们说云硬盘可以动态地挂载和卸载,使用起来十分方便。那么更进一步的问题是,已经挂载的云硬盘能够支持在线扩容吗? * 还有一种云端常见的存储类产品,如阿里云的文件存储NAS、AWS的EFS等,也可以挂载到云虚拟机。那么,你知道这种产品形态和云硬盘有什么区别,主要用于什么场景吗? 你可以在留言区和我互动。如果觉得有收获,也欢迎你把这篇文章分享给你的朋友。感谢阅读,我们下期再见。