容器的本质:特殊的进程
进程是程序和其运行所需要的计算机环境的总和,是计算机进行资源分配和调度的独立单位。 而容器则是一种特殊的进程,它被内核隔离在独立的命名空间下,享有独立的网络、磁盘、文件系统,并且被限制了执行所需要的资源(如cpu 内存等)。
容器同宿主机上其他进程一样, 同其他进程共享宿主机的内核,并接受内核的调度。
容器与虚拟机的区别,如下图所示:
图左画出了虚拟机的工作原理。Hypervisor通过硬件虚拟化功能,模拟出了运行一个操作系统需要的各种硬件,比如 CPU、内存、I/O 设备等等。然后,它在这些虚拟的硬件上安装了一个新的操作系统,即 Guest OS。
图右中并不存在一个Hypervisor层模拟各个硬件,它仅仅只是对容器进程的运行环境进行了限制,但仍然使用了宿主机的内核。
因此此图描述的不够严谨,系统中不存在真实运行的各个容器进程,这只不过是障眼法。
更严谨的应该如下图:
此图中,容器进程直接运行在宿主机上,被宿主机内核管理,docker仅仅起到旁路辅助和管理工作。隔离和限制:Namespace和Cgroup
隔离
docker使用 Linux Namespace将容器进程进行隔离,容器享有独立的进程空间,因此在容器内部使用ps
查看,会发现运行的进程id为1,而宿主机的其他资源不再可见。
docker 使用linux内核提供的namespace进行隔离,相比虚拟机,拥有着启动速度快
和高性能
的优点。
但也有其缺点,那就是隔离的不够彻底,有如下问题:
- 由于共享内核,无法在window宿主机上运行linux容器、或者在低版本linux宿主机上运行高版本linux容器
- 某些资源是全局资源,无法被namespace, 如时间。 若在一个容器内对时间进行修改, 则其他容器及宿主机其他进程也会生效。
限制
docker使用Linux Cgroup对容器进程进行资源限制,限制的资源包括:cpu、内存、磁盘、网络带宽等。
因为容器也是进程,只有对容器进行资源限制,才能避免容器占用过多宿主机资源,从而影响其他容器、宿主机其他进程的正常工作。
容器镜像原理:rootfs
通过上面的描述,我们可以对容器有一个准确的定义:容器是一种使用namespace和cgroup进行隔离和限制的进程。
那容器内访问的文件系统是如何工作的呢?
答: 通过Mount Namespace将文件系统挂载为容器进程的根文件系统。
docker 运行的核心原理如下:
- 启用 Linux Namespace配置
- 设置指定的Cgroup
- 切换进程的根目录。
需要注意的是ootfs 只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。在 Linux 操作系统中,这两部分是分开存放的,操作系统只有在开机启动时才会加载指定版本的内核镜像。
正是由于 rootfs 的存在,容器才有了一个杀手锏: 一致性。
容器本质是进程,进程的本质是程序及其运行环境的组合,这里的运行环境不单单只java版本、类库版本等,还包括操作系统版本等。
而rootfs将除linux内核外所有依赖环境进行打包,从而保证了软件开发、测试、部署、交付全流程的一致性。也因此docker改变了软件开发和交付的流程。