Docker 容器为什么傲娇?全靠镜像撑腰!

6a7c32450ac0df36cf30228619af718f.gif

作者 | 飞向星的客机

来源 | CSDN博客

🌟 前言

Docker 镜像是 Docker 容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。

Docker 镜像是一个只读的模板,一个独立的文件系统,包括运行一个容器所需的数据,可以用来创建容器。

e4283dcfe71c8a4a67557914ed30eed3.gif

759dfada73a49259caf1afac1ed10327.png

base镜像

base(基础) 镜像是指完全从零开始构建的镜像, 它不会依赖其他镜像,甚至会成为被依赖的镜像,其他镜像以它为基础进行扩展。

通常 base 镜像都是 Linux 的系统镜像, 如 Ubuntu、CentOS、Debian 等。

下面通过 Docker 拉取一个 base 镜像并查看, 这里以 CentOS 为例, 示例代码如下:

1c3807b7b7a1d12f16b2c431f2ae97fe.png

从以上示例中可以看出,一个 CentOS 镜像大小只有 202MB,但在安装系统时,一个 CentOS 大概有几 GB,这与操作系统有关。

先观察 Linux 原本的操作系统结构,如图所示👇

d64bc0b0868289dec6485e1249b603ad.png

Kernel 是内核空间。bootfs 文件系统在 Linux 启动时加载。rootfs 是包含操作命令的文件系统。

base 镜像的创建过程中,Kernel、 bootfs 与 rootfs 都会加载,然后 bootfs 文件系统 (包括 Kernel) 被卸载掉,镜像只保留 rootfs 文件系统,供用户进行操作。bootfs 与 Kernel 将与宿主机共享。

另外,为了增加 Docker 的灵活性,base 镜像提供的都是最小安装的 Linux 系统。

Linux 系统不同的发行版之间最大的区别就是 rootfs 的不同,例如,Ubuntu 系统的应用程序管理器是 apt,而 CentOS 是 yum。

由此可见,只要提供不同的 rootfs 文件系统就可以同时支持多种操作系统,如图所示👇

386732a632507a181c6d11e1e5818440.png

从上图中可以看到,两个不同的 Linux 发行版提供了各自的 rootfs 文件系统,而它们共用的是底层宿主机的 Kernel。

假设宿主机的系统是 Ubuntu 16.04,Kernel 版本是 4.4.0,无论 base 镜像原本的发行版 Kernel 版本如何,在这台宿主机上都是 4.4.0。

下面通过示例来验证,示例代码如下:

6782be58ecd99e54e954002419023fd6.png

从上述示例中可以看出,base 镜像与宿主机的 Kernel 版本都是 3.10。

base 镜像的 Kernel 是与宿主机共享的,其版本与宿主机一致,并且不能进行修改。

5caafa93c3a73523dca229b543c1855e.png

镜像的本质

Docker 镜像是一个只读的文件系统,由一层一层的文件系统组成,每一层仅镜像的本质包含前一层的差异部分,这种层级文件系统被称为 UnionFS。

大多数 Docker 镜像都在 base 镜像的基础上进行创建,每进行一次新的创建就会在镜像上构建一个新的 UnionFS。

查看 ubuntu:15.04 镜像的层级结构,示例代码如下:

b0edeb7d88b3bb81dd05acb377ccea41.png

通常,对 Docker 的操作命令都是以 “docker” 开头。pull 是下载镜像的命令,在英文中是 “拉” 的意思,所以下载镜像又叫作 拉取镜像。

以上示例中,第 5 行到第 8 行是每一层 UnionFS 的 ID 号,第 9 行是整个镜像的 ID 号,这个 ID 号可以用来操控镜像。

然后,查看镜像,示例代码如下:

3679d00b584c6d1d637ec111353a5888.png

在以上示例中,不仅可以看到先前下载的 Ubuntu15.04 镜像,还可以看到其他镜像,说明 docker images 是查看本地所有镜像的命令。而查看到的信息中,除了镜像名称,还有版本号、镜像 ID 号、创建时间以及镜像大小。

接着,通过命令查看镜像的构建过程,示例代码如下:

d46f9481dd64f85f5ec7059368741b2b.png

这里使用 “history” 与镜像 ID 号组合的命令查看镜像构建过程,所显示的信息包括镜像 ID 号、创建时间、由什么命令创建以及镜像大小。

从以上示例中的信息可以看出,ubuntu:15.04 镜像由四个只读层 (Read Layer) 构建而成,每一层都是由一条命令构成的,最终得到 ID 号为 dlb55fd07600 的镜像,但以用户的视角只能看到最上层。

