您当前的位置:首页 > 电脑百科 > 程序开发 > 容器

超薄Docker容器-减少Docker镜像大小的指南

时间:2021-04-02 09:54:22  来源:  作者:
超薄Docker容器-减少Docker镜像大小的指南

Photo by William Warby on Unsplash

您是否曾经想过为什么您的单应用程序Docker容器会增长到400 MB? 或者,也许为什么一个只有几十MB的应用程序二进制文件会生成一个MB的Docker映像?

在本文中,我们将回顾一些导致容器变胖的主要因素,以及为您的项目提供超薄Docker容器的最佳实践和技巧。

 

Docker镜像层

Docker容器镜像本质上是堆积的文件,稍后将被实例化为正在运行的容器。 Docker利用联合文件系统(UnionFS)设计,其中文件按层分组在一起。 每一层可能包含一个或多个文件,并且每一层都位于上一层的顶部。 作为最终用户,我们体验了作为统一文件系统的所有层的所有内容的虚拟运行时合并:

超薄Docker容器-减少Docker镜像大小的指南

A simplified view of UnionFS (Image by the author)

UnionFS的底层实现向我们提供的最终文件系统视图(Docker通过可插拔存储驱动程序支持许多不同的视图)具有其所构成的所有层的总大小。 Docker为图像创建容器时,它将以只读格式使用镜像的所有层,并在它们之上添加一个薄的读写层。 这个薄的读写层使我们能够实际修改正在运行的Docker容器中的文件:

超薄Docker容器-减少Docker镜像大小的指南

A running container adds a read-write layer on top of an image's read-only layers.

如果在上述第4层中删除了文件,会发生什么情况? 尽管已删除的文件不会再出现在观察到的文件系统中,但是由于该文件包含在较低的只读层中,因此它最初占用的大小仍将是容器占用空间的一部分。

从一个小的应用程序二进制文件开始到以一个胖容器镜像结束是相对容易的。 在以下各节中,我们将探索不同的方法来使镜像的尺寸尽可能地薄。

提防构建路径

我们构建Docker镜像的最常见方式是什么?

docker build .

上面的命令告诉Docker我们将当前的工作文件夹视为构建过程的根文件系统路径。

为了更好地理解发出上述命令时实际发生的情况,我们应该记住,Docker构建是一个客户端-服务器进程。 我们从中执行docker build命令的Docker CLI(客户端)使用基础Docker引擎(服务器)来构建容器镜像。 为了限制对客户端基础文件系统的访问,构建过程需要知道虚拟文件系统的根目录是什么。 正是在此确切路径下,Dockerifle中的任何命令都试图查找可能最终在正在生成的镜像中结束的文件资源。

让我们考虑一下我们通常放置Dockerfile的位置。 在项目的根源中,也许吧? 好吧,将项目根目录中的Dockerfile与Docker构建相结合,我们已经有效地添加了完整的项目文件夹作为构建的潜在文件资源。 这可能会导致在构建上下文中不必要地添加多个MB和数千个文件。 如果我们不小心在Dockerfile中定义了ADD / COPY命令,则所有这些文件都可以成为最终镜像的一部分。 在大多数情况下,这不是我们所需要的,因为最终容器镜像中仅应包含一些选定的项目人工制品。

始终检查是否为Docker构建提供了适当的构建路径,并且Dockerfile没有向镜像添加不必要的文件。 如果出于任何原因确实需要将项目的根目录定义为构建上下文,则可以通过.dockerignore有选择地包括/排除文件。

挤压图像

命令合并的另一种方法是使用Docker的squash命令构建镜像,尤其是在使用您不希望或无法修改的其他Dockerfile时。

除非您使用的是非常老的Docker版本(<1.13),否则Docker允许我们将所有层压缩为一个层,从而有效地删除所有虚幻资源。 我们仍然可以将原始的,未更改的Dockerfile与许多单独的命令一起使用,但是这次我们通过--sqash选项执行构建:

