LWN: Resource limits in user namespaces

关注了就能看到更多这么棒的文章哦~

Resource limits in user namespaces

By Jonathan Corbet
January 18, 2021
DeepL assisted translation
https://lwn.net/Articles/842842/

User namespaces (用户命名空间) 给 kernel 带来了许多有趣的挑战。它们让用户有了自己可以拥有对系统的完全控制的错觉,但其实仍然遵循在 namespace 之外的限制条件。Resource limites (资源限制,可以 man getrlimit 来了解) 就是实现限制的一种方案,看目前看来这种方案对一些用户来说似乎太过严格了。Alexey Gladkov 提出一组 patch set,试图通过一种不是很直接的方法来解决这个问题。

这个 patch set 中提到,以这种使用场景为例:某个用户希望运行一个肯定不会在 container(容器)内进行 fork 系统调用的服务程序。那么用户可以将进程数量的 resource limit 设置为 1 来对这个服务程序施加限制,明确组织 fork 新进程。但这个限制是全局性的,所以如果这个用户试图在两个 container 里面都运行这个服务程序,第二个 container 就会因为违背了 rlimit 限制而无法成功启动。结果,我们的用户很难过,考虑去放羊了。

很明显,我们需要一种方法,使得一些 resource limit 能适用于每个 container;然后每个 container 都可以在 process limit 设置为 1 的情况下仍能正常运行这个服务程序,每个人都会很高兴(这下没人放羊摸鱼了)。我们可以很容易想出几种方法来实现这个目标:

  • 将全局性的 resource limit(其中很多都是 per-process 的资源限制了)变成可以在 user namespace 之内设置的 limit。全局的 limit 仍然适用,但可以在 namespace 之内设置更低的 limit 值,以达到预期的效果。

  • 创建一个新的 cgroup,按照多层级的方式来管理 resource limit。毕竟这种类型的控制正是创建 cgroup 的目的。

不过,Gladkov 的 patch set 没有采取这些方法。相反,这个 patch set 将一些全局资源的使用计数器(进程、等待处理的 signal、内存中 locked page、消息队列中的字节数)都移到了与 user namespace 相关联的 ucounts 结构中。从而这些资源使用情况的统计数据成为每个命名空间内独立的了。

user namespace 都是按层级排列来排列的,最根部的就是 "initial namespace",每个 namespace 都分配了一个 ucounts 结构。resource-usage counts 统计计数的管理可以按照层级一直追溯。因此,如果某个进程在一个 user namespace 中创建了一个新的进程,那么这个 namespace 中的进程计数就会递增,但更高级别的 namespace 的计数也会递增。resource limit (仍然是全局的) 在层次结构中的每一级都会检查,任何一级只要超过了 limit 都会阻止这个操作的进行。

像编者这样思考速度慢、缺乏咖啡因刺激的人,就会疑惑这个改动是怎么能工作的。是的,现在每个 user namespace 都会有自己的 resource 统计计数,比如进程的数量统计。如果全局性的限制设置为 1 了,那么每个 user namespace 里面都可以包含一个进程,就不会超过这一层的 limit。但是统计计数会逐层传播,如果两个 namespace 都有一个共同的 parent namespace,那么就会在该级别上超过 limit,用户看来并没有得到什么好处。

看看 patch set 中提供的测试代码,就明白了。在这个测试程序中,"server" 进程是由 root 创建的,之后再更改 user ID 和 group ID,并转移到一个单独的 user namespace 中。因此,parent namespace 属于 root,并不会受改动过的 user ID 之下那些 resource limit 的限制。只要自己的使用场景中符合这种模式,就可以正常工作。

不过,有人可能会问,为什么不采取其他方法呢?把 limit 向下传播(而不是把 counter 向上传播)似乎也能解决这个问题,而且更加灵活,不需要 root 权限。事实上,Linus Torvalds 针对之前版本的 patch set 进行 review 时就问过为什么不采用这种方法。Eric Biederman 回答说,向下传播 limit 的方法 "也应该能够工作",随后又重复讲了一下使用场景,但没有真正澄清为什么需要使用传播 counter 而不是 limit 的方法。

早在 2015 年就讨论过使用 cgroup 来解决这个问题。当时,cgroup 的维护者 Tejun Heo 拒绝了这个想法,称其 "相当愚蠢"。他解释说:

一般来说,我强烈反对为系统中不属于基础资源的东西添加 cgroup。否则的话,下一步会要管理什么?打开了多少文件?使用了多少 pipe buffer?flock 的使用次数?session leader 或者 program group(进程组)?

如果你想避免某一类工作耗尽了某项资源,保护这个 resource 才是显而易见的事情。

那次讨论从这里开始就不再活跃了,但这个讨论结果哪怕时间过去这么久也一直被坚守着:cgroup 不会用于控制 container 内的 resource limit。

对于现在面临这个问题的用户来说,唯一的明显解决方案就是 Gladkov 的 patch set。然而,这些 patch 是否能被合并,将取决于内核社区的其他成员是否认为这种方法是正确的。还没有人讨论到这一点,可能首先需要有人能发布对于这个改动的机制和动机的更清晰的描述。container 内的 resource limit 是一个多年来一直没有解决的问题,可能还需要不少时间才能得到真正的解决方案。

Update:正如LWN读者评论中所解释的那样,resource limit 已经是 per-process 的了,所以不必再做什么改动就可以使它们能在每个 container 的内独立进行调整。不过,用于执行这些 limit 的统计计数值是全局的,这会导致上面提到的问题。因此,这个解决方案——使统计计数成为本地维护的,同时仍将它们向上汇总——似乎是个合理的方案。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值