由浅入深 docker 系列: (5) 资源隔离

上篇文章说了容器只是宿主机中的一个用户进程,和虚拟机完全不同,那么,为什么在容器内看不到宿主机进程,容器又是如何实现进程、文件、网络等资源的隔离呢?

这就牵涉到今天要介绍的 linux 内核的 Namespace 和 Cgroups 特性了。

1.Namespace

Linux 内核从版本 2.4.19 开始陆续引入了 Namespace 的概念。其目的是将某个特定的全局系统资源通过抽象方法使得 namespace 中的进程看起来拥有它们自己的隔离的全局系统资源实例。Linux 内核中实现了六种 Namespace,按照引入的先后顺序,列表如下:

例如,容器进程启动时,只要启用了 Mount Namespace,并将自己打包的文件系统挂载好,就可以实现每个容器仅看到自己的文件,实现文件资源的隔离。总之,Docker 守护进程创建容器实例时都启用了相应的 Namespace,使得容器中的进程都处于一种隔离的运行环境之中。

那么如何启用相应 Namespace 呢?

通过系统调用 clone () 来创建一个具有独立 Namespace 的进程是最常见的做法,它可以通过 flags 参数传入相应标志位来控制进程的各种状态,如以下示意代码:

docker run 中 namespace 相关参数

  • –ipc string IPC namespace to use
  • –pid string PID namespace to use
  • –userns string User namespace to use
  • –uts string UTS namespace to use

你可以在容器启动的时候,指定这些参数,从而强制容器运行在特定 namespace 之中。例如,你可以指定 –pid host,从而让容器进程使用宿主机进程空间,此时容器可以看到 host 上所有的进程(想象这样一个场景,你把常用的性能诊断工具都打包到一个镜像中,然后必要的时候在服务器上使用此镜像进行问题分析,此时加上该参数会很方便)。

2.Cgroups

通过 Namespace,容器实现了资源的隔离,从而每个容器看起来都像是拥有自己独立的运行环境。注意,只是看起来。因为容器使用 cpu、内存等并不受限制,假如某个容器占用这些资源过高,就可能会造成其它容器运行迟缓甚至异常,这就需要 Cgroups 了。

cgroups 的全称是 control groups,是 Linux 内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 cpu,内存等资源实现精细化的控制。

其典型的子系统如下:

  • cpu 子系统,主要限制进程的 cpu 使用率。
  • cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
  • cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  • memory 子系统,可以限制进程的 memory 使用量。
  • blkio 子系统,可以限制进程的块设备 io。
  • devices 子系统,可以控制进程能够访问某些设备。
  • net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  • freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
  • ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。

而 Cgroups 的实现也很有意思,它并不是一组系统调用,linux 将其实现为了文件系统,这很符合 Unix 一切皆文件的哲学,因此我们可以直接查看。

例如,我在 ubuntu18.04 系统中,直接执行 mount -t cgroup 即可看到,系统已经自动在 sys/fs/cgroup 目录下挂载好了相应文件,每个文夹件代表了上面所讲的某种资源类型。

mount -t cgroup

我们可以查看 sys/fs/cgroup/cpu 文件夹下的文件,它代表对 cpu 资源的控制,其中 tasks 文件中是我们系统的进程 pid,表示对这些进程进行资源控制,其它文件如 cpu.cfs_quota_us 表示 cpu 的利用率,默认值为 – 1,表示不做限制。

Cgroup/cpu

如何使用 Cgroups 呢

很简单,我们可以直接在相应资源控制组目录下创建文件夹,系统会自动创建需要的文件,例如,在上述 cpu 目录下创建 hello 目录,然后看到相应文件已自动创建。

cgroup/cpu/hello

 然后我们写一个死循环,代码如下,然后编译执行

执行 top 命令,很容易发现,cpu 使用率达到了 100% 左右,怎么办呢?

进入刚才创建的 hello 目录,将 cpu.cfs_quota_us 的值改为 20000(此参数表示 1 秒周期内进程使用 cpu 的最大微秒数,因此 20000 表示 20%),然后将 deadLoop 进程的 Pid 写入 tasks 文件中去,再次执行 top 命令,你将看到 cpu 使用率只有 20% 了!

命令如下:

很直观吧,其它资源的控制也与此类似。

docker 对 Cgroups 的使用

默认情况下,docker 启动一个容器后,就会在 /sys/fs/cgroup 目录下的各个资源目录下生成以容器 ID 为名字的目录,在容器被 stopped 后,该目录被删除。那么,对容器资源进行控制的方式,就同上边的例子一样,显而易见了。

至于 docker run 提供的 Cgroups 相关参数,就请你自己查阅文档吧。

3. 其它

本篇简单介绍了下 Linux 内核的 Namespace 和 Cgroups 功能,从而理解容器中资源的隔离和控制技术的实现原理,主要是帮助你建立基本的概念,而关于这些功能的使用细节以及内核中的实现原理,还很复杂,想了解的话还需要你投入大量精力。

另外,我们也可以看到,docker 并不是全新的技术,它是对很多已有技术的封装、抽象,只要深入的学习下去,你就会看到很多熟悉的东西,你对容器的理解也会越来越深刻,加油!

参考资料:

Docker 背后的内核知识 ——Namespace 资源隔离 – InfoQ

理解 Docker(4):Docker 容器使用 cgroups 限制资源使用

« »

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *