Docker镜像结构和原理

详细介绍,Docker镜像结构和原理

docker镜像:

  • 镜像,即创建容器的模版,含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于方便和快速的创建并启动容器
  • 镜像可以是自己创建,或者在别人的镜像基础上创建。我们常说的"ubuntu"镜像其实不是一个镜像名称,而是代表了一个名为ubuntu的Repository,同时在这个Repository下面有一系列打了tag的Image。

镜像含里面是一层层的文件系统,叫做 Union FS(联合文件系统),联合文件系统,可以将几层目录挂载到一起(就像千层饼,洋葱头,俄罗斯套娃一样),形成一个虚拟文件系统,虚拟文件系统的目录结构就像普通 linux 的目录结构一样,镜像通过这些文件再加上宿主机的内核共同提供了一个 linux 的虚拟环境,每一层文件系统叫做一层 layer,联合文件系统可以对每一层文件系统设置三种权限,只读(readonly)、读写(readwrite)和写出(whiteout-able),但是镜像中每一层文件系统都是只读的,构建镜像的时候,从一个最基本的操作系统开始,每个构建提交的操作都相当于做一层的修改,增加了一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层把底层遮住了一样,当使用镜像的时候,我们只会看到一个完全的整体,不知道里面有几层,实际上也不需要知道里面有几层,结构如下:

注意一定要区分镜像和容器

在Docker的术语里,一个只读层被称为镜像,一个镜像是永久不会变的,故镜像是无状态的。容器是在镜像层之上增加一个可写层。这个可写层有运行在CPU上的进程,而且有两个不同的状态:运行态和退出态。当启动容器时,Docker容器就进入运行态,当停止容器时,它就进入退出态。当有一个正在运行的Docker容器时,从运行态到停止态,此时状态的变更会永久地写到容器的文件系统中。要切记,对容器的变更是写入到容器的文件系统的,而不是写入到Docker镜像中的,Docker镜像是只读的,永远不会变的。
​ 同一个镜像可以启动多个Docker容器,这些容器启动后都是活动的,彼此还是相互隔离的。对其中一个容器所做的变更只会局限于这个容器本身。

详细介绍,Docker镜像结构和原理

一个典型的 Linux文件系统由 bootfs 和 rootfs 两部分组成
bootfs(boot file system) 主要包含bootloader和kernel,bootloader主要用于引导加载 kernel,Linux刚启动时会加载bootfs文件系统,当boot加载完成后,kernel 被加载到内存中后接管系统的控制权,bootfs会被 umount 掉

rootfs (root file system) 包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件,不同的 linux 发行版(如 ubuntu 和 CentOS ) 主要在 rootfs 这一层会有所区别。
一般的镜像通常都比较小,官方提供的Ubuntu镜像只有60MB多点,而 CentOS 基础镜像也只有200MB左右,一些其他版本的镜像甚至只有几MB,比如: busybox 才1.22MB,alpine镜像也只有5M左右。镜像直接调用宿主机的内核,镜像中只提供 rootfs,也就是只需要包括最基本的命令,配置文件和程序库等相关文件就可以了。

下图就是有两个不同的镜像在一个宿主机内核上实现不同的rootfs。

详细介绍,Docker镜像结构和原理

上图 Debian 和 BusyBox上层提供各自的 rootfs,底层共用 Docker Host 的 kernel。

注意:base 镜像只是在用户空间与发行版一致,kernel 版本与发型版是不同的,**kernel 版本取决于宿主机*

[root@docker ~]# uname -r
3.10.0-514.el7.x86_64                 ##Host kernel 为 3.10.0-514
[root@docker ~]# docker run -ti centos    ##启动并进入 CentOS 容器
[root@docker ~]# cat /etc/redhat-release   ##验证容器是 CentOS 7
CentOS Linux release 7.4.1708 (Core)
[root@docker ~]# uname -r      ##容器的 kernel 版本与 Host 一致
3.10.0-514.el7.x86_64