docker build --squash .

再次对生成的镜像进行100%优化:

超薄Docker容器-减少Docker镜像大小的指南

A 100% optimised image with image squash (Image by the author)

这里需要注意的有趣一点是,由于我们的Dockerfile创建了一个添加文件的层,然后创建了另一个删除该文件的层,所以squash足够聪明,以至于无需创建任何层(我们只有9ccd9…层 我们正在使用的基本图片)。 然后,额外的荣誉就可以南瓜了。 但是,请注意,挤压图层可能会阻止您或您的镜像用户利用先前缓存的图层。

注意:使用您不想更改的第三方Dockerfile时,一种最小化任何可能浪费空间的快速简便方法是使用--squash构建它。 您可以使用潜水工具检查图像的最终效率。

命令合并

您是否见过带有RUN指令非常长的Dockerfile,其中多个Shell命令与&&聚合在一起? 命令合并。

通过合并命令,我们实际上是根据此单个long命令的结果创建了一个单独的层。 由于不存在用于添加文件并随后在另一层中删除文件的中间层,因此最后一层将不会为此类幻影文件使用任何空间。 让我们通过修改上述Dockerfile来了解这一点:

FROM alpineRUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmp && rm /tmp/10MB.zip

现在我们有了一个优化的镜像:

超薄Docker容器-减少Docker镜像大小的指南

A 100% optimised image with commands merge (Image by the author)

当您完成构建Dockerfile时,请检查它以查看是否可以合并命令以减少可能的浪费空间。

标准化镜像层

如果基础存储驱动程序支持,则镜像可以具有的最大层数为127。 如果确实需要,可以增加此限制,但是随后您可以缩小构建该映像的位置的选择(即,您需要在类似修改的基础内核上运行的Docker引擎)。

正如上面有关Docker镜像层的部分中所讨论的,由于UnionFS,进入层的任何文件资源都保留在该层中,即使您在后一层中管理该文件也是如此。 我们来看一个示例Dockerfile:

FROM alpineRUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmpRUN rm /tmp/10MB.zip

构建以上镜像:

超薄Docker容器-减少Docker镜像大小的指南

Building a sample image with wasted space (Image by the author)

并进行潜水检查:

超薄Docker容器-减少Docker镜像大小的指南

Image is only 34% efficient (Image by the author)

效率为34%表示图像中浪费了很多空间。 这将导致更长的图像获取时间,额外的带宽消耗和更慢的启动时间。

我们如何摆脱这个浪费的空间?

删除缓存

通常,当我们将应用程序容器化时,我们需要使用软件包管理器(例如apk,yum或apt)在生成的映像上提供额外的工具,库或实用程序。

当我们通过缓存先前获取的软件包来安装软件包时,软件包管理器试图为我们节省时间和带宽。 为了使生成的Docker映像的尺寸尽可能小,我们不需要保留程序包管理器缓存。 毕竟,如果我们的容器需要其他映像,我们总是可以使用更新的Dockerfile重建映像。

要删除上述三个流行的软件包管理器的软件包管理器缓存,我们可以在聚合(即命令合并)命令的末尾添加以下命令,例如:

APK: ... && rm -rf /etc/apk/cacheYUM: ... && rm -rf /var/cache/yumAPT: ... && rm -rf /var/cache/apt

 

注意:在最终确定Docker镜像之前,请不要忘记删除构建期间使用的所有缓存以及容器正常运行所不需要的任何其他临时文件。

选择基础镜像

每个Dockerfile都以FROM指令开头。 在此定义我们将在其上创建自己的图像的基础图像。

如Docker文档中所述:

" FROM指令初始化一个新的构建阶段,并为后续指令设置基础映像。 因此,有效的Dockerfile必须以FROM指令开头。 该图像可以是任何有效的图像-从公共存储库中提取图像特别容易。"

