Docker容器镜像加载底层原理
脚丫先生 人气:2前言
回想上学的时候,某老师神采奕奕的讲解着操作系统某个知识点原理,可谓是激情澎湃,我环顾周围基友们,脸上懵逼两字清晰可见,毫不含糊,恍然大悟原来是在讲天书。为了不让大家脸上呈现懵逼二字,希望能以人能看懂的语言,让大家理解。
一、Docker run
这一小节,标题是Docker run ,我想受到过九年义务教育的大家,不难翻译吧,如果这都翻译不了,那么你一定要顺着网线爬过来找我,我一定把你脑壳打得开窍。
上一章节,最后我们用Docker启动了一个ningx的容器:
#运行容器 docker run --name nginx-container -p 80:80 -d nginx
浏览器输入虚拟机地址即可访问 Nginx
那么,docker run启动命令是干了什么,才生成了一个nginx容器的呢?带着疑问,我们接着说。
首先我们要很明白 运行容器的命令
nginx-contarner
:nginx容器的名称(容器相当于java中类生成的对象)nginx
:镜像(镜像相当于java中类)
(下图借鉴一位博主的,如有不合适,请我联系我删除)
运行容器命令,做了什么呢?首先Docker会去本机寻找镜像,如果没有找到,它会去DockerHub上去下载,如果能够找到并下载到本地就会使用这个镜像去执行,否则就会返回找不到该镜像的错误。
就好比,你在家里上厕所,需要纸,你就会在厕所里寻找卫生纸,如果找到了,就直接可以用了。但是,如果你没有找到,那就得叫老爸在家里找卫生纸,找到了你就可以安心笑了,没有找到的话,老爸就会告诉你家里没有卫生纸纸。这里的卫生纸就是所谓的镜像.
二、Docker底层原理
1、Docker是怎么工作的
我们知道Docker是一个客户端-服务端(C/S)架构。根据上节讲述的C/S架构运行模式就不难去理解。
Docker客户端只需要向Docker守护进程发出请求,Docker守护进程在接收到Docker客户端的请求后去执行针对容器的相应操作,完成所有工作后返回结果。(详细深入待后续)
守护进程和普通进程区别是指 : 将后台程序变成一种服务,比如说,用命令行输入启动程序,如果不是守护进程的话,一旦命令行窗口关闭,程序就终止了;而如果启动守护进程,则退出命令行窗口之后,服务一直处于运行状态。
2、为什么Docker比虚拟机快
Docker与虚拟机的对比,第一节文章就已经阐述过了。通过对比,我们就可以知道为什么Docker比虚拟机快。这里我在总结下:
1、容器由于没有了虚拟操作系统和虚拟机监视器这两个层次,大幅减少了应用程序运行带来的额外消耗。
2、Docker利用的是宿主机的内核,那么容器中的应用程序就是完全运行在了宿主操作系统中。因此在docker容器里运行应用程序,与在宿主机中运行应用程序效果是一样的。
三、Docker镜像
1、镜像
镜像是Docker容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。(这里可以去上一篇文章中,有对Docker镜像的形象比喻)
2、联合文件系统UnionFS
要想理解Docker镜像的加载原理,那么我们得要知道UnionFS(联合文件系统)
1、Union文件系统(UnionFS) 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
看这文绉绉的Union文件系统的描述,难免呈现懵逼二字,过于抽象。看着都没耐心继续往下看…理解不了
UnionFS 最主要的功能简单理解为把不同的目录合并成一个目录,原来目录下的内容路径都不会改变。比如,我现在有两个目录 A 和 B,它们分别有两个文件:
$ tree . ├── A │ ├── a │ └── x └── B ├── b └── x
然后,使用联合挂载的方式,将这两个目录挂载到一个公共的目录 C 上:
$ mkdir C $ mount -t aufs -o dirs=./A:./B none ./C
这时,再查看目录 C 的内容,就能看到目录 A 和 B 下的文件被合并到了一起:
$ tree ./C ./C ├── a ├── b └── x
可以看到,在这个合并后的目录 C 里,有 a、b、x 三个文件,并且 x 文件只有一份,同时合并后的目录对于原来的目录里文件的路径是不会改变的。这,就是“合并”的含义。
但是,大家应该会有个疑问,就是x文件到底是原先哪个目录下的。答案是谁先被挂载就显示成谁的,也就是说如果目录A先被挂载,那优先显示目录A里的x文件。(具体详细深入后面讲解)
3、Docker镜像的加载原理
Docker设计时,就充分利用Union FS的技术,将其设计为分层存储的架构。 镜像实际是由多层文件系统联合组成,这种层级的文件系统UnionFS,在内部又分为2部分:
1) bootfs(boot file system):docker镜像的最底层是bootfs,主要包含bootloader(加载器)和kernel(内核)。bootloader主要是引导加载kernel,linux刚启动时会加载bootfs文件系统。当bootfs加载完成后,整个内核就在内存中了,此时内存的使用权已由bootfs转交给了内核,此时系统也会卸载bootfs。
这里的加载,可以理解为,我们windows电脑开机时候,从黑屏到进入操作系统的过程。
2)rootfs(root file system):在bootfs之上,包含的就是典型linux系统中的/dev、/proc、/bin、/etc等标准目录和文件。不同的 Linux 发行版, boots 基本是一致的, rootfs 会有差別。
举个例子:如下图所示
以图为例,从左到右,分为3个过程:
1、docker镜像的最底层是bootfs,然后是一个Base Image的基础镜像,这个基础镜像是Centos。
2、执行安装mysql操作,那么Docker会在Centos基础镜像之上又加了一层mysql镜像。
3、执行安装tomcat,那么在之前的mysql镜像上在继续加一层tomcat镜像。
就像叠积目一样,一层一层往上。也说明了docker的镜像实际上是由层一层的文件系统组成的。对于不同的的linux发行版本,bootfs基本是一致的,rootfs会有差别,所以不同的发行版可以共用bootfs。
另外,平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?
因为底层直接用主机的内核,自己只需要提供rootfs就行了,所以rootfs可以很小,只需要包含最基本的命令、工具和程序库即可。这样一来,启动速度也快了,因为最浪费时间的引导加载过程没了。
4、Docker镜像分层理解
知道了镜像的加载原理,我们在来看看Docker镜像的分层理解。
首先可以去下载一个reids 镜像,注意观察下载的日志输出,可以看到是一层层的在下载。
当下载的层文件与我们之前层文件有冲突,也就是下载过的文件就会显示Already exists不会再去下载,它只会去下载一些跟 redis 相关的新的东西。
那么:为什么Docker镜像要采用这种分层的结构呢?
这种方式最大的好处就在于资源共享。比如有多个镜像都从相同的BASE镜像构建来的,那么宿主机只需要在磁盘上保留1分BASE镜像,同时内存中也只需要加载一份BASE镜像,这样所有的容器都可以使用。另外,镜像的每一层都是可以共享的。
如何查看镜像分层?
可以通过docker image inspect来查看镜像的分层,比如查看刚才下载的redis镜像:
docker image inspect redis:latest
所有的docker镜像都起始于一个基础镜像层,当进行修改或者增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
就好比:修房子,首先有了地基,然后盖了第一层后并装修,开始盖第二层,你发现想要的东西,第一层已经有了,那么就不会重复去修建了,只会修不一样,新的装饰。
四、Docker容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等 。
Docker | 面向对象 |
---|---|
容器 | 对象 |
镜像 | 类 |
容器的实质是进程,但与直接在宿主执行的进程是不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。
五、Docker镜像与容器的形象比喻
请看上一章节:大白话带你快速安装Docker,不懂你捶我
加载全部内容