Docker容器镜像加载及底层基本原理深入解析

  

前言

回想上学的时候,某老师神采奕奕的讲解着操作系统某个知识点原理,可谓是激情澎湃,我环顾周围基友们,脸上懵逼两字清晰可见,毫不含糊,恍然大悟原来是在讲天书。为了不让大家脸上呈现懵逼二字,希望能以人能看懂的语言,让大家理解。

一、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,不懂你捶我

以上就是Docker容器镜像加载及底层基本原理深入解析的详细内容,更多关于Docker容器镜像加载底层原理的资料请关注编程学习网其它相关文章!

相关文章