
容器其实是一种沙盒技术。顾名思义,沙盒就是能够像一个集装箱一样,把你的应用“装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去。
但要用技术手段去实现它们,我们该如何下手呢?
一、Linux容器实现原理
1.1、Linux 容器最基本的实现原理
首先,容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”。
而Namespace 技术给进程、文件、网络施一个“障眼法”,让把进程隔离成一个独立的空间。
所以说,容器,其实是一种特殊的进程而已。
比如通过PID Namespace,让其认为自己是PID=1从而与宿主机上别的进程分隔开;通过Mount Namespace,让被隔离进程只看到当前 Namespace 里的挂载点信息,并通过重新挂载的方式让进程只能看到一个空的目录,从而让容器的文件与宿主机文件分隔开;通过Network Namespace,让被隔离进程看到当前 Namespace 里的网络设备和配置,从而将容器中网络与宿主机网络分隔开。
1.2、容器技术原理:隔离与限制
虚拟机需要启动一个真实的虚拟机和运行一个完整的 Guest OS 才能执行用户的应用进程;而容器跟宿主机上的其他进程一样,都由宿主机操作系统统一管理,这就意味着这些因为虚拟化而带来的性能损耗都是不存在的;而另一方面,使用 Namespace 作为隔离手段的容器并不需要单独的 Guest OS,这就使得容器额外的资源占用几乎可以忽略不计。
但是,基于 Linux Namespace 的隔离机制相比于虚拟化技术也有很多不足之处,其中最主要的问题就是:隔离得不彻底。
因为容器只是运行在宿主机上的一种特殊的进程,即使使用Namespace对其进行进程、文件、网络等隔离,但是多个容器却共享同一个宿主机的操作系统内核,因此,应用“越狱”的难度自然也比虚拟机低得多。
另外,由于容器是宿主机上的一个进程,那么其与宿主机上其他所有进程之间依然是平等的竞争关系,因此为了避免容器消耗过多的宿主机资源,我们可以使用Cgroups对容器进行cpu、内存等限制。
详情参看:容器技术原理:隔离与限制
1.3、容器技术原理:文件系统
Namespace 和 Cgroups 做了容器的资源限制与隔离,把进程“装”在了一个与世隔绝的房间里。这个房间四周虽然有了墙,但是如果容器进程低头一看地面(文件系统),又是怎样一副景象呢?
Mount Namespace负责让被隔离的进程只能看到当前Namespace里挂载目录的文件。并且可以重新挂载指定目录,让挂载的宿主机目录在容器中是空的目录。避免在容器中访问到宿主机的文件。
chroot负责把任意目录变为根目录,这样容器就能挂载宿主机上任意目录当做根目录了
rootfs负责将系统文件挂载到容器的根目录
rootfs 只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。同一台机器上的所有容器,都共享宿主机操作系统的内核。
Union File System 也叫 UnionFS,最主要的功能是将多个不同位置的目录联合挂载(union mount)到同一个目录下。(相同的文件进行复用)
详情参看:容器技术原理:文件系统
1.4、容器技术原理:网络
新创建的 namespace 默认不能和主机网络,以及其他 namespace 通信。Network Namespace是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自的网络栈信息。
虚拟机网络模式有三种:桥接模式(bridge)、NAT模式、仅主机模式
详情参看:容器技术原理:网络
这样,在一个宿主机上的普通进程,通过PID Namespace被隔离成单独的进程、通过Mount Namespace+chroot让其拥有隔离的空的根目录、通过rootfs负责将系统文件挂载到根目录、通过UnionFS进行不同容器间文件的复用、通过Cgroups对容器资源的限制、通过Network Namespace实现网络的隔离,从而形成一个特殊的进程(容器)
二、Docker容器对Linux容器技术的应用
2.1、容器技术原理:Docker容器技术原理
rootfs 的基础上,Docker 公司结合UnionFS创新性地提出了使用多个增量 rootfs 联合挂载一个完整 rootfs 的方案,这就是容器镜像中“层”的概念。(分层镜像)

我们可以看到:
(1)这个容器进程“python app.py”,运行在由 Linux Namespace 和 Cgroups 构成的隔离环境里;
(2)而它运行所需要的各种文件,比如 python,app.py,以及整个操作系统文件,则由多个联合挂载在一起的 rootfs 层提供。
(3)DockerFile中的每一步,都是一层,组成了只读层;
(4)只读层 + Init层 + 可读写层 = rootfs;
安装 Docker 以后,会默认创建三种网络(bridge、host、none),可以通过 docker network ls 查看。当你运行一个容器的时候,可以使用–network参数来指定。
详情参看:容器技术原理:Docker容器技术原理
2.2、容器技术原理:从原理层面解释Docker日常使用中遇到的问题
通过以上对容器原理的了解,我们可以解决很多日常操作时遇到的问题,比如:为什么实际占用空间比每个镜像的总和要小?为什么在写DockerFile的时候不能有两个CMD?为什么要做资源限制?为什么在arm上打的镜像在x86上不能用
详情参看:容器技术原理:从原理层面解释Docker日常使用中遇到的问题
好了,告一段落,你悟了吗?




