一、Docker 镜像原理

1、镜像

Docker 镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。

以我们的 docker pull tomcat 为例,在下载的过程中我们可以看到 docker 的镜像好像是在一层一层的在下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~]# docker pull tomcat
Using default tag: latest
latest: Pulling from library/tomcat
0e29546d541c: Pull complete
9b829c73b52b: Pull complete
cb5b7ae36172: Pull complete
6494e4811622: Pull complete
668f6fcc5fa5: Pull complete
dc120c3e0290: Pull complete
8f7c0eebb7b1: Pull complete
77b694f83996: Pull complete
0f611256ec3a: Pull complete
4f25def12f23: Pull complete
Digest: sha256:9dee185c3b161cdfede1f5e35e8b56ebc9de88ed3a79526939701f3537a52324
Status: Downloaded newer image for tomcat:latest
docker.io/library/tomcat:latest

镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。

构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。

img

2、联合文件系统(UnionFS)

Docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

​  特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

3、Docker镜像加载原理

bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

19827908-823b1f9e4726c37b

[quote color=“info”]  对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。 [/quote]

4、分层镜像

特点:共享资源。比如,有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。这一层称为"容器层",“容器层"之下为"镜像层”。

5、查看镜像

我们有2种方法查看镜像:

  1. 使用docker inspect:获取镜像的元数据
  2. 使用docker history:查看镜像的构建历史

理论上,一个容器的镜像,最多可以有127层,怎么查看镜像的层数?

通过 docker Inspect 命令,寻找 RootFS 代码片段,在 Layers 中,有多少行 (sha256) 就代表镜像有多少层。

1
docker inspect nginx #根据镜像nginx,查看镜像详细信息
1
2
3
4
5
6
7
8
9
10
11
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f",
"sha256:e379e8aedd4d72bb4c529a4ca07a4e4d230b5a1d3f7a61bc80179e8f02421ad8",
"sha256:b8d6e692a25e11b0d32c5c3dd544b71b1085ddc1fddad08e68cbd7fda7f70221",
"sha256:f1db227348d0a5e0b99b15a096d930d1a69db7474a1847acbc31f05e4ef8df8c",
"sha256:32ce5f6a5106cc637d09a98289782edf47c32cb082dc475dd47cbf19a4f866da",
"sha256:d874fd2bc83bb3322b566df739681fbd2248c58d3369cb25908d68e7ed6040a6"
]
},

使用docker history可以看到镜像的构建历史。

1
docker history nginx 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
605c77e624dd 6 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 6 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 6 months ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 6 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
<missing> 6 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB
<missing> 6 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB
<missing> 6 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB
<missing> 6 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB
<missing> 6 months ago /bin/sh -c set -x && addgroup --system -… 61.1MB
<missing> 6 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
<missing> 6 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.1 0B
<missing> 6 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.5 0B
<missing> 7 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 7 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 7 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB

在Dockerfile中一般,每一行的添加ADD、COPY、RUN,或者创建新(配置)文件的命令,都会触发新建一层镜像,这层镜像一般会被称为中间层镜像,中间层镜像存在的意义:加速镜像构建、重复利用资源。

二、Docker 镜像操作案例

使用 docker commit 提交容器副本使之成为为一个新的镜像,在此示例中,我们使用 ubuntu 镜像为例,原始的默认ubuntu镜像不带vim命令,我们在外网连通的情况下,安装 vim 软件,安装完成后commit为新的ubuntu镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 运行 unbntu 镜像
[root@localhost ~]# docker run -it ubuntu bash

# 配置更新
root@393af6f0b855:/# apt-get update

# 安装 vim
root@393af6f0b855:/# apt-get install -y vim

# 验证
root@393af6f0b855:/# vim a.txt
root@393af6f0b855:/# cat a.txt
aaa

# 提交容器副本使之成为一个新Ubuntu镜像
[root@localhost ~]# docker commit -m="vim cmd add ok" -a="whb" 393af6f0b855 myubuntu:v1

# 查看镜像
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myubuntu v1 a7a511fae0fb 9 seconds ago 178MB
1
2
# commit 语法
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的镜像名称:[tag]