当用户将这个镜像放在容器中运行时,四层之上会创建出一个可读可写层(Read-Write Layer),用户对 Docker 的操作都通过可读可写层进行。如果用户修改了一个已存在的文件,那该文件将会从可读可写层下的只读层复制到可读可写层,该文件的只读版本仍然存在,只是已经被可读可写层中该文件的副本所隐藏。

可读可写层又叫作容器层,只读层又叫作镜像层,容器层之下均为镜像层,层级结构如图所示👇

18ad923a44764d3358614b38e5dc1916.png

镜像的这种分层机制最大的一个好处就是:共享资源。

例如,有很多个镜像都基于一个基础镜像构建而来,那么在本地的仓库中就只需要保存一份基础镜像,所有需要此基础镜像的容器都可以共享它,而且镜像的每一层都可以被共享,从而节省磁盘空间。

因为有了分层机制,本地保存的基础镜像都是只读的文件系统,不用担心对容器的操作会对镜像有什么影响。

为了将零星的数据整合起来,人们提出了镜像层 (Image Layer) 这个概念,如图所示👇

下图所示为一个镜像层,我们能够发现,一个层并不仅仅包含文件系统的改变,它还能包含其他重要的信息。

82cfafd739bdd5e65f8c1a51e6fb2cc0.png

元数据 (Metadata) 就是关于这个层的额外信息,包括 Docker 运行时的信息与父镜像层的信息,并且只读层与可读可写层都包含元数据,如图所示👇

d8321fadda00dfa8cdef919890c26dc8.png

除此之外,每一层还有一个指向父镜像层的指针。如果没有这个指针,说明它处于最底层,是一个基础镜像,如图所示👇

9318309e4cf1e7f10ef006eb3442e8e1.png

b67711f8cf77dd396117bf9f518f4e02.png

查找本地镜像

Docker 本地镜像通常是储存在服务器上的,下面验证本地镜像的储存路径,示例代码如下:

bb48d1bc5516328d77ccf6ab4f7fdefa.png

从以上示例中可以看到,Docker 本地镜像储存路径是 /var/Iib/Docker。

在本地查看镜像时,通常使用 docker images 命令,示例代码如下:

645359d800c0b2fb4ab4c49c15b57196.png

从以上示例中可以看到,结果显示中有多项镜像信息,下面对信息进行解释。

🍇 REPOSITORY

镜像仓库,即一些关联镜像的集合。

例如,Ubuntu 的每个镜像对应着不同的版本。与 Docker Registry 不同,镜像仓库提供 Docker 镜像的存储服务。

即 Docker Registry 中有很多镜像仓库,镜像仓库中有很多镜像 (相互独立)。

🍇 TAG

镜像的标签,常用来区分不同的版本,默认标签为 latest。

🍇 IMAGE ID

镜像的ID号,镜像的唯一标识,常用于操作镜像 (默认值只列出前 12 位)。

🍇 CREATED

镜像创建的时间。

🍇 SIZE

镜像的大小。

🍇 参数用法

在 docker images 命令后加上不同的参数就形成了不同的查询方式,导致不同的查询结果。

下面介绍各参数的含义以及用法。

  • -a

表示显示所有本地镜像,默认不显示中间层镜像,这是工作中经常使用到的参数,用来从本地镜像中寻找符合生产条件的镜像。

示例代码如下:

67fac259bf182829e24aeef9693a3c52.png

  • -q

表示只显示本地所有镜像 ID 号。

示例代码如下:

e043c635317cfd6987279a8b6cffeab6.png

  • -no-trunc

表示使用不截断的模式显示,并显示完整的镜像 ID 号。

示例代码如下:

71818508ad74e7fea1bceb045e340bf9.png

2b9aadbc478e972e15256a67c8c3f448.png

构建镜像

Docker 的官方镜像库 Docker Hub 发布了成千上万的公共镜像供全球用户使用。用户可以直接拉取(下载)所需要的镜像,提高了工作效率。但是在很多工作环境中,一旦对镜像有特殊需求,就需要我们手动去构建镜像。

本文章将会介绍基于 docker commit 命令与 Dockerfile 两种方式来构建自己的 Docker 镜像。

🍑 使用 docker commit 命令构建镜像

使用 docker commit 命令将容器的可读可写层转换为一个只读层,这样就把一个容器转换成了一个不可变的镜像,如图所示👇

b48e1781bb66ed19338ddbaad24f6110.png

下面我们给一个 Centos 的镜像安装一个 Vim 服务,设置开机启动,并将其构建成一个新的镜像,以免每次启动容器都要再次安装 Vim。

首先启动一个 Centos 的容器,示例代码如下:

f84e04cca5bb877117797ed98b9ccd24.png

