什么是进程?

一旦“程序”被执行起来,它就从磁盘上的二进制文件,变成了计算机内存中的数据、寄存器里的值、堆栈中的指令、被打开的文件,以及各种设备的状态信息的一个集合。像这样一个程序运行起来后的计算机执行环境的总和,称为进程。

边界

容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”。
Namespace 技术是用来修改进程视图的主要方法,可以实现容器的资源隔离;Cgroups 技术是用来制造约束的主要手段,可以对容器使用的资源进行限制。

常用namespace

Linux 操作系统还提供了 Mount、UTS、IPC、Network 和 User 这些 Namespace,用来对各种不同的进程上下文进行“障眼法”操作。

  • mount Namespace,用于让被隔离进程只看到当前 Namespace 里的挂载点信息
  • network Namespace,用于让被隔离进程看到当前 Namespace 里的网络设备和配置
  • pid namespace,进程隔离
  • ipc namespace,进程间通信隔离
  • user namespace,独立的用户、用户组
  • uts namespace,独立的hostname、domain name

docker 容器

Docker 容器实际上是在创建容器进程时,指定了这个进程所需要启用的一组 Namespace 参数。这样,容器就只能“看”到当前 Namespace 所限定的资源、文件、设备、状态,或者配置。

VM与容器对比

Hypervisor 的软件是虚拟机最主要的部分。它通过硬件虚拟化功能,模拟出了运行一个操作系统需要的各种硬件,比如 CPU、内存、I/O 设备等等。然后,它在这些虚拟的硬件上安装了一个新的操作系统,即 Guest OS。这样,用户的应用进程就可以运行在这个虚拟的机器中,它能看到的自然也只有 Guest OS 的文件和目录,以及这个机器里的虚拟设备。

Docker Engine 的软件替换了 Hypervisor。因此,很多人会把 Docker 项目称为“轻量级”虚拟化技术的原因,实际上就是把虚拟机的概念套在了容器上。docker engine是一个用来创建和管理容器”进程”的守护进程,容器并不是像VM一样运行在docker engine之上。容器中运行的进程本身不受docker控制,docker只是作为容器资源的管理。
namespace 和 cgroup 是内核特性,容器本质上就是一个加了限定参数的普通进程。故容器的安全性,也就是隔离性,是没办法达到虚拟机的级别。

进程隔离 - 图1

对比

根据实验,一个运行着 CentOS 的 KVM 虚拟机启动后,在不做优化的情况下,虚拟机自己就需要占用 100~200 MB 内存。此外,用户应用运行在虚拟机里面,它对宿主机操作系统的调用就不可避免地要经过虚拟化软件的拦截和处理,这本身又是一层性能损耗,尤其对计算资源、网络和磁盘 I/O 的损耗非常大。

而相比之下,容器化后的用户应用,却依然还是一个宿主机上的普通进程,这就意味着这些因为虚拟化而带来的性能损耗都是不存在的;而另一方面,使用 Namespace 作为隔离手段的容器并不需要单独的 Guest OS,这就使得容器额外的资源占用几乎可以忽略不计。

总结

  • “敏捷”和“高性能”是容器相较于虚拟机最大的优势。
  • 基于 Linux Namespace 的隔离机制相比于虚拟化技术也有很多不足之处,其中最主要的问题就是:隔离得不彻底
  • 在 Linux 内核中,有很多资源和对象是不能被 Namespace 化的,最典型的例子就是:时间。这就意味着,如果你的容器中的程序使用 settimeofday(2) 系统调用修改了时间,整个宿主机的时间都会被随之修改,这显然不符合用户的预期。

    补充

  1. docker虽然宣称打包一次到处跑,实际上基于4.x内核的程序在3.x是跑不了的。
  2. 通过命令行获取自己现在是在container里面还是在外面的方法,比如查看/proc/1/cgroup下的文件结构。pids:/init.scope表示容器外。