Podman --device-write-bps 详解:设备写入带宽限制的原理、用法与约束
2026/9/19 23:57:21 网站建设 项目流程

Podman --device-write-bps 详解:设备写入带宽限制的原理、用法与约束

【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman

--device-write-bps是 Podman 用于限制容器向指定块设备写入带宽(字节/秒)的核心资源控制选项,支持在podman runcreatecontainer cloneupdate以及pod createpod clone中直接使用。本指南将围绕该选项的语法、底层实现(cgroup 限速与块设备校验)、可验证的观测方法以及 rootless 与 cgroups V1 等约束条件展开,帮助你在多容器、多租户场景下精准控制磁盘写带宽。

选项总览:作用范围与格式

--device-write-bps定义于 docs/source/markdown/options/device-write-bps.md,这是一个被多个命令共用的“选项文件”。从该文件头部的注释可以看出,它会被以下命令的 man page 共同引用:

  • podman container clone
  • podman create
  • podman pod clone
  • podman pod create
  • podman run
  • podman update

即无论是创建单个容器、克隆容器,还是创建/克隆 Pod,甚至是给已运行的容器动态调整资源,都可以使用同一套语法。对应 man page 分别位于 podman-create.1.md.in、podman-run.1.md.in、podman-update.1.md.in、podman-container-clone.1.md.in、podman-pod-create.1.md.in 与 podman-pod-clone.1.md.in。

其基本语法为:

--device-write-bps=path:rate
  • path:目标块设备路径,例如/dev/sda
  • rate:每秒允许写入的字节数,例如1mb

官方给出的最小示例为:

--device-write-bps=/dev/sda:1mb

参数解析规则:从字符串到限速配置

在 CLI 层,该选项在 cmd/podman/common/create.go 中被定义为字符串数组(StringArrayVar),意味着可以多次指定,为多个不同设备分别设置不同的写入速率:

deviceWriteBpsFlagName := "device-write-bps" createFlags.StringArrayVar( &cf.DeviceWriteBPs, deviceWriteBpsFlagName, []string{}, "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", )

