docker pull的镜像放在哪里_优化docker镜像的几种方法

51925cf8ddb7d821c130943e27f28cd9.gif

Devops和k8s的火热,越来越多的企业将docker运用到自动化运维中,不管是为了保证开发、测试、生产环境的环境一致性,还是和CI/CD工具的集成度,比如jenkins对docker或k8s的自动构建部署等,亦或利用docker进行自动化测试等

那么,在现在这种随随便便一天动辄几十次的快速构建迭代中,镜像作为一个贯穿整个自动化过程中的一个关键,怎么保证自动化构建部署的效率?就是镜像尽可能的小

要保证镜像尽可能小,可以从五个方面

  • 基础镜像小

  • 层级尽量少

  • 去除不必要

  • 复用镜像层

  • 分阶段构建

基础镜像小

基础镜像小,主要是保证镜像层底层或者说From的镜像本身小

每个企业或个人使用容器,都是应对不同的业务场景,没有完全一致的业务场景,所以你最好不要直接用别人的第三方镜像,除非你了解该镜像的所有层级内容,而且从安全角度考虑,也尽量使用官方镜像,它没有太多第三方的,你不需要的东西,你可以在此基础上增加你的业务部分内容

fbfb69b89b6381b12ca982ea447094f8.png

选择Alpine镜像代替Ubuntu、CentOS、Debian等镜像

虽然Alpine没有其他系统完备的库、依赖,但是基本的应用它都是支持的,都可以通过apk去安装(apk包管理) ,而且官方现在也推荐用Alpine,很多开源项目都有基于Alpine的官方镜像

但如果你的项目涉及到编译,比如python等涉及编译的项目,要注意,Alpine用的是musl,因为它原本是用作嵌入式系统的,所以并没有glibc那么完整的C标准库

另外如果你要在Alpine中跑一些脚本的话,那你要注意一些shell和linux下的还是有所区别的,Alpine是基于busybox的,同样也是设计于嵌入式的,所以很多shell命令做了裁剪,并不具备Ubuntu、CentOS、Debian等系统中那么完整的功能

除了使用小镜像之外,可以使用空镜像scratch,自己手动添加rootfs来构建镜像,这个不建议新手,因为你可能不知道你需要些啥,折腾半天反而比官方镜像还要大

层级尽量少

771544366995d8e4f6286ed7da3239ad.gif

前面文章有介绍docker的联合文件系统"Docker挂了,数据如何找回",Dockerfile构建镜像流程大致如下:

  • docker从基础镜像运行一个容器

  • 执行一条指令对容器进行修改

  • 执行类似docker commit 的操作提交这次修改

  • docker再基于刚提交的镜像运行一个新容器

  • 执行Dockerfile中的下一条指令,依次循环,直到命令执行完成

所以每执行一条Dockerfile中的指令,就会提交一次修改,这次修改会保存成一个只读层挂载到联合文件系统,看过上面的文章应该知道,上面层的文件,如果和下面层有冲突或不同,会覆盖隐藏底层的文件,所以每增加一层,镜像大小就会增加,虽然在docker1.10后只有RUN、COPY、ADD指令会创建层,其他指令会创建临时的中间镜像,不会直接增加构建的镜像大小   

所以在编写Dockerfile时,我们可以根据实际情况去合并一些指令,比如我们在编译安装nginx时,解压、编译、安装以及删除源文件的指令可以放在一起,以减少最终的镜像层

去除不必要

前面提到的用空镜像,或者裁剪过的小镜像来做基础镜像,其实就是一种去除不必要的依赖、库的一种形式

除了以上的这种形式,还有必要去除的,就是Dockerfile构建过程中或手动构建后commit的过程中所产生的临时文件

比如源码包、编译过程中产生的日志文件、添加的包管理仓库、包管理缓存,以及构建过程中安装的一些当时又用过后没用的软件或工具

如果可以,甚至建议不在容器中进行编译,如果二进制binary文件可以执行的话,在本地编译后,将binary文件copy到容器内

除了上面的,还有一些不常更新的文件,比如web静态资源文件css、js以及图片、视频等资源,建议存储OSS或共享存储系统nfs、mfs等,这些文件不应该打包到镜像里面,而应该通过OSS调用或通过共享存储挂载

对于不需要build进镜像的资源,可以使用.dockerignore文件进行指定要忽略的文件或目录

当然,如果你想基于别人的镜像来做优化的话,可以通过docker history命令来查看镜像的层级关系,做相应的优化,更好的工具推荐dive

2c0c34f84fc4f5958824ea95c0453a8b.gif

当然也可以用自动化的镜像瘦身工具docker-slim,它支持静态分析和动态分析,静态分析主要是通过分析镜像历史信息,获取生成镜像的dockerfile文件及相关的配置信息,而动态分析主要是通过ptrace、pevent、fanotify解析出镜像中必要的文件和文件依赖,将对应文件组织成新镜像来减小镜像体积

另外还可以通过docker-squash来压缩镜像层级,但是要考虑实际情况,并不是压缩一定是好的

