Skip to content

QoS ​

服务质量(Quality of Service,QoS)用来限制单个沙箱的网络和块设备 I/O 消耗。当前版本中,该功能默认关闭,只能在创建模板时配置。模板中的限制会分别应用到每个沙箱。

网络 QoS 只影响流量速率,不改变沙箱的网络可达范围。块设备 I/O QoS 限制每个沙箱块设备的吞吐量和操作速率,不改变文件系统容量或权限。

工作原理 ​

QoS 配置从模板进入沙箱运行时,最后由 CubeShim 在 virtio-net 和 virtio-blk 数据路径中执行:

网络 QoS 会分别限制每个沙箱的两个方向:上传从沙箱发往外部网络,下载从外部网络进入沙箱。配置的带宽上限和数据包处理速率上限会独立应用到上传和下载。

块设备 I/O QoS 会分别限制沙箱中的每个 virtio-blk 设备。同一设备的读取和写入共享配置的吞吐量桶和操作速率桶,不单独设置读写上限。

底层分别使用吞吐量和操作速率 Token Bucket。吞吐量桶按处理的字节数扣减额度,操作速率桶每处理一次操作扣减一次额度。额度按固定速率恢复;桶里有余量时允许短时突发,额度耗尽后需要等待恢复。因此,很短的测速可能暂时超过配置值,持续测试的平均速率应接近配置上限。额度大小和恢复速度由平台自动设置,用户只需要填写公开的限制值。

QoS 是运行时配置,不会修改模板的 rootfs。文件系统输入相同时,不同 QoS 配置仍可复用同一份 rootfs artifact。

配置语义 ​

用户可以配置网络限制、块设备 I/O 限制,或者同时配置两者:

json
{
  "network": {
    "bandwidthMbps": 100,
    "packetsPerSecond": 5000
  },
  "blockIo": {
    "throughputMiBps": 64,
    "iops": 1000
  }
}

network 中的 bandwidthMbps 和 packetsPerSecond 至少配置一个,且值必须是大于 0 的整数。bandwidthMbps 限制每个沙箱的每秒兆比特数,packetsPerSecond 限制每秒 virtio-net 数据包处理操作数。如果启用了分段等卸载功能,一个操作可能对应多个线上数据包,因此实际线上 PPS 可能与配置值不同。上传和下载分别使用独立额度,不同沙箱之间也不共享额度。

blockIo 中的 throughputMiBps 和 iops 至少配置一个。throughputMiBps 限制持续块设备吞吐量,单位为 MiB/s;iops 限制每秒块设备 I/O 操作数。同一组限制会独立应用到沙箱的每个 virtio-blk 设备,每个设备分别维护自己的额度。

当前版本只支持在创建模板时配置 QoS。使用该模板创建的每个沙箱都会应用相同的限制,创建沙箱时不能单独设置或覆盖 QoS。模板未配置 qos 时,不会设置本页介绍的网络或块设备 I/O 限制。

使用边界 ​

bandwidthMbps 只限制单个沙箱的最大网络速率。节点级、租户级或 NAT 出口的总带宽仍由底层基础设施决定;所有活跃沙箱的需求超过实际链路容量时,沙箱之间仍会竞争带宽。

当前上传和下载使用相同的带宽和数据包处理速率配置,暂不支持分别设置两个方向的上限。块设备 I/O 也不区分读取和写入限制。QoS 不提供优先级、最低保障吞吐量或权重调度。

内部注解与兼容性 ​

网络和块设备 I/O QoS 内部使用 cube.master.net 和 cube.master.blk.qos 注解存储。它们是运行时契约,不属于公开 API:

  • 沙箱或模板请求中直接设置这两个注解会被拒绝,请改用模板的 qos 字段。
  • 模板的 create_request、preview 和 snapshot 响应不再包含这两个注解,请读取 configuredQos。
  • 带 create_request override 的模板 commit 仍会读取源沙箱的持久化创建请求,以便继承模板托管的 QoS;如果该记录不可读,commit 会失败,而不是生成一个没有限速的模板。

配置模板 ​

创建模板时通过 --qos 或 CubeAPI 请求体设置限制。下面示例配置了每个沙箱上传、下载各 100 Mbps 和 5000 个数据包处理操作/秒的上限,同时配置每个块设备 64 MiB/s 和 1000 IOPS 的上限。

CLI 内联 JSON ​

bash
cubemastercli tpl create-from-image \
  --image cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest \
  --writable-layer-size 1G \
  --qos '{"network":{"bandwidthMbps":100,"packetsPerSecond":5000},"blockIo":{"throughputMiBps":64,"iops":1000}}'

CLI JSON 文件 ​

也可以把配置保存为 qos.json:

json
{
  "network": {
    "bandwidthMbps": 100,
    "packetsPerSecond": 5000
  },
  "blockIo": {
    "throughputMiBps": 64,
    "iops": 1000
  }
}

创建模板时使用 @ 前缀读取文件:

bash
cubemastercli tpl create-from-image \
  --image cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest \
  --writable-layer-size 1G \
  --qos @qos.json

CubeAPI ​

通过 CubeAPI 创建模板时,在请求中传入同一个 qos 对象:

http
POST /cubeapi/v1/templates
Content-Type: application/json
json
{
  "image": "cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest",
  "writableLayerSize": "1G",
  "qos": {
    "network": {
      "bandwidthMbps": 100,
      "packetsPerSecond": 5000
    },
    "blockIo": {
      "throughputMiBps": 64,
      "iops": 1000
    }
  }
}

CubeMaster 会在创建模板前校验 qos。每个已配置的部分必须至少包含一个大于 0 的整数限制。显式传入 0、负数、非整数、空配置部分或未知字段时,请求会被拒绝。