显然,有很多不同的基础图像可供选择,每个基础图像都有自己的优势和功能。 当涉及到您自己的Docker映像的最终大小时,选择一个足以提供应用程序运行所需的工具和环境的映像至关重要。

正如您所期望的那样,不同的流行基本镜像的大小差异很大:

超薄Docker容器-减少Docker镜像大小的指南

Popular Docker base images size (Image by the author)

实际上,使用Ubuntu 19.10基本映像对应用程序进行容器化将至少增加73 MB,而使用Alpine 3.10.3基本映像的完全相同的应用程序只会使大小增加6 MB。 随着Docker缓存图像层,下载/带宽损失仅在您第一次使用该图像启动容器时适用(或者简单地,在拉取图像时)。 但是,增加的大小仍然存在。

此时,您可能已经得出以下(非常合逻辑的)结论:"那么,我将永远使用Alpine!"。 如果在软件中只有那么清楚的话。

您会发现,Alpine linux背后的家伙还没有发现Ubuntu或Debian家伙仍在寻找的特殊秘密调味料。 为了能够创建比Debian小(例如)小的数量级的Docker映像,他们必须对Alpine映像中要包含的内容和不包含的内容做出一些决定。 在选择Alpine作为默认基本映像之前,应检查它是否提供了所需的所有环境。 此外,即使Alpine随附了软件包管理器,您也可能会发现Alpine中不提供您在(例如)基于Ubuntu的开发环境中使用的特定软件包或软件包版本。 在为项目选择最合适的基础映像之前,您应该了解这些权衡并进行测试。

最后,如果您确实需要使用一个较胖的基础镜像,则可以使用镜像最小化工具(例如免费和开源DockerSlim)来减小最终映像的大小。

注意:在尝试减小尺寸时,为自己的镜像选择适当的基础镜像很重要。 评估您的选择并选择一张镜像,该镜像可提供您所需的工具,以确保您可以承受的尺寸。

完全不选择基础镜像

如果您的应用程序可以在没有基础映像提供任何其他环境的情况下运行,则可以选择完全不使用基础映像。 当然,由于FROM在Dockerfile中是强制性的,因此您仍然必须拥有它并将其指向某个内容。 在这种情况下,您应该使用什么?

从头开始,即:

"一个明显为空的图像,特别是对于构建图像"从零开始"。 在构建基础映像(例如debian和busybox)或超小型映像(仅包含一个二进制文件以及它所需要的任何内容,例如hello-world)的上下文中,此映像最有用。 从头开始是Dockerfile中的一项禁止操作,并且不会在映像中创建额外的层。"

注意:如果您的应用程序包含可以以独立方式运行的自包含可执行文件,则选择暂存基础映像可以使您尽可能减少容器的占用空间。

多阶段构建

当Docker 17.05可用时,多阶段构建成为关注的焦点。 期待已久的功能,多阶段构建允许镜像构建器将自定义镜像构建脚本抛在后面,并将所有内容集成到众所周知的Dockerfile格式中。

用高级术语来说,您可以将多阶段构建视为将多个Dockerfile合并在一起,或者简单地将一个具有多个FROM的Dockerfile合并。

在进行多阶段构建之前,如果要构建项目的工件并使用Dockerfile将其分发到容器中,则可能必须遵循一个构建过程,最终以一个如下图所示的容器结束:

超薄Docker容器-减少Docker镜像大小的指南

Building and distributing your Application without multi-stage builds (Image by the author)

尽管上述过程在技术上没有任何问题,但最终镜像和生成的容器在构建/准备项目人工制品时创建的层上都layers肿了,这些层对于项目的运行时环境不是必需的。

多阶段构建使您可以将创建/准备阶段与运行时环境分开:

超薄Docker容器-减少Docker镜像大小的指南

Multi-stage builds, separation creation/preparation from runtime (image by author)

您仍然可以使用单个Dockerfile定义完整的构建工作流程。 但是,您可以将文物从一个阶段复制到另一个阶段,同时将数据丢弃在不需要的层中。