从以上示例中可以看到,容器启动之后,主机名发生了改变,说明用户直接进入了容器,再进行操作就是对容器的操作。

然后,在容器中安装 Vim,示例代码如下:

2a60ed9944bd15e28a24180e1ec83521.png

安装完成之后,退出容器,示例代码如下:

487549c9d4568c15e32f3b392a02009e.png

使用 exit 命令退出容器之后, 该容器将默认关闭。

下面使用 docker commit 命令在 CentOS 镜像的基础上创建新的镜像,示例代码如下:

7f0f2a1233f9223ff309319ed36e2cf8.png

在命令中需要用镜像 ID 号来指定基础镜像,并不需要将 ID 号都输入进去,只要输入几个字符使 ID 号与其他镜像不冲突即可。

此时可以看到刚刚构建的新镜像,代码如下:

98e1e01cb30018bd895b7e4d016e7c67.png

从以上示例中可以看到, 新镜像的大小是 326MB,而此前的 CentOS 镜像只有 202MB, 这是因为在安装 Vim 时还安装了许多依赖包。

然后, 查看镜像中是否已经自动安装了 Vim, 示例代码如下:

af2b98a4bf5fccaf3de319b4c826fd7b.png

从以上示例中可以看到,新镜像已经包含了 Vim。

这种构建新镜像的方式在工作中并不常见,原因如下。

(1)效率低下,如果要给 Ubuntu 镜像也添加一个Vim,需要将上述全部过程重复一遍。

(2)不透明,用户使用时不知道镜像是如何构建的,难以对镜像做出正确的判断。

🍑 使用 Dockerfile 构建镜像

镜像可以基于 Dockerfile 构建。Dockerfile 是一个描述文件,包含若干条命令,每条命令都会为基础文件系统创建新的层次结构,这正好弥补了 docker commit 构建镜像效率低下的缺点。

Dockerfile 定义容器内部环境中发生的事情。网络接口和磁盘驱动器等资源的访问在此环境内虚拟化,与系统的其余部分隔离。

Dockerfile 主要使用 docker build 命令,根据 Dockerfile 文件中的指令,执行若干次  docker commit 命令构建镜像,每次执行 docker commit 命令时都会生成一个新的层,因此许多新的层会被创建,如图所示👇

31e17c50bd9c7106a5020535f5e73549.png

🍑 Dockerfile常用命令

下面介绍 Dockerfile 中常用的命令,完整说明见官方文档。

  • FROM

指定源镜像,必须是已经存在的镜像,必须是Dockerfile中第一条非注释的命令,因为其后的所有指令都使用该镜像。

  • MAINTAINER

指定作者信息。

  • RUN

在当前容器中运行指定的命令。

  • EXPOSE

指定运行容器时要使用的端口。可以使用多个EXPOSE命令。

  • CMD

指定容器启动时运行的命令,Dockerfile 可以出现多个 CMD 指令,但只有最后一个生效。CMD 可以被启动容器时添加的命令覆盖。

  • ENTRYPOINT

CMD 或容器启动时添加的命令会被当做参数传递给 ENTRYPOINT。

  • COPY

文件或目录复制到当前容器中。

  • ADD

将文件或者目录复制到当前容器中,源文件如果是归档(压缩)文件,则会被自动解压到目标位置。

  • VOLUME

为容器添加容器卷,可以存在于一个或多个目录,用来提供共享存储。该命令会在容器数据卷部分详细介绍。

  • WORKDIR

在容器内设置工作目录。

  • ENV

设置环境变量。

  • USER

指定容器以什么用户身份运行,默认是 root。

🍑 运行一个Dockerfile

下面演示使用 Docker file 创建 centos/vim,示例代码如下:

744f8dfddc15f948b6a1489315a9e998.png

这里在宿主机的 root 目录下创建了一个 Dockerfile 文件。

接着,向 Docker file 文件中添加内容, 示例代码如下:

7b9ca9ddb27ebba2eb26726f890a1214.png

添加完成之后,保存并退出。

有了 Dockerfile 文件之后即可创建新的镜像,示例代码如下:

1839830af14151fdf440a6f752eebe93.png

通过 docker build 命令执行 Dockerfile 文件,-t 用来指定新镜像名为 centos/vim-Dockerfile,命令行末尾的 . 表示 Dockerfile 文件在当前目录,Docker 默认从指定的目录寻找 Dockerfile 文件,也可以使用 -f 参数指定 Dockerfile 文件的位置。

9cc02b11b60697be21bf1ed0b8ae4000.png

构建完成之后,查看镜像是否构建成功,示例代码如下:

8134c5176c0441e0446f6e483627cbb8.png

从以上示例中可以看到,新镜像已经构建成功。

