Docker 设置 --cpus=1 后,nproc 仍显示多个处理器
--cpus 限制 CPU 时间配额,--cpuset-cpus 限制可调度的处理器集合。核对 Docker 配置与 nproc 实现,区分 GNU coreutils 9.8 的 cgroup v2 行为。
容器启动时设置了 --cpus=1,里面的 nproc 却仍返回 8。再加 --cpuset-cpus=0-2 后,输出变成 3。仅凭这组数字,不能判断 Docker 忽略了 CPU 限制:CPU 时间配额和允许调度的处理器集合是两项不同设置。
本文针对 Linux Docker Engine 的普通 CPU 配额,不讨论实时调度。还要区分容器内 nproc 的实现与版本:GNU coreutils 9.8 起,nproc 开始考虑 cgroup v2 CPU 配额;旧版或其他实现的输出不能直接套用这个新行为。
--cpus=1 限制的是时间预算
--cpus 控制容器可使用的 CPU 时间总量。例如 --cpus=1.5 相当于在默认 100000 微秒的调度周期下配置 150000 微秒的 CPU 配额。多线程任务可以在不同处理器上运行,但它们共同消耗这份预算;这不是为容器独占预留 1.5 个核。
--cpus=1 也不表示固定绑定到编号 1 的 CPU,更不要求处理器枚举结果一定变成 1。在一台有 8 个逻辑处理器的机器上,它仍是一份相当于一个逻辑处理器的 CPU 时间预算,不能把参数再除以 8。
相应地,--cpus=0.5 是半个逻辑处理器的时间配额,不是“使用宿主机总 CPU 的一半”。如果原本想给任务一个 CPU 的预算,却按宿主机核数写成很小的小数,可能反而限制得过紧。
先核对目标容器保存的配置
在连接该容器所在 Engine 的终端执行只读检查。worker-example 是待替换的容器名:
docker inspect --type container --format \
'NanoCpus={{.HostConfig.NanoCpus}} CpuQuota={{.HostConfig.CpuQuota}} CpuPeriod={{.HostConfig.CpuPeriod}} CpusetCpus={{.HostConfig.CpusetCpus}}' \
worker-example
使用 --cpus=1 创建容器时,NanoCpus 对应的值为 1000000000。CpuQuota、CpuPeriod 则用于查看通过相应参数保存的设置;不要只看到其中一个字段是 0 就断定没有限制,还要一起检查 NanoCpus。
CpusetCpus 为空表示没有在这个字段显式指定处理器集合,不表示 CPU 时间配额一定为空。配置与预期不符时,先检查实际容器 ID、创建命令和部署配置,避免修改了另一个容器的启动参数。
inspect 反映的是 Docker 配置,不是一次完整的内核执行效果证明。如果业务监控确实显示持续超出预期,仍需检查 Engine 的警告、宿主机 cgroup 支持和监控统计口径;不能用一个 nproc 数字代替这些检查。
只有需要绑定具体 CPU 时才设置 cpuset
确实要求工作进程只在指定逻辑处理器上调度,才使用 --cpuset-cpus。下面是新建隔离验证容器的示例,镜像、名称及 CPU 编号都要按实际环境替换;该镜像应已有正确的默认启动命令:
docker run --name worker-cpu-check \
--cpus=1 \
--cpuset-cpus=0-2 \
your-approved-image:version
这里允许调度到编号 0、1、2 的三个逻辑处理器,同时保留总计一个 CPU 的时间预算。它不是给每个处理器各分配一份 --cpus=1,也不会禁止其他容器或宿主机进程使用这些处理器。
指定的编号必须在当前宿主机与上级限制允许的范围内。不要为了让 nproc 显示 1 就机械地把全部服务绑到 CPU 0;这会改变调度条件,也可能让多个服务挤在同一个处理器上。
若目的只是控制应用 worker 数,应优先使用应用明确支持的并发参数。CPU 配额不会替你把一个按宿主机处理器数创建线程池的应用改成单线程。
nproc 需要按实现和运行环境解释
镜像里已有 GNU nproc 时,可查看:
docker exec worker-example nproc --version
docker exec worker-example nproc
最小镜像可能没有该程序,BusyBox 等实现也可能不接受同样的版本选项。命令不存在或参数不支持,不代表 CPU 配额失效。
GNU coreutils 9.8 及以后的 nproc 会考虑 cgroup v2 配额,但返回值仍是正整数,也可能受到 OMP_NUM_THREADS、OMP_THREAD_LIMIT 的影响。不能要求它精确显示 --cpus=0.5 这样的分数。
此外,nproc --all 查询的是系统安装的处理器数量,不遵循普通 nproc 的 cgroup 配额和上述 OpenMP 环境变量限制。用 --all 的输出验证容器配额,检查对象已经变了。
验证资源目标,而不只是一个显示值
保留原部署配置,再在隔离环境比较配置字段、应用实际 worker 数和正常负载下的 CPU 使用。成功标准取决于需求:限制时间预算、限定可调度 CPU,或者减少应用并发,不能互相替代。
正式调整前确认业务延迟和吞吐要求,并保留原 CPU 参数。回退时恢复原配额、cpuset 和应用并发设置,按既有部署流程应用;不需要删除镜像、清理卷或给容器增加特权。若只是纠正监控对 nproc 的解释,则无需改变原本有效的资源限制。