查看配置与应用状态 ​

查看模板配置 ​

使用 CLI 查询模板:

bash
cubemastercli tpl info <template-id>

CubeAPI 对应 GET /cubeapi/v1/templates/<template-id>。配置了 QoS 的模板会返回:

json
{
  "configuredQos": {
    "network": {
      "bandwidthMbps": 100,
      "packetsPerSecond": 5000
    },
    "blockIo": {
      "throughputMiBps": 64,
      "iops": 1000
    }
  }
}

查看沙箱状态 ​

使用 CLI 查询沙箱:

bash
cubemastercli cubebox info --sandboxid <sandbox-id>

CLI 会显示 NETWORK_QOS 和 BLOCK_IO_QOS。CubeAPI 的 GET /cubeapi/v1/sandboxes/<sandbox-id> 返回对应的 configuredQos 字段:

json
{
  "configuredQos": {
    "network": {
      "bandwidthMbps": 100,
      "packetsPerSecond": 5000
    },
    "blockIo": {
      "throughputMiBps": 64,
      "iops": 1000
    }
  }
}

configuredQos 表示记录的 QoS 配置。如果 CubeMaster 保存的沙箱创建请求中没有 QoS annotation,则从模板读取配置。这是配置记录,不能证明 limiter 已在 hypervisor 生效,也不能证明每项限制都已配置。

节点网络 QoS 排障 ​

Cubelet 在创建沙箱 tap 时会记录网络初始化请求。在沙箱所在节点上,按沙箱 ID 检索 Cubelet 日志:

text
tap create ensure request: sandbox_id=<sandbox-id> ... persist_metadata={"qos_enabled":"true",...}

qos_enabled 表示该请求携带了网络 Token Bucket 配置。它不反映块设备 I/O QoS,也不属于公开 API。

Cubelet 的只读诊断接口 GET /v1/network/taps(也可使用 cubecli container taps)会列出每个沙箱的 tap 状态;它能确认 tap 存在且状态正常,但不包含 QoS 信息。控制面的 configuredQos 记录的是请求的配置,不是实际生效速率;限速是否生效请使用下面的 iperf3 测量验证。

使用 iperf3 验证 ​

先创建带网络 QoS 的模板并等待模板进入 READY 状态,再使用该模板创建待测沙箱。下面的客户端命令都在这个沙箱内执行。

iperf3 不一定预装在服务端或沙箱镜像中。测试前先在两端运行 iperf3 --version;如果命令不存在,按系统或镜像使用的包管理器安装。例如 Debian/Ubuntu 可以执行:

bash
apt-get update
apt-get install -y iperf3

在沙箱能够访问的机器上启动服务端:

bash
iperf3 -s -p 5201

确认沙箱能够访问服务端 IP,并且服务端防火墙已放通所选 TCP 端口。如果连接失败,应先解决连通性问题,再判断网络 QoS 是否生效。

分别测试上传和下载:

bash
# 沙箱上传到服务端
iperf3 -c <server-ip> -p 5201 -t 10 -O 3 -P 4

# 沙箱从服务端下载
iperf3 -c <server-ip> -p 5201 -t 10 -O 3 -P 4 -R

测试结果会受到协议开销、Token Bucket 短时突发和测量窗口影响。100 Mbps 的配置通常会测得接近 100 Mbps,不要求精确相等。

模板返回 configuredQos 只能说明配置已经保存。带宽限速需要通过持续上传、下载测试确认实测值接近配置的带宽上限。iperf3 不能直接测量数据包处理操作数;如果只配置 PPS,应使用固定大小的数据包工作负载,并在测试端点通过报文计数器验证,同时注意卸载功能可能导致线上 PPS 与配置的操作速率不同。

验证多沙箱并发带宽限制 ​

一个 iperf3 server 进程通常一次只处理一个测试。多个客户端连接同一个 server 端口时,测试可能被串行执行,无法反映并发带宽。测试多个沙箱时,为每个沙箱启动独立端口:

bash
for port in 5301 5302 5303 5304 5305; do
  iperf3 -s -p "$port" -D
done

同时启动沙箱客户端,分别连接 5301 到 5305。如果模板配置为 100 Mbps,并且宿主机和网络容量足够,5 个沙箱应各自接近 100 Mbps,总吞吐接近 500 Mbps。

多沙箱测试还需要保证配置的带宽上限之和不超过测试链路容量。否则,结果会同时受到共享链路竞争影响,不能只根据单个沙箱低于配置上限判断带宽 limiter 是否正常。

使用 fio 验证块设备 I/O ​

先创建带块设备 I/O QoS 的模板并等待模板进入 READY 状态,再使用该模板创建待测沙箱。

fio 不一定预装在沙箱镜像中。测试前先运行 fio --version;如果命令不存在,按镜像使用的包管理器安装。Debian 或 Ubuntu 可以执行 apt-get update && apt-get install -y fio。

先在沙箱中执行 lsblk,选择位于可写 virtio-blk 文件系统中的测试目录。不要直接对根块设备执行破坏性测试。下面的命令使用 /tmp/qos-fio 文件,并绕过 guest page cache:

bash
# 持续顺序写吞吐量
fio --name=block-qos-bw --filename=/tmp/qos-fio \
  --size=1G --rw=write --bs=1M --direct=1 --runtime=30 --time_based

# 随机读 IOPS
fio --name=block-qos-iops --filename=/tmp/qos-fio \
  --size=1G --rw=randread --bs=4k --direct=1 --iodepth=32 \
  --runtime=30 --time_based

rm -f /tmp/qos-fio

初始 Token Bucket 额度耗尽后,持续吞吐量和 IOPS 应接近配置上限。virtio-fs 或内存中的文件不会经过 virtio-blk limiter,不能用于这项验证。