一、Docker资源限制基础:为什么需要资源限制?Cgroups工作原理简介

各位同学,咱们今天聊聊资源限制这个话题。说实话,我刚开始用Docker那会儿,觉得这玩意儿就是个轻量级虚拟机,跑起来就完事了。结果呢?有一次线上服务突然卡死,排查了半天——原来是某个容器把CPU吃满了,其他容器全在排队等资源。嗯,从那以后,我再也不敢忽视资源限制了。

1.1 为什么需要资源限制?

你想想看,一台物理机上跑十几个容器,如果没有资源限制,会是什么场景?

  • 一个容器把CPU占满——其他容器响应变慢,甚至超时
  • 一个容器疯狂写日志——磁盘空间被撑爆,所有容器都写不了数据
  • 一个容器内存泄漏——OOM Killer开始随机杀进程,谁也跑不掉

说白了,资源限制就是给每个容器画个圈,告诉它:你只能在这个圈里折腾,别越界。我在项目中遇到过最典型的案例:一个Java应用因为没配内存限制,堆内存直接飙到8GB,把同一台机器上的数据库容器给挤崩了。那次事故之后,我养成了一个习惯——任何容器启动,第一件事就是配资源限制

核心原则:没有资源限制的容器,就像没有刹车的跑车——跑得快,但随时可能出事。

1.2 资源限制到底限制什么?

Docker主要限制这几类资源:

资源类型 限制参数 典型场景
CPU --cpus--cpu-shares 限制容器最多使用1.5个核
内存 --memory--memory-swap 限制容器最多使用512MB内存
磁盘I/O --device-read-bps--device-write-iops 限制容器每秒最多读写100MB
网络 --network配合tc工具 限制容器带宽为10Mbps

我个人习惯是:内存限制必须配,CPU限制看情况,磁盘I/O限制在共享存储场景下一定要配。为什么内存必须配?因为内存不够会触发OOM,直接干掉进程,这后果太严重了。

1.3 Cgroups工作原理简介

好,现在咱们聊聊底层原理。Docker的资源限制能力,其实是靠Linux内核的Cgroups(Control Groups)实现的。Cgroups是什么?说白了,就是内核里的一套机制,用来分组管理进程的资源使用

我记得第一次看Cgroups的文档时,被那些术语搞得头大。后来我换了个角度理解:Cgroups就像给进程们分宿舍——每个宿舍(控制组)有独立的资源配额,宿舍里的进程只能使用分配好的资源,不能抢隔壁宿舍的。

Cgroups的核心概念

  • 子系统(Subsystem):每种资源对应一个子系统,比如cpu、memory、blkio
  • 控制组(Control Group):一组进程的集合,共享相同的资源限制
  • 层级结构(Hierarchy):控制组可以嵌套,形成树状结构

举个例子,当你执行:

docker run --memory=512m --cpus=1.5 nginx

Docker会在Cgroups文件系统里创建对应的控制组。你可以去/sys/fs/cgroup目录下看看:

# 查看Docker容器的Cgroups配置
ls /sys/fs/cgroup/memory/docker/<容器ID>/
cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.limit_in_bytes

输出结果会显示内存限制值,比如536870912(512MB)。这就是Cgroups在背后做的工作——内核根据这些配置,实时监控并限制进程的资源使用

小技巧:如果你想知道某个容器当前用了多少资源,可以直接读Cgroups文件:cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.usage_in_bytes,比docker stats更底层、更实时。

Cgroups v1 vs v2

这里有个坑,我得提醒大家。Linux内核有Cgroups v1和v2两个版本:

特性 Cgroups v1 Cgroups v2
子系统管理 每个子系统独立挂载 统一挂载,单一层级
资源控制 分散在不同目录 集中管理,更简洁
兼容性 老系统默认使用 新系统(Ubuntu 22.04+、CentOS 9+)默认使用

我曾经在升级内核后遇到过一个问题:容器启动报错,说找不到Cgroups挂载点。查了半天才发现,新内核默认启用了Cgroups v2,而Docker版本太老不支持。解决方案很简单——升级Docker到20.10以上版本,或者在内核启动参数里加上systemd.unified_cgroup_hierarchy=0回退到v1。

避坑指南:如果你在生产环境用Cgroups v2,一定要确认Docker版本≥20.10,并且containerd版本≥1.5。我曾经因为版本不匹配,导致资源限制完全失效,容器随便吃内存——那场面,简直灾难。

1.4 实战:快速验证Cgroups效果

光说不练假把式。咱们来做个实验,看看Cgroups到底怎么工作的。

先启动一个限制内存的容器:

docker run -d --name stress-test --memory=256m alpine sleep 3600

然后进入容器,安装stress工具:

docker exec -it stress-test sh
apk add stress

现在,尝试分配300MB内存:

stress --vm 1 --vm-bytes 300M --timeout 10s

你会看到什么?进程直接被OOM Killer干掉了!因为Cgroups限制了它最多只能用256MB。这就是资源限制在起作用——内核在背后默默守护着你的系统

我个人建议,在测试环境多试试这种极限场景。只有亲眼看到OOM Killer怎么工作的,你才会真正重视资源限制的配置。

1.5 小结

这一章咱们聊了:

  • 为什么需要资源限制——防止一个容器拖垮整个系统
  • Cgroups是什么——内核里的资源分组管理机制
  • Cgroups v1和v2的区别——新老版本要注意兼容性
  • 实战验证——亲眼看看资源限制怎么生效

下一章,我会带大家深入CPU资源限制的细节,包括CPU份额、CPU绑定、CPU周期这些参数到底怎么配。到时候咱们再聊,记得动手试试今天的实验。