9. Docker——容器深入理解
本章讲解知识点
- 说说进程
- 隔离与限制
<br>
1. 说说进程
1.1 引子
容器技术本质上是一种类似于集装箱的沙盒技术。它的作用就像是将你的应用程序装在一个集装箱里一样。这样一来,不同应用程序之间就有了明确的边界,避免了相互干扰的情况。而且,由于应用程序被装在集装箱里,因此它们可以轻松地被移植到其他环境中,这正是 PaaS 追求的最佳状态。
我们先说说边界是如何实现的。
当我们一个代码被编译成可执行文件,再为它提供数据,这些数据加上二进制文件,放在磁盘上,就是我们平常所说的一个“程序”,也叫代码的可执行镜像(executable image)。
一旦“程序”被执行起来,它就从磁盘上的二进制文件,变成了计算机内存中的数据、寄存器里的值、堆栈中的指令、被打开的文件,以及各种设备的状态信息的一个集合。这样的一个集合被称为进程。
进程在静态状态下表现为程序,通常静静地存储在磁盘上;但一旦开始运行,它就会转变为计算机内部数据和状态的集合,这就是它的动态表现。
容器技术的核心功能在于通过限制和调整进程的动态表现,为其创建一个明确的“边界”。在 Docker 等大多数 Linux 容器中,Cgroups 技术是制造限制的主要手段,而 Namespace 技术则是调整进程视图的主要方式。
1.2 小实验
我们创建容器并执行 bash shell
$ docker run -it busybox /bin/sh / #
这样,我们启动了一个容器,在容器里执行 /bin/sh
,并且给我们分配了一个命令行终端跟这个容器交互。
让我们 ps 一下:
/ # ps PID USER TIME COMMAND 1 root 0:00 /bin/sh 10 root 0:00 ps
在 Docker 中,最初执行的 /bin/sh
是容器内的第一个进程(PID=1),而该容器只运行了两个进程。这意味着之前执行的 /bin/sh
和刚刚执行的 ps 已被 Docker 隔离到与主机完全不同的世界中。
这是一个很有意思的现象,因为我们知道,我们的整个宿主机可不止这么点进程。但是 Docker 似乎做了一些手脚,让 /bin/sh
以为自己是 1 号进程,放在宿主机上,它本不应该是 1 号进程。
Linux 中的 Namespace 机制可以实现这种“手脚”。Namespace 的使用方法非常简单:它只是 Linux 中创建新进程的一个可选参数。我们知道,在 Linux 系统中,创建进程的系统调用是 clone(),例如:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
这个系统调用就会为我们创建一个新的进程,并且返回它的进程号 pid。而当我们用 clone() 系统调用创建一个新进程时,就可以在参数中指定 CLONE_NEWPID 参数,比如:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
这时,新创建的这个进程将会“看到”一个全新的进程空间,在这个进程空间里,它的 PID 是 1。之所以说“看到”,是因为这只是一个“障眼法”,在宿主机真实的进程空间里,这个进程的 PID 还是真实的数值,比如 1000。
而除了我们刚刚用到的 PID Namespace,Linux 操作系统还提供了 Mount、UTS、IPC、Network 和 User 这些 Namespace,用来对各种不同的进程上下文进行“障眼法”操作。比如,Mount Namespace,用于让被隔离进程只看到当前 Namespace 里的挂载点信息;Network Namespace,用于让被隔离进程看到当前 Namespace 里的网络设备和配置。这就是 Linux 容器最基本的实现原理了,容器其实是一种特殊的进程而已,这是很重要的一个结论。
<br>
2. 隔离与限制
1. 基本概念
Namespace 技术改变了应用进程对整个计算机的视图,即它们的“视野”被操作系统限制,只能看到特定的内容。然而,对于宿主机来说,这些被隔离的进程与其他进程相比并没有太大的区别。这就是我们上一节讲的“隔离”。
这一节,我们来讲讲“限制”。
虽然容器内的第一个进程在“隔离”的作用下只能看到容器内的情况,但在宿主机上,该进程作为第 1000 个进程与其他进程之间仍然存在公平竞争关系。这意味着,虽然该进程表面上被隔离,但它所能使用的资源(如 CPU、内存)可以被宿主机上的其他进程(或容器)随时占用。当然,该进程本身也可能占用所有可用资源。这样就有点不太合理了。
因此容器使用的第二个重要特性就是:Linux Cgroups。 Linux Cgroups(Linux Control Group) 是 Linux 内核中用来为进程设置资源限制的一个重要功能。主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup
路径下。我们可以查看一下,输出结果是一系列文件系统目录:
$ mount -t cgroup cpuset on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cpu on /sys/fs/cgroup/cpu type cgroup (rw,nosuid,nodev,noexec,relatime,cpu) cpuacct on /sys/fs/cgroup/cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct) blkio on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) memory on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) ...
在 目录下,有许多名为 cpuset、cpu 和 memory 的子目录,这些目录被称为子系
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专刊适合于立志转行云计算的小白,有一定的编程、操作系统、计算机网络、数据结构、算法基础。 本专刊同时也适合于面向云计算(Docker + Kubernetes)求职的从业者。 本专刊囊括了云计算、VMWare、Docker、Kubernetes、Containerd等一系列知识点的讲解,并且最后总