cgroups

cgroups 原来叫 Process Container 但因为叫 Container 有诸多的歧义,所以干脆就改叫 Control Groups 也就是 cgroups。cgroups 在 2016 年发布了 v2 版本,但本文只讨论 v1 版本的 cgroups,后续将会另外讨论 v2 版本。

定义

当前已有的 subsystem 分别是:blkio、cpu,cpuacct、cpuset、devices、freezer、hugetlb、memory、net_cls,net_prio、perf_event、pids、rdma、systemd

cgroup 提供了一个虚拟文件系统作为用户管理 cgroup 的接口,当前环境挂载到了 /sys/fs/cgroup 下

内存控制

根据 docker run --help 获取容器在运行时可以指定的若干个参数如下

      --kernel-memory string                  Kernel memory limit
  -m, --memory string                         Memory limit
      --memory-reservation string             Memory soft limit
      --memory-swap string                    Swap limit equal to memory plus swap: '-1' to enable unlimited swap
      --memory-swappiness int                 Tune container memory swappiness (0 to 100) (default -1)
      --oom-kill-disable                      Disable OOM Killer
      --oom-score-adj int                     Tune host's OOM preferences (-1000 to 1000)

Memory Limit

Docker 参数cgroup interface说明
-m, --memorymemory.limit_in_bytes内存使用量硬限制
--memory-reservationmemory.soft_limit_in_bytes内存使用量软限制
--oom-kill-disablememory.oom_controlOOM 策略控制
--kernel-memorymemory.kmem.limit_in_bytes内核内存使用限制

让我们来看看 memory.limit_in_bytes 下面存了什么好东西

[email protected]:/sys/fs/cgroup/memory/playgound# cat memory.limit_in_bytes
268435456

诶,原来就只是一串数字而已,这个值是以 bytes 为单位显示的 256M,它限制了这个 cgroup 下所有的进程使用的总内存只能在 256M 内。memory.soft_limit_in_bytes 下面存的也是类似这样的一个数值,不过应该小于或者等于 memory.limit_in_bytes。Soft limit 应该比 hard limit 要低,在宿主机内存压力大的时候内核将会尽量的将有配置 soft limit 的 cgroup 下的 task 所用的内存压到 soft limit 值以下(这是一个 best-effort 特性),让其他 cgroup 有机会获得内存。

再看看 memory.oom_control 下面有什么,这个像是个策略控制器

[email protected]:/sys/fs/cgroup/memory/playgound# cat memory.oom_control
oom_kill_disable 0
under_oom 0

这次不只是一个数字了,这还有 KV 来着。oom_kill_disable 有两个值,0(默认) -> OOM 的时候会 kill 掉进程,1 -> OOM 的时候会暂停进程。under_oom 代表了当前 cgroup 是否处于 OOM 状态,由 0 和 1 表示 true 跟 false。

--oom-kill-disable 参数将 memory.oom_control 中的 oom_kill_disable 修改为 1(即 OOM 的之后不会 kill 进程)

Memory Swap

Docker 参数cgroup interface说明
--memory-swapmemory.memsw.limit_in_bytesswap 分区限制量
--memory-swappinessmemory.swappinessswap 分区使用倾向比例(姑且这么叫)

--memory-swap 限制了内存和交换区的使用量总和,实际可用的 swap = memsw.limit_in_bytes - memory.limit_in_bytes

swappiness 取值范围是 0 ~ 100(默认 60),如果为 0 则只会在内存使用完后才使用 swap,否则就会按照给定的比例来适时的使用 swap 分区(具体怎么用,还需要再深入)

[email protected]:/sys/fs/cgroup/memory/kubepods/podaf0d8d80-0bc3-11e8-869d-c81f66d06721# cat memory.swappiness
60

CPU 控制

      --cpu-count int                  CPU count (Windows only)
      --cpu-percent int                CPU percent (Windows only)
      --cpu-period int                 Limit CPU CFS (Completely Fair Scheduler) period
      --cpu-quota int                  Limit CPU CFS (Completely Fair Scheduler) quota
      --cpu-rt-period int              Limit CPU real-time period in microseconds
      --cpu-rt-runtime int             Limit CPU real-time runtime in microseconds
  -c, --cpu-shares int                 CPU shares (relative weight)
      --cpus decimal                   Number of CPUs
      --cpuset-cpus string             CPUs in which to allow execution (0-3, 0,1)
      --cpuset-mems string             MEMs in which to allow execution (0-3, 0,1)

其中前两个是 Windows 系统专有参数,在此不做研究。

--cpu-shares 可以理解为是 CPU 抢占时的分配权重。假定当前有两个进程在同一个 cgroups 组下,A task 的 CPU share 是 50 而 B task 的是 25,当同时对于 CPU 资源争抢时,A 可以得到两倍于 B 的资源。在资源空闲期间,A 或者 B 都可以获得所有 CPU 资源。

[email protected]:/sys/fs/cgroup/cpu/kubepods/pod0bb631a0-f373-11e7-8cf2-c81f66d06721# cat cpu.shares
1048

--cpu-period--cpu-quota 需要配合一起食用,使用 CFS Scheduling(完全公平调度)。前者设定了 CFS 调度的时间周期(默认值 100000,单位 微秒),后者定义了当前所在 cgroups 下属 task 可以使用的周期内的时间片(默认 -1 即不限制,单位微秒)

[email protected]:/sys/fs/cgroup/cpu/kubepods/pod0bb631a0-f373-11e7-8cf2-c81f66d06721# cat cpu.cfs_period_us
100000
[email protected]:/sys/fs/cgroup/cpu/kubepods/pod0bb631a0-f373-11e7-8cf2-c81f66d06721# cat cpu.cfs_quota_us
102400

以上示例的值 quota 超出了 period(前者是后者 1.024 倍),意味着在多核心的情况下,该 cgroup 组可以用掉 1.024 核心

--cpu-rt-period--cpu-rt-runtime 同样需要配合在一起,使用的是 Real-Time Scheduling。二者的定义与 CFS 中的 period 和 quota 差不多。[备注:还需要深入看下差异]

--cpus 在 Docker 1.3 后加入的参数,其本质是 --cpu-period--cpu-quota,例如 --cpus=3 那么等同于 --cpu-period=100000 --cpu-quota=150000

--cpuset-cpus 绑定了容器可以运行的 CPU 节点(并不排斥其他容器也用同样的 CPU 节点),支持 0-3(0 至 3,共 4 节点)和 0,1(0 和 1 核心,共 2 节点)的写法。默认所有节点

[email protected]:/sys/fs/cgroup/cpuset/kubepods/pod0bb631a0-f373-11e7-8cf2-c81f66d06721# cat cpuset.cpus
0-7

--cpuset-mems 尽在 NUMA 架构的 CPU 上生效,绑定 CPU 的内存节点。(性能有坑,慎重!NUMA 架构的 CPU -- 你真的用好了么?

[email protected]:/sys/fs/cgroup/cpuset/kubepods/pod0bb631a0-f373-11e7-8cf2-c81f66d06721# cat cpuset.mems
0-1

ref. libcontainer/cgroups/fs