2. CPU限制实战:使用--cpus参数限制容器CPU使用

CPU资源限制,是容器化部署中最基础也最关键的环节。我见过太多线上事故,都是因为某个容器把宿主机的CPU吃满,导致其他服务集体瘫痪。今天我们就来聊聊--cpus这个参数,怎么用、怎么测、怎么避坑。

2.1 为什么需要限制容器CPU?

你想想看,一台宿主机上跑十几个容器,如果每个容器都"野蛮生长",CPU资源抢着用,会发生什么?

  • 某个高负载容器把CPU占满,其他容器直接"饿死"
  • 业务高峰期,所有容器一起抢CPU,响应时间飙升
  • 宿主机负载过高,连SSH都连不上去

说白了,不限制CPU的容器,就像没有刹车的跑车。我在项目中遇到过好几次,开发环境里跑个压测脚本,结果把整个测试集群搞崩了。从那以后,我要求所有容器必须显式声明CPU限制。

2.2 --cpus参数详解

--cpus是Docker 1.13版本引入的参数,用来限制容器能使用的CPU核心数。它支持小数,比如--cpus=1.5表示容器最多使用1.5个CPU核心。

核心概念:--cpus 限制的是CPU时间片,不是物理核心数。1个CPU = 1000毫核(millicores)。

举个例子:

# 限制容器最多使用1个CPU核心
docker run --cpus=1 -d nginx

# 限制容器最多使用0.5个CPU核心
docker run --cpus=0.5 -d nginx

# 限制容器最多使用2.5个CPU核心
docker run --cpus=2.5 -d nginx

2.3 实战:用stress工具验证CPU限制

光说不练假把式。我们来做个实验,看看--cpus到底有没有用。

首先,拉一个stress镜像:

docker pull polinux/stress

实验一:不限制CPU

docker run --rm -it polinux/stress --cpu 4 --timeout 30s

这个命令会启动4个CPU工作线程,每个线程疯狂计算。用docker stats看看:

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT
a1b2c3d4e5f6   stress    398.5%    12.3MiB / 1.944GiB

看到没?CPU使用率接近400%,相当于吃满了4个核心。如果宿主机只有4核,其他容器基本没法用了。

实验二:限制为1个CPU

docker run --rm -it --cpus=1 polinux/stress --cpu 4 --timeout 30s

再看docker stats

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT
b2c3d4e5f6a7   stress    99.8%     12.5MiB / 1.944GiB

嗯,CPU使用率被稳稳地限制在100%左右。虽然容器内部启动了4个线程,但Docker通过CFS调度器,只给它分配了1个CPU的时间片。

我的经验:生产环境中,我习惯给每个容器预留20%的CPU余量。比如业务需要2个CPU,我就设--cpus=2.5。这样突发流量来了,容器还能扛一扛。

2.4 多容器场景下的CPU分配

假设你有一台4核的宿主机,要部署三个服务:

服务 建议CPU限制 说明
Web服务 2.0 核心业务,需要稳定性能
数据处理 1.0 后台任务,可以慢一点
日志收集 0.5 非关键任务,别抢资源

这样分配,总限制是3.5个CPU,留了0.5个CPU给系统和其他进程。我曾经见过有人把三个容器都设成--cpus=2,结果总限制6个CPU,远超宿主机4核。你以为能超卖?实际上容器之间疯狂争抢,性能反而更差。

注意:--cpus的总和不要超过宿主机核心数。超卖会导致CPU资源竞争,所有容器性能都受影响。我曾经吃过这个亏,三个容器各设2个CPU,4核机器直接被打到100%负载,SSH都卡。

2.5 动态调整CPU限制

有时候容器已经跑起来了,才发现CPU限制设得太紧或太松。别慌,Docker支持动态调整:

# 查看当前限制
docker inspect <容器ID> | grep -i cpu

# 动态调整CPU限制(从1.0改为2.0)
docker update --cpus=2.0 <容器ID>

# 验证是否生效
docker stats <容器ID>

这个功能在线上排障时特别有用。我记得有一次,某个服务突然流量暴涨,我直接在线上把CPU限制从2.0调到4.0,服务立刻恢复正常。等流量降下来,再调回去就行。

2.6 与旧版CPU限制参数的对比

--cpus出现之前,Docker用的是--cpu-quota--cpu-period这两个参数。它们的关系是:

--cpus = --cpu-quota / --cpu-period

默认的--cpu-period是100000微秒(100ms)。如果你设--cpus=1.5,相当于:

--cpu-quota=150000 --cpu-period=100000

我个人建议直接用--cpus,简单直观。除非你有非常精细的调度需求,才去碰--cpu-quota--cpu-period

2.7 避坑指南

最后,分享几个我踩过的坑:

  • 不要设成0--cpus=0不会限制CPU,等于没设。我曾经手滑输错过,排查了半天。
  • 注意小数精度--cpus=0.1是100毫核,但有些旧版Docker不支持这么小的值。建议最小设到0.5。
  • 配合--cpuset-cpus使用:如果想让容器绑定到特定CPU核心,可以用--cpuset-cpus=0,1。但要注意,绑核后--cpus的限制依然有效。
  • 监控不能少:设了限制不代表万事大吉。我习惯用docker stats配合Prometheus,实时监控每个容器的CPU使用情况。

一句话总结:--cpus是容器CPU限制的"黄金参数",简单、直观、有效。生产环境务必给每个容器设置合理的CPU上限,这是保障集群稳定的第一步。

下一章,我们会聊--cpu-shares这个相对权重参数。它和--cpus有什么区别?什么时候该用哪个?到时候我们细说。