S_lion's Studio

如何优雅的编写dockerfile

字数统计: 1.3k阅读时长: 4 min
2021/09/02 Share

写一个能够运行起来的的dockerfile是比较简单的,而写一个“产品化程度高”的dockerfile是需要设计的。

操作简易的dockerfile意味着几乎任何命令都能成功执行,以致于镜像动不动就1个多G好几百M,要知道的是镜像越大,拉取的速度也会随之增加,对于系统的开销也就越大。dockerhub上有上万个镜像,我们可以随意的拿来作为基础镜像,与之伴随的就是潜在的安全隐患,某些特殊的场景下,比如金融、政府等领域,对于安全性要求特别高,这种随意的镜像制作手法是不被允许的。下文会讲讲如何优雅的构建镜像。

减少构建时间

一个开发周期包括构建 Docker 镜像,更改代码,然后重新构建 Docker 镜像。在构建镜像的过程中,如果能够充分利用docker的缓存机制,可以减少不必要的重复构建步骤。

设计高效的构建顺序

镜像的构建顺序很重要,当你向 Dockerfile 中添加文件,或者修改其中的某一行时,那一部分的缓存就会失效,该缓存的后续步骤都会中断,需要重新构建。所以优化缓存的最佳方法是把不需要经常更改的行放到最前面,更改最频繁的行放到最后面。

只拷贝需要的文件

当拷贝文件到镜像中时,尽量只拷贝需要的文件,如果被拷贝的文件内容发生了更改,缓存就会被破坏。在上面的示例中,镜像中只需要构建好的 jar 包,因此只需要拷贝这个文件就行了,这样即使其他不相关的文件发生了更改也不会影响缓存。

缓存层最小化

每一个 RUN 指令都会被看作是可缓存的执行单元。太多的 RUN 指令会增加镜像的层数,增大镜像体积,而将所有的命令都放到同一个 RUN 指令中又会破坏缓存,从而延缓开发周期。当使用包管理器安装软件时,一般都会先更新软件索引信息,然后再安装软件。推荐将更新索引和安装软件放在同一个 RUN 指令中,这样可以形成一个可缓存的执行单元,否则你可能会安装旧的软件包。

减少网络传输时间

ADD命令可以下载外网URL地址的资源,但由于网络环境性能差异,或者根本就是无网环境,推荐提前将所需的资源包下载下来使用,减少网络对构建过程的影响。

减小镜像体积

如果之前接触过k8s就知道,海量集群中有节点故障是常有的事儿,故障节点上的服务会漂移到其他正常节点,此时如果正常节点没有这些服务的镜像就需要从仓库中拉取,而镜像小也就意味着拉取时间短,恢复时间快,一个镜像时看不出来,如果是需同时下载多个镜像就会给系统造成不小的开销。

删除不必要的依赖

删除不必要的依赖,没有硬性需求不安装调试工具。某些包管理工具(如 yum)除了安装用户指定的包之外,还会安装一些弱依赖包,这会增加镜像的体积。yum 可以通过添加参数 --setopt=install_weak_deps=false 来确保不会安装弱依赖的包,--setopt=tsflags=nodocs参数来取消文档包的安装。

包管理工具会维护自己的缓存,这些缓存会保留在镜像文件中,推荐的处理方法是在每一个 RUN 指令的末尾删除缓存。如果你在下一条指令中删除缓存,不会减小镜像的体积。

可维护性

使用官方镜像

使用官方镜像可以节省大量的维护时间,因为官方镜像的所有安装步骤都使用了最佳实践。

使用可读性高的标签

基础镜像尽量不要使用 latest 标签。虽然这很方便,但随着时间的推移,latest 镜像可能会发生重大变化。因此在 Dockerfile 中最好指定基础镜像的具体标签。当然,我们构建的镜像也同理。

使用体积最小的基础镜像

我们在dockerhub上可以看到一个镜像会有形形色色的标签。其中slim 风格的镜像是基于 Debian 发行版制作的,而 alpine 风格的镜像是基于体积更小的 Alpine Linux 发行版制作的。 以 openjdk 为例,jre 风格的镜像只包含 Java 运行时,不包含 SDK,这么做也可以大大减少镜像体积。

保持服务的默认端口

构建镜像时需保证此服务的运行端口暴露出来。

最后,在 Push 之前,切记先在本地运行一下构建好的镜像,确保服务无任何问题,以免为后期排错造成不必要的时间浪费。

CATALOG
  1. 1. 减少构建时间
    1. 1.1. 设计高效的构建顺序
    2. 1.2. 只拷贝需要的文件
    3. 1.3. 缓存层最小化
    4. 1.4. 减少网络传输时间
  2. 2. 减小镜像体积
    1. 2.1. 删除不必要的依赖
  3. 3. 可维护性
    1. 3.1. 使用官方镜像
    2. 3.2. 使用可读性高的标签
    3. 3.3. 使用体积最小的基础镜像
    4. 3.4. 保持服务的默认端口