随后,pkg/specgenutil/specgen.go中的parseThrottleBPSDevices(pkg/specgenutil/specgen.go)负责把用户输入的字符串解析为结构化的限速配置。从源码可以提炼出以下严格校验规则:

  1. 必须包含:分隔符,否则报错bad format: <dev>
  2. 设备路径必须以/dev/开头,否则报错bad format for device path: <dev>
  3. 速率值必须是正整数,单位可选,支持kbmbgb(大小写不敏感,由units.RAMInBytes统一换算为字节数),解析失败或为负数都会报错:

    The correct format is ` : [ ]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb

因此以下写法均合法:

--device-write-bps=/dev/sda:1mb # 每秒 1 MiB --device-write-bps=/dev/sda:1048576 # 等价写法:直接写字节数 --device-write-bps=/dev/sda:512kb --device-write-bps=/dev/sda:2gb

同时设置多个设备的写法:

--device-write-bps=/dev/sda:1mb --device-write-bps=/dev/sdb:500kb

解析结果被存入 SpecGenerator 的ThrottleWriteBpsDevice字段(见 pkg/specgenutil/specgen.go)。

块设备校验:为什么非块设备会被拒绝

与 Docker 不同,Podman 在生成 OCI 运行时配置前会校验path是否真的是块设备(block device)。这一逻辑位于 pkg/specgen/utils_linux.go:

  • statBlkDev调用unix.Stat获取设备状态,并检查stat.Mode & S_IFMT == S_IFBLK;若不是块设备,返回%s: not a block device错误;
  • 校验通过后,进一步通过unix.Major(rdev)unix.Minor(rdev)解析出设备的主设备号(major)与次设备号(minor);
  • fillThrottleDev将 major/minor 填入spec.LinuxThrottleDevice

FinishThrottleDevices(pkg/specgen/utils_linux.go)会把解析后的设备最终写入ResourceLimits.BlockIO.ThrottleWriteBpsDevice。也就是说,cgroup 限速实际上是以major:minor 设备号而非路径字符串为键的。

这一点在系统级测试中有直接验证:test/system/280-update.bats中“podman update - non-block device rejected by --deviceoptions”用例(test/system/280-update.bats)对/dev/zero(字符设备,非块设备)依次传入--blkio-weight-device--device-read-bps--device-write-bps--device-read-iops--device-write-iops,断言全部以退出码 125 失败,且输出包含not a block device

podman update "$cid" --device-write-bps=/dev/zero:10mb # 期望:Error ... /dev/zero: not a block device

完整实战用法

1. 创建容器时限制写入带宽

podman run -d --name db \ --device-write-bps=/dev/sda:1mb \ --device-read-bps=/dev/sda:10mb \ postgres

2. 在 Pod 中限制共享存储写入

podman pod create --name web \ --device-write-bps=/dev/sda:2mb \ -p 8080:80 podman run --pod web nginx

3. 对已运行容器动态调整(update)

podman update支持在不重启容器的情况下调整设备写带宽。官方 man page(podman-update.1.md.in)给出的多选项综合示例:

podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 \ --memory 1G --memory-swap 2G --memory-reservation 2G \ --blkio-weight-device /dev/sda:123 --blkio-weight 123 \ --device-read-bps /dev/sda:10mb --device-write-bps /dev/sda:10mb \ --device-read-iops /dev/sda:1000 --device-write-iops /dev/sda:1000 \ --pids-limit 123 ctrID

update 的实现路径同样复用 create 的解析逻辑:cmd/podman/containers/update.goGetChangedDeviceLimits(cmd/podman/containers/update.go)把 SpecGenerator 中的ThrottleWriteBpsDevice取出,封装成UpdateContainerDevicesLimits,再经ContainerUpdate下发。

4. 克隆容器时继承或改写限速

podman container clone --device-write-bps=/dev/sda:500kb old-ctr new-ctr

5. 结合 --latest 快捷操作

podman run -d --name app sleep infinity podman update --latest --device-write-bps=/dev/sda:3mb

底层原理:cgroup 是如何“限速”的

设备读写限速最终由 Linux cgroup 的 block I/O 控制器实现,Podman 通过 OCI 运行时(crun/runc)将ThrottleWriteBpsDevice写入 cgroup 文件系统:

  • cgroups v2:写入io.max(格式为major:minor rbps=... wbps=...);
  • cgroups v1:写入blkio.throttle.write_bps_device

E2E 测试直接验证了这一事实。test/e2e/run_test.go中的 “podman run device-write-bps test” 用例(test/e2e/run_test.go)启动容器后,在容器内读取自身的 cgroupio.max文件来断言限速已生效:

podman run --rm --device-write-bps=/dev/nullb0:1mb alpine \ sh -c "cat /sys/fs/cgroup/\$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"

系统级测试test/system/280-update.bats的资源选项矩阵(test/system/280-update.bats)也把device-write-bpsio.max对应起来,并标注其在rootless 下不生效(表格中false列):

device-read-bps = $LOOPDEVICE:10mb | false | io.max = $devicemax device-read-iops = $LOOPDEVICE:2000 | false | io.max = $devicemax device-write-bps = $LOOPDEVICE:30mb | false | io.max = $devicemax device-write-iops = $LOOPDEVICE:4000 | false | io.max = $devicemax

验证与观测:如何确认限速已生效

除了读取容器内 cgroup 文件,还可以通过podman inspect从“管理面”确认配置:

podman inspect <ctr> --format '{{.HostConfig.BlkioDeviceWriteBps}}'

在 libpod/container_inspect_linux.go 中,Podman 会把 OCI 配置里的ThrottleWriteBpsDevice(major/minor + Rate)通过主机设备节点映射回设备路径,输出为HostConfig.BlkioDeviceWriteBps,与 Docker 的 inspect 输出结构保持一致,便于迁移与自动化脚本解析。

在 Docker 兼容 API 一侧,pkg/api/handlers/compat/containers_create.go(pkg/api/handlers/compat/containers_create.go)会把请求体HostConfig.BlkioDeviceWriteBps中的每个设备转换为<path>:<rate>字符串,再走与 CLI 相同的解析流程。因此通过curl调用/containers/create传入:

{ "HostConfig": { "BlkioDeviceWriteBps": [ {"Path": "/dev/sda", "Rate": 1048576} ] } }

podman run --device-write-bps=/dev/sda:1mb效果等价。

重要限制与约束

原文档明确了两条硬性限制,实际使用前务必确认运行环境:

  1. cgroups V1 rootless 系统不支持--device-write-bps在 cgroups V1 且以 rootless 模式运行时不可用。这源于 rootless 容器使用用户命名空间与 cgroup 委托机制,v1 控制器下无法完成设备级带宽委托。
  2. 非 root 用户可能无权修改资源限制:在部分基于 systemd 的系统上,非 root 用户没有资源限制委托(delegation)权限,设置资源限制会直接失败。

对于第二条,仓库根目录的 troubleshooting.md(第 26 节 “Running containers with resource limits fails with a permissions error”)给出了完整的排查与解决方案:

症状:运行带资源限制选项的容器时报错,例如:

Error: OCI runtime error: crun: the requested cgroup controller `cpu` is not available

排查:检查当前用户的 cgroup 控制器委托情况:

cat "/sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers"

如果输出中缺少io(或cpucpuset)控制器,说明委托未开启。

解决:为所有用户启用控制器委托,创建/etc/systemd/system/user@.service.d/delegate.conf

[Service] Delegate=memory pids io cpu cpuset

保存后重新登录,即可获得设置--device-write-bps等资源限制的权限。

与同族选项的关系

--device-write-bps属于 Podman 设备 I/O 限速家族中的一员,与其配套的还有:

选项作用底层控制器字段
--device-read-bps限制设备读带宽(字节/秒)ThrottleReadBpsDevice
--device-write-bps限制设备写带宽(字节/秒)ThrottleWriteBpsDevice
--device-read-iops限制设备读 IOPSThrottleReadIOPSDevice
--device-write-iops限制设备写 IOPSThrottleWriteIOPSDevice
--blkio-weight-device设置设备级相对权重(10–1000)WeightDevice
--blkio-weight设置全局相对权重(10–1000)BlkIOWeight

它们共用同一套path:value解析框架与块设备校验逻辑,其中 IOPS 族(parseThrottleIOPsDevices,见 pkg/specgenutil/specgen.go)要求速率必须是纯整数(无单位)。实际规划时可将BPS 限速IOPS 限速组合使用:例如写入带宽限制1mb之外再加写 IOPS 上限100,从而同时约束吞吐与并发写请求数。

小结

--device-write-bps是 Podman 在多租户环境中隔离容器磁盘写带宽的关键开关,其价值在于:语法简洁(path:rate,支持 kb/mb/gb 单位与多设备组合)、创建/克隆/更新全生命周期可用、底层通过 cgroupio.max(v2)或blkio.throttle.write_bps_device(v1)强制执行,并有podman inspect与系统级 bats 测试双重可观测保障。部署前请重点核对两点:cgroups v1 rootless 环境不可用,以及非 root 用户是否具备 cgroup 控制器委托权限(参考 troubleshooting.md 第 26 节启用Delegate即可解决)。

【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询