dockerfile构建镜像优化实践

大家日常构建docker镜像时,常用dockerfile生成,而dockerhub上的dockerfile也已经超过100w了。本文将在5个方面介绍,如何在生产环境中,提高docker镜像的构建速度,减小docker镜像大小,以及增强dockerfile的可操作性、安全性和可重用性。

TIP1:提升docker镜像构建速度

在生产环境中,会经常涉及到代码修改,代码修改后,我们就需要重新构建docker镜像,而有效地利用docker缓存机制(cache),可以避免很多不必要的步骤。如下面的例子:
在这里插入图片描述
我们知道docker镜像是一层一层叠加的,因此合理地安排dockerfile每一层是非常重要的。每一层的缓存都是独立的,基于之前的cache构建生成的。因此如果某一层发生变更,则会导致当前层及其后面镜像均需要重新构建。因此建议将变化大的,放在后面,合理利用docker cache机制。

TIP2:精准拷贝

在这里插入图片描述
尽量只拷贝你需要的文件到docker镜像中,避免图方便,直接拷贝整个目录进去。因为对你的docker镜像来说,有用的可能只有app.jar这个包需要打包到镜像中,app目录下的其它文件并非必需的,如配置文件等,如果app.jar没有发生变化,docker镜像就不需要重新构建。但是如果拷贝整个app目录时,如果app目录下有配置文件被修改了,但是这个文件对你来说并不需要,此时也会导致docker镜像重新构建。

TIP3:合并RUN命令,减少层级

在这里插入图片描述
如上面的例子,update和一些基础软件包,可以合并到一条RUN命令执行,这样可以减少docker镜像层级。但是这也有负面影响,即安装软件包时,每次都得update一下,或者cache不变更的话,软件包就一直是老版本的了。

对于docker镜像大小呢:

TIP4:去除非必需依赖包

在这里插入图片描述
删除非必需的依赖包,也尽量不装debuging工具,如果需要调试,可以在实际调试过程中再安装。
当期的包管理工具,如apt,也建议用户尽量只安装特定需要的软件,如api工具,提供了-no-install-recommends的选项,让用户安装软件时,不安装非必须的依赖包。后续运行或调试用需要,再另行安装。

TIP5:删除无用的包管理缓存在这里插入图片描述

注意:删除包管理缓存,需要将rm命令和update、install命令放在同一个RUN中,如果放在另一个RUN里,是没法减小镜像大小的,这个通过docker cache机制可能很好理解。
减小docker镜像大小的方法还有很多,如使用多阶(muti-stage)镜像构建方式。可参考docker多阶镜像(multi-stage)构建

提高dockerfile可维护性:

TIP6:在这里插入图片描述

尽量使用官网提供的镜像,一方面节省时间成本,另一方面是这些镜像优化过。

TIP7:使用确切的tag在这里插入图片描述

虽然使用latest方便快捷,而且能保证镜像最新,但是如果非必需,建议使用确切tag,这样即使dockerhub上镜像更新,也不会导致本地镜像重新构建。

TIP8:找小一点的镜像

在这里插入图片描述
相同的tag,更小的flavor,则说明镜像的大小越小。slim版本基于一个精简的Debian,而alpine版本则基于更小的alpine Linux发行版镜像。一个显著的区别是debian仍然使用GNU lbc,而alpine使用musl libc,虽然要小得多,但在某些情况下可能会导致兼容性问题。在openjdk的情况下,jre flavor只包含java runtime,而不包含sdk;这也大大减少了镜像大小。

TIP9:在稳定的环境下构建docker镜像

在这里插入图片描述
如上面栗子,我们的程序需要Maven和JDK,所以让我们将Dockerfile建立在Docker Hub中一个特定的最小的官方Maven映像的基础上,其中包括JDK。如果需要安装更多依赖项,可以在运行RUN中完成安装。

TIP10:分步拉取依赖在这里插入图片描述

上面的例子中,我们可以修改pom.xml,即可指定我们需要下的代码,中间的RUN告诉maven只下载需要的依赖。

TIP11:使用多阶镜像(multi-stage)

在这里插入图片描述
[多阶镜像]提供的COPY --from=STAGE_NAME可以减少镜像构建中的依赖构建和下载。

原文