注意:多阶段构建允许您创建跨平台的可重复构建,而无需使用特定于操作系统的自定义构建脚本。 通过有选择地包括在构建的前几个阶段中生成的伪像,可以使镜像的最终大小保持最小。

结论

为容器创建Docker镜像是现代软件工程师必须经常处理的过程。 有大量在线资源和示例向您展示如何创建Dockerfile,但是,您应注意生成的映像的大小。

在本文中,我们回顾了一些方法和技巧,以最大程度地减少Docker镜像的最终大小。 通过精心制作仅包含必要工件的Dockerfile,选择合适的基础映像并使用多阶段构建,可以大大减少Docker映像的最终大小。

 

(本文翻译自Nassos Michas的文章《Super-Slim Docker Containers》,参考:
https://medium.com/better-programming/super-slim-docker-containers-fdaddc47e560)



Tags:Docker容器   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
对于初学者来说,刚刚接触docker会有点迷,特别是镜像与容器。其实我们可以理解镜像与容器为一对多的关系。下图错误的示范,为什么是错误的呢?因为可以通过一个镜像确实可以启动...【详细内容】
2021-06-09  Tags: Docker容器  点击:(136)  评论:(0)  加入收藏
Photo by William Warby on Unsplash您是否曾经想过为什么您的单应用程序Docker容器会增长到400 MB? 或者,也许为什么一个只有几十MB的应用程序二进制文件会生成一个MB的Docke...【详细内容】
2021-04-02  Tags: Docker容器  点击:(233)  评论:(0)  加入收藏
虚拟技术和云飞速发展的今天,云和容器已经深入人心,每个IT人都或多或少的使用容器和云。但是用归用,很多人对其底层的原理确实知之甚少。很多人想把容器当成虚拟机来用,却遭遇大...【详细内容】
2021-03-10  Tags: Docker容器  点击:(230)  评论:(0)  加入收藏
前言我之前发过几篇文章,比如搭建Zabbix监控系统、搭建ELK日志分析平台等,那有不少网友就给我留言了,说不用这么麻烦,用Docker部署就可以了,分分钟就搞定的事情。Docker真的有这...【详细内容】
2021-01-12  Tags: Docker容器  点击:(158)  评论:(0)  加入收藏
现在的项目部署运维中,使用Docker容器越来越多,Docker给我们提供创建容器的方式也有好几种,主要是docker run命令、Dockerfile文件、docker-compose三种方式,一个比一个方便。我...【详细内容】
2020-11-20  Tags: Docker容器  点击:(113)  评论:(0)  加入收藏
你是否还在大量控制台窗口中监控容器,还是对使用终端命令充满热情?而使用Docker的图形用户界面(GUI)工具,则可以更简单的对容器进行管理,并提高效率。而且它们都是免费的。Portain...【详细内容】
2020-11-16  Tags: Docker容器  点击:(114)  评论:(0)  加入收藏
本篇文章我们将讲解跨主机网络的容器通信实现,涉及到Docker overlay网络与MacVlan网络的解决方案,下面我们就一起来学习吧!01 Docker跨主机网络通信跨主机的容器网络方案主要有...【详细内容】
2020-10-26  Tags: Docker容器  点击:(92)  评论:(0)  加入收藏
接着文章「系统架构」如何使用Dockerfile制作Docker容器?(1)我们继续介绍ENV、ARG、VOLUME、EXPOSE、WORKDIR、USER、HEALTHCHECK、ONBUILD几个命令。7、ENV这个指令很简单,就...【详细内容】
2020-08-16  Tags: Docker容器  点击:(77)  评论:(0)  加入收藏
Docker让开发者可以打包应用程序及依赖包到可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,也可以实现虚拟化。许多开发者关心如何在Docker容器中实现软件加密及授...【详细内容】
2020-08-07  Tags: Docker容器  点击:(98)  评论:(0)  加入收藏
1 前言了解Docker容器的运行环境非常重要,我们把应用放在容器里执行,环境变量会直接影响程序的执行效果。所以我们要知道容器内部的环境变量,也要知道如何改变这些环境变量。2...【详细内容】
2020-08-05  Tags: Docker容器  点击:(66)  评论:(0)  加入收藏
▌简易百科推荐
一、为什么要搭建主从架构呢1.数据安全,可以进行数据的备份。2.读写分离,大部分的业务系统来说都是读数据多,写数据少,当访问压力过大时,可以把读请求给到从服务器。从而缓解数据...【详细内容】
2021-12-15  实战Java    Tags:Docker   点击:(10)  评论:(0)  加入收藏
在网页中渲染公式一直是泛学术工具绕不开的一个功能,最近更新产品功能,正巧遇到了这个需求,于是使用容器方式简单实现了一个相对靠谱的公式渲染服务。分享出来,希望能够帮到有类...【详细内容】
2021-12-01  编程菌zfn    Tags:Docker   点击:(10)  评论:(0)  加入收藏
1.1 docker命令直接部署1.1.1 拉取镜像docker pull wurstmeister/zookeeperdocker pull wurstmeister/kafka1.1.2 启动zookeeper容器docker run -d --name myzookeeper -p 2...【详细内容】
2021-11-15  无    Tags:docker   点击:(47)  评论:(0)  加入收藏
01 前言 顺着docker的发展,很多测试的同学也已经在测试工作上使用docker作为环境基础去进行一些自动化测试,这篇文章主要讲述我们在docker中使用浏览器进行自动化测试如果可以...【详细内容】
2021-10-29  小码哥聊软件测试    Tags:Docker   点击:(42)  评论:(0)  加入收藏
因为你懂得的原因,下载docker镜像速度非常喜感,故收集几个国内常用的docker镜像。Docker中国区官方镜像地址:https://registry.docker-cn.com网易163的镜像http://hub-mirror.c...【详细内容】
2021-10-28  抓蛙程序猿    Tags:docker   点击:(48)  评论:(0)  加入收藏
环境:Spring5.3.10通常,应用程序开发人员不需要对ApplicationContext实现类进行子类化。相反,SpringIOC容器可以通过插入特殊集成接口的实现来扩展。使用BeanPostProcessor自定...【详细内容】
2021-10-26  Java网络研发架构师    Tags:Spring   点击:(33)  评论:(0)  加入收藏
我们在很多场景下都需要做笔记,来对抗遗忘,一份好的笔记不仅能在需要的时候供我们查阅,也能帮助我们归纳整理知识提高做事效率。 目前市面上有很多云笔记软件,体验上各有不同,但...【详细内容】
2021-10-11  运维贼船    Tags:docker   点击:(61)  评论:(0)  加入收藏
1. Nacos官网Nacos Docker 快速开始2. Clone 项目git clone https://github.com/nacos-group/nacos-docker.git3. cd 到nacos-docker 路径下 直接启动即可cd nacos-dockerdo...【详细内容】
2021-09-16  程序狗爱化妆    Tags:Nacos   点击:(109)  评论:(0)  加入收藏
今天不做保姆级教程,分享奶爸常用、好用的Docker应用。有了这些Docker,Nas的可玩性会大幅提高,有时候奶爸也在想,刨去官方套件不考虑的话,Nas真的是差不多。如果小伙伴们有需要,后...【详细内容】
2021-09-03  晋升奶爸的垃圾佬    Tags:Docker   点击:(167)  评论:(0)  加入收藏
环境要求 ubuntu系统:20.04 docker版本:20.10.7 redis版本:6.0.6步骤由于我这里已经有相应的redis镜像,这里就不记录了,关于docker一些基础知识可以看我以前的笔记开启3台re...【详细内容】
2021-07-26  石老师小跟班    Tags:Redis主从复制   点击:(117)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条