2、Docker资源限制核心参数:CPU、内存与磁盘I/O

资源限制这件事,说白了就是给容器戴上「紧箍咒」。

我见过太多线上事故了——某个容器把宿主机CPU吃满,其他服务全部瘫痪。嗯,这就是没做资源限制的后果。今天咱们就把Docker资源限制的三个核心维度讲透:CPU、内存、磁盘I/O。

2.1 CPU限制:别让一个容器吃掉所有核

CPU限制有三个关键参数,我习惯把它们分成「硬限制」和「软限制」两类。

2.1.1 --cpus:最直观的硬限制

这个参数直接限制容器能使用多少个CPU核心。比如:

docker run --cpus="1.5" nginx

意思是这个容器最多用1.5个核。你想想看,如果宿主机是4核,那它最多占37.5%的CPU时间。

我的经验:线上服务我一般给到0.5~2个CPU。给太少容易超时,给太多浪费资源。我曾经有个Java应用,给了4个CPU,结果GC线程把CPU吃满了,其他业务线程反而抢不到时间片。

2.1.2 --cpu-shares:相对权重,不是绝对限制

这个参数很多人理解错了。它不是限制CPU上限,而是设置权重。

docker run --cpu-shares=512 service-a
docker run --cpu-shares=1024 service-b

当CPU资源充足时,两个容器都能跑满。只有CPU争抢时,service-b会得到两倍于service-a的时间片。

注意:默认值是1024。如果你不设置,所有容器权重一样。我曾经踩过坑——给一个日志处理容器设了2048,结果线上业务容器CPU被抢走一半。后来我改成用--cpus做硬限制,--cpu-shares只做辅助。

2.1.3 --cpuset-cpus:绑定到特定核心

这个参数把容器绑定到指定的CPU核心上运行。

docker run --cpuset-cpus="0,2" nginx

为什么需要这个?

我遇到过一种情况:某些CPU核心因为NUMA架构,访问特定内存更快。把容器绑定到这些核心上,性能能提升10%~20%。

实战建议:

  • 普通Web服务:用--cpus就够了
  • 数据库类:考虑--cpuset-cpus绑定到物理核心
  • 批处理任务:用--cpu-shares做优先级区分

2.2 内存限制:OOM Killer的最后一刀

内存限制比CPU更严格。CPU可以超卖,内存不行。一旦超了,系统会直接杀掉进程。

2.2.1 -m / --memory:硬性上限

docker run -m 512m nginx

容器最多用512MB内存。超了怎么办?会被OOM Killer干掉。

我记得有一次,一个Node.js应用因为内存泄漏,连续被杀了三次。后来我加了--memory-swap限制,才把问题暴露出来。

2.2.2 --memory-swap:内存+交换分区的总上限

这个参数很多人搞混。它控制的是「内存+交换分区」的总量。

docker run -m 512m --memory-swap=1g nginx

意思是:内存最多512MB,交换分区最多512MB,总共1GB。

避坑指南:如果不设置--memory-swap,默认是-m的两倍。我曾经有个容器,内存设了256m,交换分区自动给了512m。结果内存不够时疯狂写交换,性能暴跌。现在我习惯把--memory-swap设成和-m一样,禁用交换。

2.2.3 --memory-reservation:软限制

这个参数是「建议值」,不是硬限制。

docker run -m 1g --memory-reservation=512m nginx

当宿主机内存充足时,容器可以用到1GB。当内存紧张时,Docker会尝试把容器内存压缩到512MB以内。

嗯,这个参数我一般用在混部场景。比如把离线任务和在线任务混在一起,离线任务设一个较低的reservation,在线任务设高一些。

2.3 磁盘I/O限制:别让日志把磁盘打满

磁盘I/O限制是最容易被忽略的。我见过一个容器疯狂写日志,把整个宿主机的磁盘IOPS打满,其他容器全部卡死。

2.3.1 --blkio-weight:I/O优先级

和--cpu-shares类似,这是相对权重。

docker run --blkio-weight=500 service-a
docker run --blkio-weight=1000 service-b

当磁盘I/O争抢时,service-b得到两倍于service-a的I/O时间。

我的习惯:数据库容器给1000,日志容器给200,普通业务给500。这样即使日志量暴增,也不会影响数据库性能。

2.3.2 --device-read-bps / --device-write-bps:读写速率限制

这个参数直接限制每秒读写多少字节。

docker run --device-write-bps /dev/sda:10mb nginx

意思是:往/dev/sda写数据时,速度不超过10MB/s。

为什么要限制?

我曾经有个日志采集容器,每秒写200MB日志到磁盘。结果数据库容器因为磁盘带宽被抢,查询延迟从2ms飙升到500ms。后来我加了--device-write-bps=50mb,问题解决。

2.3.3 --device-read-iops / --device-write-iops:IOPS限制

除了带宽,还可以限制每秒I/O操作次数。

docker run --device-write-iops /dev/sda:1000 nginx

这个参数对SSD特别有用。因为SSD虽然带宽大,但IOPS有限。限制IOPS可以防止小文件读写把磁盘打满。

总结一下我的资源规划公式:

资源类型 硬限制 软限制 适用场景
CPU --cpus --cpu-shares 在线服务用硬限制,离线任务用软限制
内存 -m --memory-reservation 核心服务用硬限制,混部用软限制
磁盘I/O --device-*-bps --blkio-weight 日志类用bps限制,数据库用weight

最后说一句:资源限制不是设了就完事了。我建议每个容器都加上Prometheus监控,观察实际使用量。如果发现某个容器长期接近上限,就该考虑扩容了。

嗯,下一章咱们聊聊如何用这些参数做成本优化。说白了,资源限制做得好,云账单能省30%以上。