如今GitHub 仓库中已经包含了成千上万的Dockerfile,但并不是所有的Dockerfile都是高效的。本文将从四个方面来介绍Dockerfile的最佳实践,以此来帮助大家编写更优雅的Dockerfile。如果你是Docker的初学者,恭喜你,这篇文章就是为你准备的。

  本文使用一个基于 Maven 的 Java 项目作为示例,然后不断改进 Dockerfile 的写法,直到最后写出一个最优雅的 Dockerfile。中间的所有步骤都是为了说明某一方面的最佳实践。

1.减少构建时间

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

  构建顺序影响缓存的利用率

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

  只拷贝需要的文件,防止缓存溢出

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

  最小化可缓存的执行层

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

2.减小镜像体积

  镜像的体积很重要,因为镜像越小,部署的速度更快,攻击范围越小。

  删除不必要依赖

  删除不必要的依赖,不要安装调试工具。如果实在需要调试工具,可以在容器运行之后再安装。某些包管理工具(如 apt)除了安装用户指定的包之外,还会安装推荐的包,这会无缘无故增加镜像的体积。apt可以通过添加参数 --no-install-recommends 来确保不会安装不需要的依赖项。如果确实需要某些依赖项,请在后面手动添加。

  删除包管理工具的缓存

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

  当然了,还有其他更高级的方法可以用来减小镜像体积,如下文将会介绍的多阶段构建。接下来我们将探讨如何优化Dockerfile 的可维护性、安全性和可重复性。

3.可维护性

  尽量使用官方镜像

  使用官方镜像可以节省大量的维护时间,因为官方镜像的所有安装步骤都使用了最佳实践。如果你有多个项目,可以共享这些镜像层,因为他们都可以使用相同的基础镜像。

  使用更具体的标签

  基础镜像尽量不要使用latest标签。虽然这很方便,但随着时间的推移,latest镜像可能会发生重大变化。因此在Dockerfile中最好指定基础镜像的具体标签。我们使用openjdk 作为示例,指定标签为8。其他更多标签请查看官方仓库。

  使用体积最小的基础镜像

  基础镜像的标签风格不同,镜像体积就会不同。slim风格的镜像是基于 Debian发行版制作的,而alpine风格的镜像是基于体积更小的Alpine Linux发行版制作的。其中一个明显的区别是:Debian 使用的是GNU项目所实现的C语言标准库,而Alpine使用的是 Musl C 标准库,它被设计用来替代 GNU C 标准库(glibc)的替代品,用于嵌入式操作系统和移动设备。因此使用Alpine在某些情况下会遇到兼容性问题。以openjdk 为例,jre风格的镜像只包含Java运行时,不包含SDK,这么做也可以大大减少镜像体积。

4.重复利用

  到目前为止,我们一直都在假设你的jar包是在主机上构建的,这还不是理想方案,因为没有充分利用容器提供的一致性环境。例如,如果你的Java应用依赖于某一个特定的操作系统的库,就可能会出现问题,因为环境不一致(具体取决于构建jar包的机器)。

  在一致的环境中从源代码构建

  源代码是你构建Docker镜像的最终来源,Dockerfile里面只提供了构建步骤。

  首先应该确定构建应用所需的所有依赖,本文的示例Java应用很简单,只需要Maven和JDK,所以基础镜像应该选择官方的体积最小的maven镜像,该镜像也包含了JDK。如果你需要安装更多依赖,可以在RUN指令中添加。pom.xml文件和src文件夹需要被复制到镜像中,因为最后执行mvn package 命令(-e 参数用来显示错误,-B 参数表示以非交互式的“批处理”模式运行)打包的时候会用到这些依赖文件。

  虽然现在我们解决了环境不一致的问题,但还有另外一个问题 :** 每次代码更改之后,都要重新获取一遍 pom.xml 中描述的所有依赖项。**下面我们来解决这个问题。

  在单独的步骤中获取依赖项

  结合前面提到的缓存机制,我们可以让获取依赖项这一步变成可缓存单元,只要pom.xml文件的内容没有变化,无论代码如何更改,都不会破坏这一层的缓存。上图中两个COPY 指令中间的RUN 指令用来告诉Maven只获取依赖项。

  现在又遇到了一个新问题:跟之前直接拷贝jar包相比,镜像体积变得更大了,因为它包含了很多运行应用时不需要的构建依赖项。

  使用多阶段构建来删除构建时的依赖项

  多阶段构建可以由多个FROM指令识别,每一个FROM语句表示一个新的构建阶段,阶段名称可以用AS参数指定。本例中指定第一阶段的名称为builder,它可以被第二阶段直接引用。两个阶段环境一致,并且第一阶段包含所有构建依赖项。

  第二阶段是构建最终镜像的最后阶段,它将包括应用运行时的所有必要条件,本例是基于Alpine的最小JRE镜像。上一个构建阶段虽然会有大量的缓存,但不会出现在第二阶段中。为了将构建好的jar包添加到最终的镜像中,可以使用COPY --from=STAGE_NAME指令,其中STAGE_NAME是上一构建阶段的名称。

  多阶段构建是删除构建依赖的首选方案。

  本文从在非一致性环境中构建体积较大的镜像开始优化,一直优化到在一致性环境中构建最小镜像,同时充分利用了缓存机制。还有多阶段构建等方式来优化。