容器、镜像和父镜像关系:

详细介绍,Docker镜像结构和原理

详细介绍,Docker镜像结构和原理
可以看到,新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

这样最大的好处就是资源共享

容器的可写层

当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

详细介绍,Docker镜像结构和原理

所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。
只有容器层是可写的,容器层下面的所有镜像层都是只读的
下面我们深入讨论容器层的细节。

  1. 添加文件
    在容器中创建文件时,新文件被添加到容器层中。
  2. 读取文件
    在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
  3. 修改文件
    在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
  4. 删除文件
    在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。

docker存在能给我们带来的好处

1.详细介绍,Docker镜像结构和原理

现在有两个操作系统分别是应用A和应用B,这两个都是我们最传统化的环境。这两个操作系统上面运行了一些依赖包,然后再这上面安装对应的运行环境或者是应用程序,然后再去跑我们的一些应用。那我们这个时候需要怎么优化呢?

2.详细介绍,Docker镜像结构和原理

我们把应用B的操作系统去掉了,这时候应用B使用应用A的操作系统,这样的好处是应用B这个操作系统本身的操作资源就不需要了。但是也会带来一个问题、如果我们需要对操作系统做一些更改,举个例子:比如现在我们想把修改/etc/sysconfig/ 文件中的ip地址、但是应用B 不能够支持修改后的IP地址、这时候出事了,因为应用B是共享应用A的,所以IP也得变化,这时候就需要加一个空白层

3.详细介绍,Docker镜像结构和原理

加一个空白层、空白层有一句话叫做:应用优先级大于底层,也就是空白层的优先级大于下面的所有层级,这样的话我们在应用A这个程序里的空白层写新的IP、然后再应用B程序的空白层中写能够支持的旧IP,这样的话就是 应用A 为新IP 应用B为旧IP互不干扰。并且每一个应用都有自己独立的空白层。这几样就更完美了。 但是有个问题假如现在应用程序A宕机了、紧接着后面的应用B、C、D等等等应用程序都会被强制关闭,因为所有的应用都是依赖于应用A生存的。所以这个时候就得看下图

4.详细介绍,Docker镜像结构和原理

为了避免被依赖的应用程序宕机而导致后面所有依赖应用关闭、所以我们不用应用程序共享,而是通过镜像来共享,应用是一个活的,镜像是一个死的。只要我们不删除镜像它是不会被损坏。所以我们的操作系统不是有应用程序或叫已经运行的环境供给、而是由我们的镜像来供给。镜像就是一个封装好的运行环境、这样就已经非常美好了,应用A的镜像不会坏,应用A的容器也能够运行,并且节省了我们对应的操作系统级别的资源,不同的空白层又去写入不同的新数据,从而保证我们的每个应用程序都可以不一致。但这时候还会发现一个问题,就是如果应用A和应用B他们之间用的是不同的镜像。那岂不是占用很多存储资源,那怎么办就往下看

5.详细介绍,Docker镜像结构和原理

就通过分层、怎么来理解呢就是我现在构建一个LNMP,第一层我需要一个Linux内核 比如是我们的centos 6.8、第二层我们需要安装nginx、第三层我们安装php、第四层我们安装mysql5.0版本、结果有一天需要一个mysql5.5版本的那这个时候怎么办,这时候我们直接把第四层替换掉,给他换成mysql5.5版本。这样的话我们一个镜像看起来是一个文件,其实他并不是一个文件,而是由很多很多个子文件把给结合起来,但是这很多个也是有对应的数目限制的。这个限制池为128也就是不能够超过128层,比如这时候我存了一个新镜像,这个镜像也有很多层级,但是如果有一层和我们之前的镜像某一层是一样的话他会跳过这个镜像,直接下载别的,这样就大大的减少了我们的存储量。这也就是我们最后一步优化方案分层存储。

