容器原理简介
容器是一种沙盒技术。
容器的作用
之前我们部署程序时对环境可能要做一系列的配置,这样才能保证程序的正常运行;有了容器之后,它像一个沙箱一样,将我们的程序包起来,避免外部环境对影响容器内程序的正常运行,程序所需要的环境在沙箱内已经配置好,容器镜像中包含了应用程序所需的整个操作系统的文件和目录,应用程序的运行环境在操作系统级别达到了一致性。无论将沙箱放到哪个环境,内部程序都可以正常运行。
容器基本原理
容器类似于沙箱一样,将应用程序包裹在沙箱内。其中的技术手段主要是Namespace和CGroup
Namespace
在容器内,我们只能看到这个容器所拥有的的资源,宿主机上的其他资源我们在容器内是看不到的。不准确的来说,容器就像一个迷你版的服务器。但是在宿主机上来看,容器只是一个普通的进程。
通过NameSpace技术修改了程序看待这个计算机的视图,达到上述目。NameSpace信息是创建进程的一组参数,通过NameSpace可以对进程的 PID,NetWork,User,Mount,UTS,IPC等资源视图进行限制。
但是实际上塔还是运行在宿主机上的一个普通进程,除了Namespace这组参数外,有些资源是不能被Namespace化的。比如时间,容器内程序通过系统调用修改了时间之后,整个计算机的时间就被修改了。
容器技术只是对进程进行了简单的隔离,所以在容器内进行操作时就要注意哪些能做,哪些不能做。
还有如果程序依赖系统内核的某些功能,如果宿主机不满足程序要求,则程序的这部分功能就会受到影响。
CGroup
利用CGroup技术可以限制容器使用的资源,比如CPU,内存,显存,磁盘空间,网络带宽等资源。
它暴露出的接口是文件系统,可以在/sys/fs/cgroup下看到这些文件,通过文件内的信息对进程的资源进行限制。
Mount Namespace
Mount让容器进程可以只看到容器内的文件。
进程在创建时,默认会继承宿主机的各个挂载点。进程对文件系统的视图和宿主机是一样的。
容器进程在创建前,容器镜像(根文件系统)挂载到进程的根目录(/)下,这样容器进程看到的就是镜像内的文件系统了。Linux中可以通过chroot命令实现这个功能,Mount NameSpace 也是基于chroot被创造的,
Mount Namespace 可以修改进程对挂载点的认知,在挂载操作后,进程的对该挂载点的视图就被修改了。在进行挂载操作前,进程看到的还是宿主机上该挂载点的视图。
挂载
挂载操作利用的是Linux的bind mount(绑定挂载) 机制。挂载后,在挂载点上的操作只发生在被挂载的文件,原挂载点的文件被隐藏起来,不受影响。
绑定挂载的实际上是一个inode的替换过程,将宿主机的/data目录挂载到/test的目录,实际上是将/test的inode的指针指向了/home的inode的指针;
Volume
Volume 机制让容器可以看到宿主机的文件,并进行更新。
实现原理就是在容器进行chroot(挂载根文件系统)操作之前,此时进程可以看到宿主机的文件系统,此时将volume指定的宿主机目录挂载到指定的容器目录,volume的挂载操作就完成了。
容器的单进程模型
容器本质上就是一个进程。
容器的“单进程模型”,并不是指容器里只能运行“一个”进程,而是指容器没有管理多个进程的能力。这是因为容器里 PID=1 的进程就是应用本身,其他的进程都是这个 PID=1 进程的子进程。
所以我们在 docker run 容器时,一般将启动命令或脚本常驻在内存,否则启动命令或脚本执行完成后,这个容器也将退出。
容器在设计时,本身也希望和应用程序同生命周期,这样应用程序结束时容器也随之结束,可以从容器的生命周期判断应用程序的生命周期。反之,如果程序已经结束,容器还处于运行状态,这对于容器的调度增加了复杂度。