当前位置:嗨网首页>书籍在线阅读

06-最佳实践

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.2.3 最佳实践

在结束本章之前,介绍一些最佳实践,当然本节无意罗列所有的最佳实践。

1.利用构建缓存

Docker的构建过程利用了缓存机制。观察缓存效果的一个方法,就是在一个干净的Docker主机上构建一个新的镜像,然后再重复同样的构建。第一次构建会拉取基础镜像,并构建镜像层,构建过程需要花费一定时间;第二次构建几乎能够立即完成。这就是因为第一次构建的内容(如镜像层)能够被缓存下来,并被后续的构建过程复用。

docker image build 命令会从顶层开始解析Dockerfile中的指令并逐行执行。而对每一条指令,Docker都会检查缓存中是否已经有与该指令对应的镜像层。如果有,即为缓存命中(Cache Hit),并且会使用这个镜像层;如果没有,则是缓存未命中(Cache Miss),Docker会基于该指令构建新的镜像层。缓存命中能够显著加快构建过程。

下面通过实例演示其效果。

示例用的Dockerfile如下。

FROM alpine
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]

第一条指令告诉Docker使用 alpine:latest 作为基础镜像。如果主机中已经存在这个镜像,那么构建时会直接跳到下一条指令;如果镜像不存在,则会从Docker Hub(docker.io)拉取。

下一条指令( RUN apk... )对镜像执行一条命令。此时,Docker会检查构建缓存中是否存在基于同一基础镜像,并且执行了相同指令的镜像层。在此例中,Docker会检查缓存中是否存在一个基于 alpine:latest 镜像且执行了 RUN apk add --update nodejs nodejs-npm 指令构建得到的镜像层。

如果找到该镜像层,Docker会跳过这条指令,并链接到这个已经存在的镜像层,然后继续构建;如果无法找到符合要求的镜像层,则设置缓存无效并构建该镜像层。此处“设置缓存无效”作用于本次构建的后续部分。也就是说Dockerfile中接下来的指令将全部执行而不会再尝试查找构建缓存。

假设Docker已经在缓存中找到了该指令对应的镜像层(缓存命中),并且假设这个镜像层的ID是 AAA

下一条指令会复制一些代码到镜像中( COPY . /src )。因为上一条指令命中了缓存,Docker会继续查找是否有一个缓存的镜像层也是基于 AAA 层并执行了 COPY . /src 命令。如果有,Docker会链接到这个缓存的镜像层并继续执行后续指令;如果没有,则构建镜像层,并对后续的构建操作设置缓存无效。

假设Docker已经有一个对应该指令的缓存镜像层(缓存命中),并且假设这个镜像层的ID是 BBB

那么Docker将继续执行Dockerfile中剩余的指令。

理解以下几点很重要。

首先,一旦有指令在缓存中未命中(没有该指令对应的镜像层),则后续的整个构建过程将不再使用缓存。在编写Dockerfile时须特别注意这一点,尽量将易于发生变化的指令置于Dockerfile文件的后方执行。这意味着缓存未命中的情况将直到构建的后期才会出现——从而构建过程能够尽量从缓存中获益。

通过对 docker image build 命令加入 --nocache=true 参数可以强制忽略对缓存的使用。

还有一点也很重要,那就是 COPYADD 指令会检查复制到镜像中的内容自上一次构建之后是否发生了变化。例如,有可能Dockerfile中的 COPY . /src 指令没有发生变化,但是被复制的目录中的内容已经发生变化了。

为了应对这一问题,Docker会计算每一个被复制文件的Checksum值,并与缓存镜像层中同一文件的checksum进行对比。如果不匹配,那么就认为缓存无效并构建新的镜像层。

2.合并镜像

合并镜像并非一个最佳实践,因为这种方式利弊参半。

总体来说,Docker会遵循正常的方式构建镜像,但之后会增加一个额外的步骤,将所有的内容合并到一个镜像层中。

当镜像中层数太多时,合并是一个不错的优化方式。例如,当创建一个新的基础镜像,以便基于它来构建其他镜像的时候,这个基础镜像就最好被合并为一层。

缺点是,合并的镜像将无法共享镜像层。这会导致存储空间的低效利用,而且push和pull操作的镜像体积更大。

执行 docker image build 命令时,可以通过增加 --squash 参数来创建一个合并的镜像。

图8.8阐释了合并镜像层带来的存储空间低效利用的问题。两个镜像的内容是完全一样的,区别在于是否进行了合并。在使用 docker image push 命令发送镜像到Docker Hub时,合并的镜像需要发送全部字节,而不合并的镜像只需要发送不同的镜像层即可。

43.png

图8.8 合并的与不合并的镜像
3.使用no-install-recommends

在构建Linux镜像时,若使用的是APT包管理器,则应该在执行 apt-get install 命令时增加 no-install-recommends 参数。这能够确保APT仅安装核心依赖( Depends 中定义)包,而不是推荐和建议的包。这样能够显著减少不必要包的下载数量。

4.不要安装MSI包(Windows)

在构建Windows镜像时,尽量避免使用MSI包管理器。因其对空间的利用率不高,会大幅增加镜像的体积。