#范例:查看镜像的分层结构
[root@ubuntu1804 ~]#docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
8ec398bc0356: Pull complete
a53c868fbde7: Pull complete
79daf9dd140d: Pull complete
Digest: sha256:70821e443be75ea38bdf52a974fd2271babd5875b2b1964f05025981c75a6717
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

#查看镜像分层历史
[root@ubuntu1804 ~]#docker image history nginx
IMAGE        CREATED       CREATED BY               
   SIZE        COMMENT
0901fa9da894     9 days ago     /bin/sh -c #(nop) CMD ["nginx" "-g"
"daemon…  0B         
<missing>      9 days ago     /bin/sh -c #(nop) STOPSIGNAL SIGTERM 
    0B         
<missing>      9 days ago     /bin/sh -c #(nop) EXPOSE 80      
   0B         
<missing>      9 days ago     /bin/sh -c #(nop) ENTRYPOINT ["/docker-
entr…  0B         
<missing>      9 days ago     /bin/sh -c #(nop) COPY
file:0fd5fca330dcd6a7…  1.04kB       
<missing>      9 days ago     /bin/sh -c #(nop) COPY
file:1d0a4127e78a26c1…  1.96kB       
<missing>      9 days ago     /bin/sh -c #(nop) COPY
file:e7e183879c35719c…  1.2kB       
<missing>      9 days ago     /bin/sh -c set -x   && addgroup --
system -…  63.3MB       
<missing>      9 days ago     /bin/sh -c #(nop) ENV
PKG_RELEASE=1~buster   0B         
<missing>      9 days ago     /bin/sh -c #(nop) ENV NJS_VERSION=0.4.2
   0B         
<missing>      9 days ago     /bin/sh -c #(nop) ENV
NGINX_VERSION=1.19.1   0B         
<missing>      5 weeks ago     /bin/sh -c #(nop) LABEL
maintainer=NGINX Do…  0B         
<missing>      5 weeks ago     /bin/sh -c #(nop) CMD ["bash"]    
    0B         
<missing>      5 weeks ago     /bin/sh -c #(nop) ADD
file:4d35f6c8bbbe6801c…  69.2MB