复用镜像层

接上面为什么压缩不一定是好,压缩的原理是将镜像导出,然后删除所有中间层,将镜像的当前状态保存为单一层,达到压缩层级的效果

当你使用单一镜像或者少量镜像的时候可能没有太大问题,但是这样完全破坏了镜像的层级缓存功能

还是之前的文章中提的关于docker的存储的,docker镜像的每个层级会存一个hash计算后的目录,那么Dockerfile构建过程中怎么利用缓存?

在镜像的构建过程中,Docker根据Dockerfile指定的顺序执行每个指令。在执行每条指令之前,Docker都会在缓存中查找是否已经存在可重用的镜像,如果有就使用现存的镜像,不再重复创建

而如果压缩为单一的层之后,缓存就失效了,不会命中缓存的层级,所以每次构建或者pull的时候,都是整个镜像构建或pull

缓存命中除了和分层有关系,还和指令执行编排顺序有关系,首先看下缓存匹配遵循的基本规则:

  • 从一个基础镜像开始(FROM指令指定),下一条指令将和该基础镜像的所有子镜像进行匹配,检查这些子镜像被创建时使用的指令是否和被检查的指令完全一样。如果不是,则缓存失效

  • 在大多数情况下,只需要简单地对比Dockerfile中的指令和子镜像。然而,有些指令需要更多的检查和解释

  • 对于ADD和COPY指令,镜像中对应文件的内容也会被检查,每个文件都会计算出一个校验值。这些文件的修改时间和最后访问时间不会被纳入校验的范围。在缓存的查找过程中,会将这些校验和和已存在镜像中的文件校验值进行对比。如果文件有任何改变,比如内容和元数据,则缓存失效

  • 除了ADD和COPY指令,缓存匹配过程不会查看临时容器中的文件来决定缓存是否匹配。例如,当执行完RUN apt-get -y update指令后,容器中一些文件被更新,但Docker不会检查这些文件。这种情况下,只有指令字符串本身被用来匹配缓存

  • 一旦缓存失效,所有后续的Dockerfile指令都将产生新的镜像,缓存不会被使用

所以为什么和指令执行顺序和编排有关系,或者说我们在合并命令减少层级的时候不能一味的追求合并,而是需要合理的合并一些指令

举个例子,比如我们用同一个基础镜像,分别编译nginx和php,那么nginx也需要pcre库依赖,php也需要,那我们是不是可以提取共同的依赖用一条RUN指令去执行,而不是每次构建都执行

再或者最简单的,添加镜像仓库,安装基本的编译工具,比如gcc、autoconf、make、zlib等这些不常改动,但是常用的指令放在前面去执行,这样后面构建用到的所有镜像都不会再重新安装

这样合理的利用层级缓存,不管是在jenkins中自动构建镜像,还是push到远程仓库、亦或是在部署pull的时候,都能够利用缓存,从而节省传输带宽和时间

分阶段构建

最后一个更重要的是分阶段构建,或者多阶段构建,其实它也是一种减少分层或者去除不必要的一种方式,单独列出来,是觉得这个方式应该是推荐的一种方式,在docker17.05中开始支持

具体的多阶段构建,就是通过将构建过程分析,分成多个阶段来执行,后面的或者最终的构建可以使用前面构建的结果,而不需要所有的构建都包含到最终的镜像中

拿nginx构建来举个例子

38f0f6fd12389c05114ded79179b282a.png

上图是一个nginx的Dockerfile,构建之后查看大小

d378dc4186facc8bc16cd44c55e4ad84.png

然后通过多阶段构建改造Dockerfile

4452bc384a2ad268a2951ef1e2a70283.png

再构建后查看镜像大小

0d06b845746a35bf5e3d2b3bf3faca22.png

只比基础镜像多了1MB,之前的所有构建阶段as build在打包镜像的时候全部被抛弃,只最后FROM生成最终镜像

基于多阶段构建,google推出了distroless,更加轻量级,它只包含应用程序机器运行时依赖项,不包含程序管理器、shell以及标准liunx发行版中可以找到的任何其他程序

这应该就是正是我们所需要的,而我们又不会剪裁,distroless帮我们做了这个工作

还是基于刚才的nginx,我们不以rhel7为基本镜像了,我们以distroless为基本镜像构建

ebd7b1913e7d1a041a52bdb64167fcd7.png

构建后查看大小

111a74c3c274b5ca40fb0c82c38f58a4.png

通过以上几种方式,可以有效精简docker镜像,从而提高自动化运维过程中的CI/CD效率,缩短交付时间

e49350cc34cd0edbfbd829531f1c18a5.png精彩推荐Docker挂了,数据如何找回为什么你的docker容器刚启动就停了Gitlab+docker玩CI/CD

更多精彩内容请扫描下方二维码关注公众号

bb5c75b9312d4cc845a7c1587d6bddd8.png扫描二维码 299c9919237a214801bfe674ef1fdd23.png e283579ac79b7c2f1253b5b1074e5c11.png关注我们吧
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值