Dockerfile优化方式的更多相关文章

  1. ListView常见的优化方式简述

    ListView的优化 对于ListView来说,应该算是布局中几种最常用的组件之一了,使用也十分方便,下面个大家介绍一下两种常见的优化方式. 1.条目复用优化 其实listview的工作原理就是,l ...

  2. hive join的三种优化方式

    原网址:https://blog.csdn.net/liyaohhh/article/details/50697519 hive在实际的应用过程中,大部份分情况都会涉及到不同的表格的连接, 例如在进行 ...

  3. Oracle 优化方式

    Oracle的优化器有两种优化方式,即基于规则的优化方式(rule-based optimization 简称RBO)和基于代价的优化方式(cost-based optimization 简称CBO) ...

  4. 关于SPFA算法的优化方式

    关于SPFA算法的优化方式 这篇随笔讲解信息学奥林匹克竞赛中图论部分的求最短路算法SPFA的两种优化方式.学习这两种优化算法需要有SPFA朴素算法的学习经验.在本随笔中SPFA朴素算法的相关知识将不予 ...

  5. 常见 Web 性能优化方式

    这篇文章是我阅读 Web Performance 101 之后的进行的粗糙的翻译作为笔记,英语还行的童鞋可以直接看原文. 这篇文章主要介绍了现代 web 加载性能(注意不涉及代码算法等),学习为什么加 ...

  6. MySQL性能优化(一):优化方式

    原文:MySQL性能优化(一):优化方式 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/v ...

  7. 90%的开发者都不知道的UI本质原理和优化方式

    前言 很多开发者在工作中一直和UI打交道,所以认为UI非常的简单! 事实上对于90%的开发者来说,不知道UI的本质原理. 虽然在开发中,我们在接到产品的UI需求之后,可以走捷径照抄大型APP代码,但是 ...

  8. Redis数据类型内部编码规则及优化方式

    Redis的每个键值都是使用一个redisObject结构体保存的,redisObject的定义如下: typedef struct redisObject { unsigned type:4; un ...

  9. 『现学现忘』Docker基础 — 32、通过DockerFile的方式挂载数据卷

    目录 1.简单了解一下DockerFile 2.通过DockerFile的方式挂载数据卷 (1)创建DockerFile文件 (2)编辑Dockerfile文件 (3)构建Dokcer镜像 (4)启动 ...

随机推荐

  1. 完美解决MSSQL安装问题“Polybase要求安装Oracle JRE 7更新51(64位)”方案

    阅文时长 | 0.72分钟 字数统计 | 1164.8字符 主要内容 | 1.问题起因及解决方案 2.安装jdk-8u241-windows-x64 3.取消PolyBase查询服务 4.四.声明与参 ...

  2. 利用Typora+PicGo+OSS+Git+Github/码云搭建个人云笔记

    搭建个人云笔记 对于程序员来讲,使用markdown可以帮助我们快速地写作,书写出排版好看,颜值高的笔记,使用markdown来书写文档相比于使用word要简单快捷的多 市面上有很多markdown的 ...

  3. WPS-插入-公式-菜单 怎样在EXCEL中使用PRODUCT函数

    怎样在EXCEL中使用PRODUCT函数 ################   WPS2018 -插入-公式-[专门有公式菜单] 插入函数       ##################       ...

  4. 《SystemVerilog验证-测试平台编写指南》学习 - 第1章 验证导论

    <SystemVerilog验证-测试平台编写指南>学习 - 第1章 验证导论 测试平台(testbench)的功能 方法学基础 1. 受约束的随机激励 2. 功能覆盖率 3. 分层的测试 ...

  5. BUUCTF(十一)[极客大挑战 2019]Knife

    BUUCTF系列 想着应该不会这么简单吧... 结果就是这么简单ee 疯狂暗示... url:xxx/index.php 密码:Syc 连接成功... cd / ls cat flag

  6. inux软件安装管理之——dpkg与apt-*详解

    inux软件安装管理之--dpkg与apt-*详解 Nosee123关注 0.5922017.09.12 17:47:44字数 3,894阅读 8,565 [Linux软件安装管理系列]- - 传送门 ...

  7. Linux进阶之seq,pidof,wget,curl,tr,grep命令

    本节内容 seq  pidof  wget  curl  tr  grep 1.seq(sequence) 生成数列 例子1:指定结束位置 [root@renyz ~]# seq 5 1 2 3 4 ...

  8. INFJ名言

    财富是由什么构成的? 按世俗的观点,就是占有金钱和财宝. 但如果我们用除金钱之外的其他方式来衡量财富, 那么许多在物质上匮乏的人在精神上却是富有的, 许多在物质上富有的人在精神上却是匮乏的. The ...

  9. python3 smtplib发送邮件

    使用smtp包发送邮件还依赖email的一些方法 发送邮件主要分为三步: 1,定义邮箱参数:邮箱服务器地址,邮箱用户名,邮箱密码,邮件发送方,邮件接收方,邮件标题,邮件内容 2,配置发送内容 3,实例 ...

  10. linux环境下时区无法设置(UTC无法更改为CST)的问题解决

    在进行linux下修改时区的时候 总是修改不了 修改成 Asia/Shanghai  但是 时区总是 +0000 却不是想要的+0800 按照网上的方法 A方法:tzselect:执行tzselect ...