[root@ubuntu1804 ~]#docker inspect nginx
[
 {
    "Id":
"sha256:0901fa9da894a8e9de5cb26d6749eaffb67b373dc1ff8a26c46b23b1175c913a",
    "RepoTags": [
      "nginx:latest"
   ],
    "RepoDigests": [
    
 "nginx@sha256:a93c8a0b0974c967aebe868a186e5c205f4d3bcb5423a56559f2f9599074bbcd"
   ],
    "Parent": "",
    "Comment": "",
    "Created": "2020-07-10T20:26:44.624785651Z",
    "Container":
"348c3ade7f4bdc0366f3f390ea4cfaebfb355ad7d621547eaf73728136d3bd2d",
    "ContainerConfig": {
      "Hostname": "348c3ade7f4b",
      "Domainname": "",
      "User": "",
      "AttachStdin": false,
      "AttachStdout": false,
      "AttachStderr": false,
      "ExposedPorts": {
        "80/tcp": {}
     },
      "Tty": false,
      "OpenStdin": false,
      "StdinOnce": false,
      "Env": [
      
 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "NGINX_VERSION=1.19.1",
        "NJS_VERSION=0.4.2",
        "PKG_RELEASE=1~buster"
     ],
      "Cmd": [
        "/bin/sh",
        "-c",
        "#(nop) ",
        "CMD [\"nginx\" \"-g\" \"daemon off;\"]"
     ],
      "ArgsEscaped": true,
      "Image":
"sha256:8a6dfc8c21a1b3f3679b7755fc7869a22b5f8583778cf7835b5ee5387a73ae5e",
      "Volumes": null,
      "WorkingDir": "",
      "Entrypoint": [
        "/docker-entrypoint.sh"
     ],
      "OnBuild": null,
      "Labels": {
        "maintainer": "NGINX Docker Maintainers <docker-
maint@nginx.com>"
     },
      "StopSignal": "SIGTERM"
   },
    "DockerVersion": "18.09.7",
    "Author": "",
    "Config": {
      "Hostname": "",
      "Domainname": "",
      "User": "",
      "AttachStdin": false,
      "AttachStdout": false,
      "AttachStderr": false,
      "ExposedPorts": {
        "80/tcp": {}
     },
      "Tty": false,
      "OpenStdin": false,
      "StdinOnce": false,
      "Env": [
      
 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "NGINX_VERSION=1.19.1",
        "NJS_VERSION=0.4.2",
        "PKG_RELEASE=1~buster"
     ],
      "Cmd": [
        "nginx",
        "-g",
        "daemon off;"
     ],
      "ArgsEscaped": true,
      "Image":
"sha256:8a6dfc8c21a1b3f3679b7755fc7869a22b5f8583778cf7835b5ee5387a73ae5e",
      "Volumes": null,
      "WorkingDir": "",
      "Entrypoint": [
        "/docker-entrypoint.sh"
     ],
      "OnBuild": null,
      "Labels": {
        "maintainer": "NGINX Docker Maintainers <docker-
maint@nginx.com>"
     },
      "StopSignal": "SIGTERM"
   },
    "Architecture": "amd64",
    "Os": "linux",
    "Size": 132484492,
    "VirtualSize": 132484492,
    "GraphDriver": {
      "Data": {
        "LowerDir":
"/var/lib/docker/overlay2/87dfa2bbff4f392e64e8a2fce11e2d9b8c3fffcfd51c6721ef0103
f7d6b525aa/diff:/var/lib/docker/overlay2/925c1a9c01939d111b3f0576608ad02a09bceea
fb6cad8dd616c24a59151bb25/diff:/var/lib/docker/overlay2/b9fd33e18d7bb7bb29b51ee3
99dfe3f21654fa4a9a086e02dcbc23f34f140c09/diff:/var/lib/docker/overlay2/35c6b8c39
68cdc21b78d4bcd6c192683b47db788db612dee04c5110037eed7af/diff",
        "MergedDir":
"/var/lib/docker/overlay2/23d3c6ecee136afe7137528b0e6997a488b1f277e86c794fa9a70b
5638a5d3f9/merged",
        "UpperDir":
"/var/lib/docker/overlay2/23d3c6ecee136afe7137528b0e6997a488b1f277e86c794fa9a70b
5638a5d3f9/diff",
        "WorkDir":
"/var/lib/docker/overlay2/23d3c6ecee136afe7137528b0e6997a488b1f277e86c794fa9a70b
5638a5d3f9/work"
     },
      "Name": "overlay2"
   },
    "RootFS": {
      "Type": "layers",
      "Layers": [
      
 "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74",
      
 "sha256:0e32546a8af0cd04ad451d6a9d22e650e500e5da3636a32648c9f5aca96a0ff7",
      
 "sha256:7ef35766ef7d5d3d958022405b308d5c105b41190e1b63b2037c4055c6950c9e",
      
 "sha256:4856db5e4f59384c413c20c46cd5403a860e1b07c8fdbad24df1ffd9209d44e7",
 "sha256:2808ec4a8ea71c2660284d06cf7e25354b70b58504edb46ac3e705fb7e6ea519"
     ]
   },
    "Metadata": {
      "LastTagTime": "0001-01-01T00:00:00Z"
   }
 }
]

[root@ubuntu1804 ~]#docker save nginx -o nginx.tar

[root@ubuntu1804 ~]#docker images
REPOSITORY     TAG         IMAGE ID      CREATED      
SIZE
nginx        latest       0901fa9da894     3 days ago    
127MB
alpine        3.11.3       e7d92cdc71fe     7 days ago    
 5.59MB
centos       centos8.1.1911   470671670cac     7 days ago    
237MB
busybox       latest       6d5fcfe5ff17     4 weeks ago    
1.22MB
hello-world     latest       fce289e99eb9     12 months ago   
1.84kB