使用 Dockerfile 构建镜像基本可以分为以下五步。

(1) 选择一个基础镜像,运行一个临时容器。

(2) 执行一条命令,对容器做修改。

(3) 执行类似 docker commit 的操作,生成一个新的镜像。

(4) 删除临时容器,再基于刚刚构建好的新镜像运行一个临时容器。

(5) 重复 (2) (3) (4) 步,直到执行完 Dockerfile 中的所有指令。

centos/vim-Dockerfile 由 CentOS 基础镜像和 RUN yum -y install vim 构成,现在两个镜像都包含了 ID 号为 lel148e4cc2c 的只读层,如图所示👇

3d32a68a2b355cf07fa3347f339bdfd6.png

以上结论可以使用 docker history 命令验证,docker history 命令专门用来查看镜像的结构,示例代码如下:

1e5a9e9f111f068884121772e1a892c6.png

这里可以看到 CentOS 镜像中确实包含了 ID 号为 1e1148e4cc2c 的只读层。

接着再查看新镜像 centos/vim-Dockerfile 的结构,示例代码如下:

f84d49f993a1942bae1771a527761950.png

从以上示例中可以看到,两个镜像都含有一个相同的只读层,并且这个只读层是共享的。

Docker 构建镜像时有缓存机制,如果构建镜像层时该镜像层已经存在,就直接使用,无须重新构建。

下面为先前的 Dockerfile 文件添加一点内容,安装一个 ntp 服务,重新构建一个新的镜像,示例代码如下:

f8db623865160642fdffb206029508bd.png

这里多加了一条安装 ntp 服务的命令。

添加完成后,开始创建镜像,示例代码如下:

49a7be7501738132276d9eabdfb14c05.png

在示例的第 6 行代码中可以看到,Docker 没有重新安装 Vim,而是直接使用了先前安装过的缓存。

Dockerfile 文件是从上至下依次执行的,上层依赖于下层。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效。

改变先前的 Dockerfile 文件中两条 RUN 命令的上下顺序,观察 Docker 还会不会使用缓存机制,示例代码如下:

0b71f4f6a35083eb6e5c3255ed4f0d45.png

将 Dockerfile 中两条 RUN 命令的顺序互换之后,开始创建镜像,示例代码如下:

500b3d0a9677b44ba22889a6008511aa.png

由以上验证可知,将两条 RUN 命令交换顺序导致镜像层次发生改变,Docker 会重建镜像层。由此可见 Docker 的镜像层级结构特性:只有下面的层次内容、顺序完全一致才会使用缓存机制。

如果在构建镜像时不想使用缓存,可以在 docker build 命令中添加 --no-cache 参数,否则默认使用缓存。

除了在使用 Dockerfile 构建镜像时有缓存机制,在从仓库拉取镜像时也会有缓存机制,即已经拉取到本地的镜像层可以被多个镜像共同使用,可以说是一次拉取多次使用,前提是下层镜像完全相同。

通常使用 Dockerfile 构建镜像时,如果由于某些原因镜像构建失败,我们能够得到前一个指令成功执行构建出的镜像,继而可以运行这个镜像查找指令失败的原因,这对调试 Dockerfile 有极大的帮助。

从 Docker Hub 拉取的 CentOS 镜像是最小化的,其中没有 vim 命令。下面测试错误构建 Docker 镜像的结果,示例代码如下:

e5d64335e7ea6f0873105e530cdd80df.png

将 Dockerfile 中任意一条 RUN 命令改为错误的,再开始创建镜像,示例代码如下:

6796b2c441852e8df91aa93944158d99.png

在示例中,由于第三步报错,镜像没有创建成功。但也生成了一个新镜像,这个镜像是第二步操作构建的,通常可以通过这个新镜像排查错误,示例代码如下:

a8f6db7c9f220a450f1d816997c77bfb.png

Docker 容器技术中,编写 Dockerfile 文件是非常重要的部分,下面总结编写 Dockerfile 文件的一些小技巧,相信可以帮助大家更好地使用 Docker 与 Dockerfile。

(1)容器中只运行单个应用。

从技术角度讲,在一个容器中可以实现整个 LNMP (Linux+Nginx+MySQL+PHP) 架构。但这样做有很大的弊端。首先,镜像构建的时间会非常长,每次修改都要重新构建;

其次,镜像文件会非常大,大大降低容器的灵活性。

(2)将多个 RUN 指令合并成一个。

众所周知,Docker 镜像是分层的,Dockerfile 中的每一条指令都会创建一个新的镜像层,镜像层是只读的。

Docker镜像层类似于洋葱,想要更改内层,需要将外层全部撕掉。

