浅谈 Cgroups V2

阅读数:127 2019 年 11 月 18 日 11:25

浅谈Cgroups V2

之前写过的一篇文章《浅谈 Cgroups》中对 Cgroups v1 进行了介绍,Cgroups 为容器实现虚拟化提供了基础。随着容器技术不断发展,Cgroups v1 中管理 controller 变得复杂起来。Cgroups v2 的出现简化了 Hierarchy,并在 kernel 4.5.0 版本成为正式特性。本文将从其产生的背景,相比 v1 具体有哪些变化等方面,对 Cgroups v2 进行介绍。

1 背景

之前写过一篇文章对 cgroup v1 进行了介绍,但是由于当前 k8s 使用 cephfs 进行数据存储,当多租户使用时,需要对 IO 进行限制。当前 cgroup v1 由于 memcg 与 blkio 没有协作,导致 buffer io 的 throttle 一直没有实现。并且 cgroup v1 在内核的实现一直比较混乱,其中主要的原因在于,cgroup 为了提供灵活性,允许进程可以属于多个 hierarchy 的不同的 group。但实际上,多个 hierarchy 并没有太大的用处,因为控制器 (controller) 只能属于一个 hierarchy。 所以在实际使用中,通常是每个 hierarchy 一个控制器。

这种多 hierarchy 除了增加代码的复杂度和理解困难外,并没有太大的用处。一方面,跟踪进程所有 controller 变得复杂;另外,各个 controller 之间也很难协同工作(因为 controller 可能属于不同的 hierarchy, 所以从 3.16 开始,内核开始转向单一层次(unified hierarchy)。并且实现了对 buffer io 的限制。

2 Cgroups v2 的变化

由于 Cgroups v1 存在的种种问题,Cgroups v2 将多 hierarchy 的方式变成了 unified hierarchy,并将所有的 controller 挂载到一个 unified hierarchy。

当前 kernel 没有移除 Cgroups v1 版本,允许 Cgroups v1 和 v2 两个版本共存。但是相同的 controller 不能同时 mount 到这两个不同的 Cgroup 版本中。

以下是 Cgroups v2 的五点改进:

  • Cgroups v2 中所有的 controller 都会被挂载到一个 unified hierarchy 下,不在存在像 v1 中允许不同的 controller 挂载到不同的 hierarchy 的情况
  • Proess 只能绑定到 cgroup 的根 (“/“) 目录和 cgroup 目录树中的叶子节点
  • 通过 cgroup.controllers 和 cgroup.subtree_control 指定哪些 controller 可以被使用
  • v1 版本中的 task 文件和 cpuset controller 中的 cgroup.clone_children 文件被移除
  • 当 cgroup 为空时的通知机制得到改进,通过 cgroup.events 文件通知

3 unified hierarchy

在 Cgroups v1 允许将不同的 controller 挂载到不同的 hierarchies 虽然很灵活,但实际上这种方式对于使用者来说是没有必要的。因此在 Cgroups v2 版本中,将所有的 controller 都挂载到一个 hierarchies。

可以使用下面命令将 Cgroups v2 挂载到文件系统,并且所有可用的 controller 会自动被挂载进去。

复制代码
mount -t cgroup2 none $MOUNT_POINT

一个 contoller 无法在 Cgroups v1 和 v2 中同时使用,如果想在 Cgroups v2 使用已经被 Cgroups v1 使用的 controller,则需要先将其从 Cgroups v1 中 umount 掉。

要注意的是,系统启动时,systemd 默认使用 Cgroups v1,并将可以使用的 controller mount 到 /sys/fs/cgroup。如果想在系统启动时,把 Cgroups v1 关掉,可以在 /etc/default/grub 文件下修改 kernel 参数, 增加 GRUB_CMDLINE_LINUX_DEFAULT=“cgroup_no_v1=all”。(all 表示关闭所有的 controller,如果想关闭指定的 controller,则将 all 替换成你需要的 controller 名称,并已逗号分隔即可)。这样就可以在 Cgroups v2 中使用你想要的 controller 了。

4 controllers

当前 cgroup v2 支持以下 controller:

  • io (since Linux 4.5)
  • memory (since Linux 4.5)
  • pids (since Linux 4.5)
  • perf_event (since Linux 4.11)
  • rdma (since Linux 4.11)
  • cpu (since Linux 4.15)

5 subtree control

在 hierarchy 下的每一个 Cgroup 中都会包含如下两个文件:

  • cgroup.controllers
    这是一个 read-only 文件。包含了该 Cgroup 下所有可用的 controllers.

  • cgroup.subtree_control
    这个文件中包含了该 Cgroup 下已经被开启的 controllers。 并且 cgroup.subtree_control 中包含的 controllers 是 cgroup.controllers 文件 controller 的子集。

cgroup.subtree_control 文件内容格式如下,controller 之间使用空格间隔,前面用”+”表示启用, 使用”-“表示停用。比如下面的例子:

复制代码
echo '+pids -memory' > x/y/cgroup.subtree_control

Cgroups v2 的具体组织结构如下图所示:

浅谈Cgroups V2

6 “no internal processes” rule

与 Cgroups v1 不同, Cgroups v2 只能将进程绑定到叶子节点。因此不能绑定进程到任何一个已开启 controller 的任何 subgroup 中。

浅谈Cgroups V2

7 cgroup.events file

在 Cgroups v2 的实现中,也对获取 group empty 时获取通知的机制进行了优化。

Cgroups v1 使用 release_agent 和 notify_on_release 在 v2 中被移除。替代的是使用了 cgroup.events 文件。这是一个只读的文件,每行一个 key value 对,key 和 value 之间通过空格分割。

当前在这个文件中只含有一个 key 就是 populated,对应的 value 是 0。0 表示 cgroup 中没有 process,1 表示 cgroup 中包含 process。

8 cgroup.stat file

在 Cgroups v2 hierarchy 下的每个 group 都会包含一个只读文件 cgroup.stat。它的内容也是 key-value 的形式。当前这个文件中包含如下两个 key:

  • nr_descendants
    表示 cgroup 中存活的 subgroup 的数量
  • nr_dying_descendants
    表示 cgroup 中已经死亡的 cgroup 的数量

9 后代 Cgroups 数量限制

在 Cgroups v2 hierarchy 中还包含了两个用于查看和设置该 Cgroups 下的后代 Cgroups 数量的限制文件:

  • cgroup.max.depth (since Linux 4.14)
    这个文件定义子 cgroup 的最大深度。0 意味着不能创建 cgroup。如果尝试创建 cgroup,会报 EAGAIN 错误;max 表示没有限制,默认值是 max。

  • cgroup.max.descendants (since Linux 4.14)
    当前可以创建的活跃 cgroup 目录的最大数量,默认值”max”表示不限制。超过限制,返回 EAGAIN。

本文转载自公众号 360 云计算(ID:hulktalk)。

原文链接:

https://mp.weixin.qq.com/s/8Rr-hxKQyHpT7L-Zx7PkcA

评论

发布