原创

Docker 到底是如何将资源进行隔离的?

Docker 不同于虚拟机,每次我们使用docker run启动一个容器,其实容器只是作为一个进程运行在宿主机上,不过不同的是这个进程内部进行了一些特殊的改造。改造后的容器进程内部他能看到的外部环境已经不在是真实宿主机的环境,他看不到其他的进程,并且认为自己是这个环境中唯一的进程,这一切都是由 docker engin 操作,那么到底是如何实现的呢?接下来主要介绍一下 docker 的资源隔离机制。

容器资源的隔离

在 docker 中主要是用了两种技术实现了上述的资源的隔离。

  • Namespaces
  • Cgroups

Namespaces

准确来说应该叫 Linux namespaces,它是 Linux 内核提供的一个功能,能够使一组进程看到一组资源,另一组进程看到另一组资源。也就说在一组进程中他们依赖于同一个 namespaces 的资源,进程组中每个进程对于资源的引用也不同,所以也就将这些资源进行了分区。

简单来说 Namespaces 提供了一种抽象机制,将原本全局共享的资源隔离成不同的集合,集合中的成员独享其原本全局共享的资源。

Linux Namespaces 最初在 2.4.19 内核版本(2002)就已经引入,当时只有mount namespaces一种类型的 namespaces,由于早期 Linux 对 namespaces 的支持没那么全面,也就没有完全推动容器化的发展,大多数的 namespaces 支持是在内核 2.6 中完成的,比如 IPC、Network、PID、和 UTS。还有个别的 namespace 比较特殊,比如 User,从内核 2.6 就开始实现了,但在内核 3.8 中才宣布完成。同时,随着 Linux 自身的发展以及容器技术持续发展带来的需求,也会有新的 namespace 被支持,比如在内核 4.6 中就添加了 Cgroup namespace。在内核版本3.8中,引入了用户名称空间,从而完成了足够的容器支持功能。

以 pid namespaces 为例,它为进程提供了一组独立于其他的 namespaces 的 pid,在pid namespaces中创建的第一个进程被分配了进程ID号1(操作系统会创建进程号为1的init进程,它没有父进程也不会退出,可以收养系统的孤儿进程。相关介绍),并接受了与普通进程相同的大部分特殊处理,最明显的是,namespaces 中的孤立进程被附加到了该进程上。这也意味着终止这个pid 1进程将立即终止其pid namespaces 中的所有进程和任何子进程。

其他的 namespaces 也都是类似与 pid namespaces,本质上就是将宿主机上的一个进程的可见范围进行修改,docker 就是利用 namespaces 这个特性,实现了容器之间的资源隔离。本质上来看,每一个 docker 容器就是宿主机进程,不同 docker 容器就对应不同的宿主机进程,这样,不同容器(即不同进程)就可以采用 namespaces 资源隔离,使得每一个容器看起来都像是一个独立的小虚拟机。

Cgroups

Cgroups(Control Groups),同样也是 Linux 内核提供的功能,其主要功能就是对每一个进程组的资源使用进行限制,包括 cpu,disk,memory等,同时还提供了对资源使用的统计,以及进程组的控制。

前边提到,namespaces 技术能够对进程组的可见范围进行修改,使其看起来就想自己在使用宿主机的所有资源,但 namespaces 并不能对资源使用进行限制。这样在多个 namespaces 的情况下就会出现资源抢占,最终导致宿主机的资源枯竭以致机器宕机,这时使用 Cgroups 就能够避免这样的情况发生。

Linux CGroup 的详细介绍可以参考以下。

Docker基础技术:Linux CGroup | | 酷 壳 - CoolShell
cgroups - Wikipedia
linux/cgroup-v2.rst at master · torvalds/linux · GitHub

sev7e0
Write by sev7e0
end
本文目录