[root@ubuntu1804 ~]#ll -h nginx.tar
-rw------- 1 root root 131M Jul 20 22:33 nginx.tar

[root@ubuntu1804 ~]#tar xf nginx.tar -C /data

[root@ubuntu1804 ~]#ll /data
total 60
drwxr-xr-x  8 root root  4096 Jul 20 22:34 ./
drwxr-xr-x 24 root root  4096 Jul 20 16:23 ../
-rw-r--r--  1 root root  7510 Jul 11 04:26
0901fa9da894a8e9de5cb26d6749eaffb67b373dc1ff8a26c46b23b1175c913a.json
drwxr-xr-x  2 root root  4096 Jul 11 04:26
0bb74fcd4b686412f7993916e58c26abd155fa10b10a4dc09a778e7c324c39a2/
drwxr-xr-x  2 root root  4096 Jul 11 04:26
517e3239147277447b60191907bc66168963e0ce8707a6a33532f7c63a0d2f12/
drwxr-xr-x  2 root root  4096 Jul 11 04:26
68c9e9da52d5a57ee196829ce4a461cc9425b0b920689da9ad547f1da13dbc9d/
drwxr-xr-x  2 root root  4096 Jul 11 04:26
d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd/
drwxr-xr-x  2 root root  4096 Jul 11 04:26
f4bf863ecdbb8bddb4b3bb271bdd97b067dcb6c95c56f720018abec6af190c6e/
drwx------  2 root root 16384 Mar 18 09:49 lost+found/
-rw-r--r--  1 root root  509 Jan  1  1970 manifest.json
-rw-r--r--  1 root root   88 Jan  1  1970 repositories

[root@ubuntu1804 ~]#cat /data/manifest.json
[{"Config":"0901fa9da894a8e9de5cb26d6749eaffb67b373dc1ff8a26c46b23b1175c913a.jso
n","RepoTags":["nginx:latest"],"Layers":
["d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd/layer.tar","f
4bf863ecdbb8bddb4b3bb271bdd97b067dcb6c95c56f720018abec6af190c6e/layer.tar","517e
3239147277447b60191907bc66168963e0ce8707a6a33532f7c63a0d2f12/layer.tar","0bb74fc
d4b686412f7993916e58c26abd155fa10b10a4dc09a778e7c324c39a2/layer.tar","68c9e9da52
d5a57ee196829ce4a461cc9425b0b920689da9ad547f1da13dbc9d/layer.tar"]}]
[root@ubuntu1804 ~]#du -sh /data/*
8.0K
/data/0901fa9da894a8e9de5cb26d6749eaffb67b373dc1ff8a26c46b23b1175c913a.json
16K /data/0bb74fcd4b686412f7993916e58c26abd155fa10b10a4dc09a778e7c324c39a2
16K /data/517e3239147277447b60191907bc66168963e0ce8707a6a33532f7c63a0d2f12
16K /data/68c9e9da52d5a57ee196829ce4a461cc9425b0b920689da9ad547f1da13dbc9d
70M /data/d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd
62M /data/f4bf863ecdbb8bddb4b3bb271bdd97b067dcb6c95c56f720018abec6af190c6e
16K /data/lost+found
4.0K /data/manifest.json
4.0K /data/repositories
[root@ubuntu1804 ~]#cd
/data/d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd/
[root@ubuntu1804
d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd]#ls
json layer.tar VERSION
[root@ubuntu1804
d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd]#tar xf
layer.tar
[root@ubuntu1804
d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd]#ls
bin  dev home layer.tar lib64 mnt proc run  srv tmp var
boot etc json lib    media opt root sbin sys usr VERSION
[root@ubuntu1804
d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd]#cat etc/i
init.d/  issue   issue.net 
[root@ubuntu1804
d2cf0fc540bb3be33ee7340498c41fd4fc82c6bb02b9955fca2109e599301dbd]#cat etc/issue
Debian GNU/Linux 10 \n \l