(3)基础镜像的标签尽量不要使用 latest。

当镜像的标签没有指定时,默认使用 latest 标签。

当镜像更新时,latest 标签会指向不同的镜像,可能会对服务产生影响。

(4)执行 RUN 命令后删除多余文件。

假设执行了更新 yum 源的命令,会自动下载解压一些软件包,但是在运行容器的时候不需要这些包。最好将它们删除,因为这些软件包会使镜像 SIZE 变大。

(5)合理调整 COPY 与 RUN 的顺序。

将变化少的部分放在 Dockerfile 文件的前面,充分利用镜像缓存机制。

(6)选择合适的基础镜像。

最好选择满足环境需要而且体积小巧的镜像,比如 Alpine 版本的 node 镜像。

Alpine 是一个极小化的 Linux 发行版,只有 5.5MB,非常适合作为基础镜像。

ef5b66507d225e1edc416bf0bd69d0ee.png

Docker Hub

🍑 docker search 命令

Docker Hub 上有许多镜像, 但并不都是 Docker 官方人员制作的, 一些镜像是由 Docker 用户上传并维护的。工作中可以通过 docker search 命令从镜像仓库中查找所需要的镜像。

示例代码如下:

a997c75b820045790cafbc6152aaf1ed.png

以上示例中,查找的是与 CentOS 有关的镜像,Docker Hub 中有关 CentOS 的镜像远不止这些,这里显示的只是其中一部分。

下面对镜像的每项信息进行讲解。

  • NDEX

镜像索引,这里代表镜像仓库。

  • NAME

镜像名称。

  • DESCRIPTION

关于镜像的描述,使用户可以有方向性地选择镜像。

  • STARS

镜像的星标数,反映 Docker 用户对镜像的收藏情况,值越高代表使用的用户越多。

  • OFFICIAL

有此标记的都是 Docker 官方维护的镜像,没有此标记的通常是用户上传的镜像。

  • AUTOMATED

用来区分是否为自动化构建的镜像,有此标记的为自动化构建的镜像,否则不是。

🍑 docker search 参数运用

给 docker search 命令添加不同的参数,就会得到不同的查询结果,下面对各参数进行解释。

  • --automated

表示只列出自动化构建的镜像,示例代码如下:

4bf01c3762032297f401f3bfab1ca5a7.png

  • --no-trunc

表示显示完整的镜像描述,示例代码如下:

a871d6e50dc50f3407b6ac368ae462b9.png

  • -s

表示列出星标数不小于指定值的镜像,需要在参数后添加指定值,示例代码如下:

376bf014c422227858149f7399f322ee.png

🍑 镜像推送

用户向 Docker Hub 推送镜像需要有一个 Docker Hub 账号。在 Docker 客户端使用 docker login 命令登录 Docker Hub 账号,如图所示👇

75d92723d098196ebe9a844859892d32.png

下面使用 docker push 命令向 Docker Hub 推送镜像,示例代码如下:

bd61e6242947a57d7cd18e58e5795208.png

这里第 6 行出现了报错,镜像没有推送成功,报错信息显示请求资源被拒绝。这是因为 DockerHub 上的镜像都有一个 tag 标签,用户上传镜像时需要给镜像添加 tag 标签。

下面使用 docker tag 命令给镜像添加 tag 标签,示例代码如下:

3198c7bfec58d5c69521e8280109e060.png

tag 标签添加完之后,再查看一遍镜像,确保准确无误,示例代码如下:

813e79d0a3e586579bd4e67c64cd2bb4.png

可以看到,先前的镜像并没有发生改变,Docker 只是在它的基础上又创建了一个新镜像。与先前的镜像相比,新镜像只是修改了名字,镜像大小不变。

有了新镜像就可以将其推送给 Docker Hub 了,示例代码如下:

e126242f4120ffb9c2d13f154aa0fd6c.png

镜像推送完成之后, 可以登录 Docker Hub 查看, 验证镜像是否推送成功, 如图所示👇

ed621db236632063b3d2b140a763e2f6.png

从上图可以看出,镜像已经推送成功,这个镜像在 Docker Hub 上可以供所有用户下载并使用。

尝试拉取先前推送的镜像,示例代码如下:

a195483c12885b79f08a38e3667d581a.png

镜像拉取完成后,查看是否拉取成功,示例代码如下:

cab0c9235423afd74981bd6fe61973ea.png

从以上示例中可以看出,已经成功拉取了先前推送的镜像,拉取的镜像与本地的镜像相比,ID 号和镜像大小都是相同的。这就体现出了 DockerHub 官方镜像库的方便之处,用户可以将镜像上传到云端,做到随用随取,也可以将自己优秀的镜像与广大开源爱好者共享。

