1. 虚拟机IO的QoS限速
在openstack+ceph的典型云平台架构下,目前有两种主要的方式可对虚拟机的IO进行QoS限速:
一种为通过librbd的镜像IO限速机制进行限速
一种为通过qemu的块设备IO限速机制进行限速
两种qos限速所处位置如下图所示:
1.1 通过librbd的镜像IO限速机制进行限速
Ceph在13.2.0版本(m版)支持对RBD镜像的IO限速,此版本仅支持总iops场景的限速,且支持突发,支持配置突发速率,但不可控制突发时长(实际相当于突发时长设置为1秒且无法修改)。在14.2.0版本(n版)增加了对读iops、写iops、总bps、读bps、写bps这5种场景的限速支持,对突发的支持效果保持不变。
场景 | 基本速率上限配置 | 突发速率配置 |
总iops | rbd_qos_iops_limit | rbd_qos_iops_burst |
读iops | rbd_qos_iops_read_limit | rbd_qos_iops_read_burst |
写iops | rbd_qos_iops_write_limit | rbd_qos_iops_write_burst |
总bps | rbd_qos_bps_limit | rbd_qos_bps_burst |
读bps | rbd_qos_bps_read_limit | rbd_qos_bps_read_burst |
写bps | rbd_qos_bps_write_limit | rbd_qos_bps_write_burst |
1.2 通过qemu的块设备IO限速机制进行限速
Qemu早在1.1版本就已支持块设备IO限速,提供6个配置项,可对上述6种场景分别进行速率上限设置。在1.7版本对块设备IO限速增加了支持突发的功能,以总iops场景为例,支持设置可突发的总iops数量。在2.6版本对支持突发的功能进行了完善,可控制突发速率和时长。
场景 | 基本速率上限配置 | 突发速率配置 | 突发时长配置 |
总iops | iops-total | iops-total-max | iops-total-max-length |
读iops | iops-read | iops-read-max | iops-read-max-length |
写iops | iops-write | iops-write-max | iops-write-max-length |
总bps | bps-total | bps-total-max | bps-total-max-length |
读bps | bps-read | bps-read-max | bps-read-max-length |
写bps | bps-write | bps-write-max | bps-write-max-length |
Ceph librbd的镜像IO限速机制使用的是令牌桶算法,而Qemu的块设备IO限速机制主要是通过漏桶算法实现。令牌桶算法和漏桶算法也是最为常见的两种QoS限速算法,最初都是为解决网络层报文限流问题而设计,实际可应用到各种需要实现QoS控制的场景。
算法的实际实现跟算法本身的定义是可能存在一定出入的,第2节先来看看这两种算法的定义和特点,第3和第4节再来分别看看librbd和qemu对算法的实际实现与算法的定义有何不同。
2. QoS限速算法介绍
2.1 令牌桶算法
对于令牌桶算法,维基百科(https://en.wikipedia.org/wiki/Token_bucket)引用的一种定义描述如下:
The token bucket algorithm can be conceptually understood as follows:
A token is added to the bucket every 1/r seconds.
The bucket can hold at the most b tokens. If a token arrives when the bucket is full, it is discarded.
When a packet (network layer PDU) of n bytes arrives,
if at least n tokens are in the bucket, n tokens are removed from the bucket, and the packet is sent to the network.
if fewer than n tokens are available, no tokens are removed from the bucket, and the packet is considered to be non-conformant.
这段描述算是通俗易懂,以限制IO请求iops为例,替换上述的网络层报文,我们可以理解如下:
一个固定容量的桶装着一定数量的令牌,桶的容量即令牌数量上限。桶里的令牌每隔固定间隔补充一个,直到桶被装满。一个IO请求将消耗一个令牌,如果桶里有令牌,则该IO请求消耗令牌后放行,反之则无法放行。
对于限制IO请求bps,只需让一个IO请求消耗n个令牌即可,n即为此IO请求的字节数。
按照以上描述,我们可以知道,令牌桶算法可以达到以下效果:
令牌桶算法可以通过控制令牌补充速率来控制处理IO请求的速率;
令牌桶算法允许一定程度的突发,只要桶里的令牌没有耗尽,IO请求即可立即消耗令牌并放行,这段时间内IO请求处理速率将大于令牌补充速率,令牌补充速率实际为平均处理速率;
令牌桶算法无法控制突发速率上限和突发时长,突发时长由实际IO请求速率决定,若实际IO请求大于令牌补充速率且速率恒定,则:突发时长=令牌桶容量/(实际IO请求速率-令牌补充速率)
在令牌桶算法的描述中,有一个条件是无强制约束的,那就是在桶里的令牌耗尽时,无法放行的packet(或者是IO请求)该怎么处理。对于处理网络层报文,实际无外乎三种方式:
第一种是直接丢弃(traffic policing,流量监管)
第二种则是排队等待直到令牌桶完成所缺失数量令牌补充后再消耗令牌放行(traffic shaping,流量整形)
第三种为前两者的折中,设定有限长度队列,在队列已满时丢弃,否则遵循第二种处理方式。当然对于不可随意丢弃的IO请求,处理方式一般为第二种。
此外,一般在实际的实现中,难以做到严格按照固定时间间隔一次补充一个令牌。可考虑的替代方案一般为,加大补充令牌的时间间隔,减少补充次数,但一次补充多个令牌,单次补充令牌的个数与时间间隔成正比。
对于令牌桶算法,我们还可以考虑一种极端的情况。令牌桶算法支持突发的能力是由令牌桶的容量决定的,以限制IO请求iops为例,假设我们将令牌桶容量仅设为1,此时令牌桶算法支持突发的能力也就不复存在了,IO请求速率上限即被严格控制为令牌补充速率。
2.2 漏桶算法
对于漏桶算法,维基百科(https://en.wikipedia.org/wiki/Leaky_bucket)中有两种类型的定义,一种是leaky bucket as a meter,一种是leaky bucket as a queue。
对于leaky bucket as a meter,引用的一种定义描述如下:
A fixed capacity bucket, associated with each virtual connection or user, leaks at a fixed rate.
λ If the bucket is empty, it stops leaking.For a packet to conform, it has to be possible to add a specific amount of water to the bucket: The specific amount added by a conforming packet can be the same for all packets, or can be proportional to the length of the packet.
If this amount of water would cause the bucket to exceed its capacity then the packet does not conform and the water in the bucket is left unchanged.
而对于leaky bucket as a queue,引用的一种定义描述如下:
The leaky bucket consists of a finite queue.
When a packet arrives, if there is room on the queue it is appended to the queue; otherwise it is discarded.
At every clock tick one packet is transmitted (unless the queue is empty)
先来看leaky bucket as a meter的定义描述,同样以限制IO请求iops为例,替换上述的网络层报文,我们可以理解如下:
一个桶,容量固定。桶以固定的速率漏水,除非桶为空。一个IO请求将往桶里增加固定量的水,假如增加的水量将导致桶里的水溢出,则该IO请求无法放行,反之则放行。
再把令牌桶的理解放在这里对比下:一个固定容量的桶装着一定数量的令牌,桶的容量即令牌数量上限。桶里的令牌每隔固定间隔补充一个,直到桶被装满。一个IO请求将消耗一个令牌,如果桶里有令牌,则该IO请求消耗令牌后放行,反之则无法放行。
实际上,把漏水换成补充令牌,把加水换成消耗令牌,再细细品读,我们可以发现,这两段描述的含义就是一样的,仅仅是角度不一样而已。所以leaky bucket as a meter与token bucket可认为