Docker Hub 在给用户带来便利的同时,也暴露出了极大的安全问题。任何 Docker 用户都可以随时随地上传镜像到 Docker Hub,将其推送给其他 Docker 用户,任何用户都可以拉取使用这些镜像,但谁都无法辨别所下载的镜像是否包含恶意信息。

2018 年发生了一起安全事故,一名 Docker 用户上传了 17 个带有恶意软件的镜像,攻击者利用这些恶意 Docker 镜像在受害者的计算机上安装基于 XMRig 的门罗币挖矿软件。其中一些镜像已经安装超过一百万次,还有一些则被使用了数十万次。Docker 官方网站维护人员调查确认后删除了这些恶意镜像,风波才得以平息。

在使用 Docker 镜像时要先在测试环境中安装运行,最安全的方法是尽可能使用自制的 Docker 镜像或使用经过验证的镜像。

0fe1fc524962931b2e690188dcac7813.png

Docker镜像优化

Docker 镜像采用的是层级结构,一个镜像最多拥有 127 层 UnionFS。每条 Dockerfile 命令都会创建一个镜像层,增加镜像大小。在生产环境中使用 Docker 容器时,要尽可能地精简 Docker 镜像,减少 UnionFS 的层数。

精简镜像不仅能缩短新镜像的构建时间,还能减少磁盘用量。由于精简后的镜像更小,用户在拉取镜像时能节省时间,部署服务的效率也能得到提升。精简镜像包含的文件更少,更加不容易被攻击,提高了镜像的安全性。

🍑 base镜像优化

base 镜像优化就是在满足环境要求的前提下使用最小的 base 镜像。常用的 Linux base 镜像有 CentOS、Ubuntu、Alpine 等,其中一些比较小的 base 镜像适合作为精简镜像的基础镜像,如 Alpine、BusyBox 等。

下面分别拉取 Alpine 与 BusyBox 的镜像进行对比,示例代码如下:

ddce5c3ce72de6138cf9a8bebcfdc39e.png

Scratch 是一个空镜像,只能用于构建其他镜像,常用于执行一些包含了所有依赖的二进制文件。如果以 Scratch 为 base 镜像,意味着不以任何镜像为基础,下面的指令将作为镜像的第一层存在。

BusyBox 相对于 Scratch 多了一些常用的 Linux 命令,BusyBox 的官方镜像大小只有 1MB 多一点,非常适合构建小镜像。

Alpine 是一款高度精简又包含了基本工具的轻量级 Linux 发行版,官方 base 镜像只有 5MB 多一点,很适合当作 base 镜像使用。

🍑 Dockerfile 优化

用户在定义 Dockerfile 文件时,使用太多的 RUN 命令,会导致镜像非常靡肿,甚至超出可构建的最大层数。根据优化原则,应该将多条 RUN 命令合并为一条命令,精心设计每一个 RUN 命令,减小镜像体积,并且精心编排,最大化地利用缓存。

下面创建一个 Dockerfile 文件,示例代码如下:

3c01809c653e3133bc9b18981350d215.png

接着, 使用这个 Dockerfile 构建一个新的镜像, 示例代码如下:

11b1678c5c28a288478db827b0f54a78.png

从以上示例中可以看到,整个镜像构建的过程是十分烦琐的。

查看新镜像的大小与 UnionFS 的层数,示例代码如下:

1cb7440949995e88147ff2d7f22e9836.png

从以上示例中可以看到,新镜像 centos/vim-bulky 的大小是 509MB,而镜像的 UnionFS 层数是 8 层。

这样编写 Dockerfile 导致新镜像非常庞大,既增加了构建部署的时间,也很容易出错。

下面对 Dockerfile 进行优化,示例代码如下:

9499e9d9a468a13031545026dac94100.png

在Dockerfile中使用 “&” 与 ‘’**" 将多条命令合成一条,"&&” 表示命令还没有结束,‘’**"表示换行。

下面通过优化过的 Dockerfile 构建新镜像,示例代码如下:

6311257038bc141594987e620c755cbe.png

从以上示例可以看出,优化后的 Dockerfile 构建镜像的过程比优化前精简了一些。

查看并对比两个新镜像,示例代码如下:

4990a53e27f456c552d2065ee6824c12.png

从以上示例中可以看到,镜像 centos/vim-portable 只有 305MB,比镜像 centos/vim-bulky 节省了 204MB 的资源,甚至比源镜像 centos/vim 还小了 21MB。

接着,查看镜像 centos/vim-portable 的 UnionFS 层数,示例代码如下:

a9a69e4659e6fbd7c46338d8245e25d4.png

从以上示例中可以看到,镜像 centos/vim-portable 的 UnionFS 只有 6 层,而先前的镜像 centos/vim-bulky 有 8 层 UnionFS。

🍑 清理无用的文件

在 RUN 命令中使用 yum、apt、apk 等工具时,可以借助这些工具自带的参数进行优化。如执行 apt-get install-y 时添加 --no-install-recommends 选项,就可以不安装建议性的依赖包,这些依赖包都是不必要的。

组件的安装和清理要放置在同一条命令里面,因为 Dockerfile 的每条命令都会产生一个新的镜像层,在执行下一条命令时,上一条命令所产生的镜像层已经为只读层,不可修改。Ubuntu 或 Debian 系统使用 rm -rf/var/lib/apt/lists/* 清理镜像中的缓存文件,CentOS 等系统使用 yum clean all 命令清理。

Docker 社区中还有许多优化镜像的工具,如压缩镜像的工具 Docker-squash,用起来简单方便。

d53b4b105e9eabd10ed39804f52c09f8.png

本章小结

本章介绍了 Docker 镜像的构成,采用了层级结构,层层递进;

然后介绍了镜像的仓库,包括官方公有仓库 Docker Hub 和 私有仓库;

最后详细讲解了如何构建 Docker 镜像,并对 Dockerfile 文件进行了简单的优化。

相信大家通过本章的学习,已经掌握了镜像的原理以及操作方式。

7bdf5273634ee26f6eb95b390617cb1e.gif

往期推荐

read 文件一个字节实际会发生多大的磁盘IO?

如何优雅保护 Kubernetes 中的 Secrets

Redis 内存满了怎么办?这样置才正确!

云原生的本手、妙手和俗手

92d847c109356532d249b56aadfb92fa.gif

点分享

770e6e871d83d189e019912806ba3e19.gif

点收藏

e9969c98d9d49d35ea79d5489bced382.gif

点点赞

7a306bf50a2ca8dd90292e6e6988b2af.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/511494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HBase读链路分析

简介:HBase的存储引擎是基于LSM-Like树实现的,更新操作不会直接去更新数据,而是使用各种type字段(put,delete)来标记一个新的多版本数据,采用定期compaction的形式来归档合并数据。这种数据结构…

设置linux文件系统密码,busybox 文件系统设置 登陆 login 密码 password shadow

用busybox做文件系统的很多介绍,这里就不啰嗦了。说几点:1、etc目录下有inittab,则系统按此文件规则来启动和运行,内容为:::sysinit:/etc/init.d/rcS#把respawn改成askfirst就会出现按enter才能进去的提示::respawn:-/…

PolarDB for PostgreSQL 开源路线图

简介:作者:蔡乐 本文主要分享一下Polar DB for PG的开源路线图,虽然路线图已经拟定,但是作为开源产品,所有参与者都能提出修改意见,包括架构核心特性的技术以及周边生态和工具等,希望大家能够踊…

5分钟入门Lindorm SearchIndex

简介:SearchIndex是Lindorm宽表的二级索引,主要用来帮助业务实现快速的检索分析。本篇文章介绍如何通过简单的SQL接口操作SearchIndex。 一、引言 云原生多模数据库Lindorm,支持海量数据的低成本存储和弹性按需付费,提供宽表、时…

linux coreutils升级,Linux命令01--Coreutils软件包01--认识coreutils

//通过ls命令的所属软件包来获取coreutils软件包的版本 #rpm -qf /bin/ls >>>coreutils-8.22-18.el7.x86_64 //下载yumdownloader工具,yumdownloader是一个从RPM源下载RPM的程序 #yum -y install yum-utils //使用yumdownloader命令安装源代码,…

最后的 48 小时!云 XR 专题赛邀你一起绽放精彩,我们赛场见!

2022 年是 5G 应用规模化发展的关键之年。随着5G的深入发展,涵盖百亿级“人机物”的智能连接正加速构建,经济社会发展不断向虚实融合演进,基础设施形态也不断向云网融合升级。随着连接对象的拓展和基础设施的升级,XR、元宇宙等新业…

Snowflake核心技术解读系列——架构设计

简介:Snowflake取得了巨大的商业成功,技术是如何支撑起它的千亿美元市值呢?它技术强在哪?本文为大家倾情解读Snowflake的核心技术原理。 背景:2020年9月16日,Snowflake成功IPO,交易首日市场估值…

Apsara Stack 技术百科 | 可运营的行业云,让云上资源跑起来

简介:企业级云管理平台,如何打造千人千面的个性化体验,从应用、云资源、硬件等进行全局智能优化,实现资源配置的最佳配比,构建精细化运营能力? 距离第一例新冠疫情病例的发现,不知不觉中已经过去…

中国数据库崛起,阿里云李飞飞:中国云数据库多种主流技术创新已领先国外

“中国云数据库在很多主流技术创新上已经领先国外。”李飞飞表示,“PolarDB未来还会不断基于新一代云计算架构进行创新突破,持续释放云计算的资源池化潜力,让客户享受到更多云原生技术的红利。” “中国云数据库在很多主流技术创新上已经领先…

十年再出发,Dubbo 3.0 Preview 即将在 3 月发布

简介:随着Dubbo和HSF的整合,我们在整个开源的体系中更多地去展现了 HSF 的能力,能够让更多的人通过使用 Dubbo 像阿里巴巴之前使用 HSF 一样更好的构建服务化的系统。 2011 年,阿里 B2B 团队决定将项目开源,一年时间就…

如何帮助金融客户“用好云”?

简介:如何帮助金融客户“用好云”?做「政企数智创新的同行者」,这对于阿里云混合云来说不仅仅是一句口号,更是在千行百业践行的行动指南。 “我一秒钟几千万上下,会跟你们吃杂碎面?” 这句出自星爷电影台…

nuc8i7beh安装linux随机重启,指南:nuc8i5beh安装黑苹果的教程,接近完美运行

黑苹果采购硬件设施nuc8i5beh镁光m.2 1100 256固态硬盘三星 DDR4 2400笔记本内存条HKC电脑显示器8G U盘(这个之前有,不属于采购的)所需软件Mojave 10.14.6网盘下载Catalina 10.15.2balenaEtcher:下载地址bios65文件:下载地址nuc8 安装软件包&…

低代码、端到端,一小时构建IoT示例场景,声网发布灵隼物联网云平台

2020年,全球 IoT 设备连接数量首次超过非 IoT 设备。市场在高速增长,但音视频物联网的开发门槛依然很高。 6月28日,声网在线上举办主题为“视听无界,智联万物”的产品发布会,正式发布了“灵隼物联网云平台”&#xff0…

云知声 Atlas 超算平台: 基于 Fluid + Alluxio 的计算加速实践

简介:本文主要介绍云知声 Atlas 超算平台基于 Fluid Alluxio 的计算加速实践,以及 Fluid 是如何为 Atlas 带来全新的数据集管理方式的。 Fluid 是云原生基金会 CNCF 下的云原生数据编排和加速项目,由南京大学、阿里云及 Alluxio 社区联合发…

eBPF技术应用云原生网络实践系列之基于socket的service | 龙蜥技术

简介:如何使用 socket eBPF进一步提升Service 网络的转发性能? 背景介绍 Kubernetes 中的网络功能,主要包括 POD 网络,service 网络和网络策略组成。其中 POD 网络和网络策略,都是规定了模型,没有提供默认…

libmysqld_dev linux,解决mysql安装mysqld doesn\'t exist or is not-鸟哥のlinux-ChinaUnix博客

最近phpwind升级8.7后,经常出现宕机的现象,不是Apache卡死,吃光内存机器重启,就是mysql数据库被锁,无奈!多个原因,apache的版本过低,导致执行效率慢;另外数据库版本过低&…

从开店营销到智能化私域运营,有赞发布人工智能引擎Jarvis

“以往在私域发起一场营销活动,从活动策划,到历史数据分析,再到内容排期,整个策划阶段往往需要一周以上的时间,而现在通过营销画布(MA)以及其中内嵌的人工智能能力,只需要30分钟不到…

“云网管” ---云上构建网络自动化体系

简介:云网管是基于阿里云网络多年技术和经验沉淀打造的云上智能网络管理运维平台,提供企业网络全生命周期管理运维的能力,让部署更快捷、运维更高效、网络更透明。 1.背景 云网管是基于阿里云网络多年技术和经验沉淀打造的云上智能网络管理…

2022 (第五届)GIS软件技术大会开幕,GIS、IT将加速融合

院士专家共论GIS与AI、数据库等IT技术加速融合趋势。 会上,超图正式发布新产品SuperMap GIS 11i(2022)、与华为联合发布禹贡开源空间数据库。 6月29日上午,2022(第五届)GIS软件技术大会主题大会在线开幕。大会以“地理智慧 多维筑基”为主题,…

牛顿迭代法c 语言程序,牛顿迭代法 c语言实现

#include #include double func(double x) //函数{return x*x*x*x-3*x*x*x1.5*x*x-4.0;}double func1(double x) //导函数{return 4*x*x*x-9*x*x3*x;}int Newton(double *x,double precision,int maxcyc) //迭代次数{double x1,x0;int k;x0*x;for(k0;k{if(func1(x0)0.0)//若通过…