Docker 17引入了一项名为多阶段构建的新功能,大大简化了优化Docker映像的过程。这篇文章概述了多阶段构建,以及如何使用它们来简化Dockerfile并大大减小映像大小。🥳。
来自戈朗:1.14-编译时的高山ADD。.run go build-o myapp.#将工件复制到最小运行时映像FROM alpine:3.11COPY--from=编译/app/myapp./myappCMD[";./myapp";]。
减小Docker镜像大小的最简单方法是将依赖项分为编译和运行时两类,然后在使用后移除编译时依赖项。
不幸的是,依赖项清理非常困难,并且很快就变成了维护的噩梦。多阶段构建完全消除了清除依赖项的需要。✨。
Docker的多阶段构建提供了使用独立映像进行编译和运行时的能力,从而绕过了依赖项清理。这意味着我们可以简单地将必要的构建构件从编译映像(非常大)复制到不同的(非常小)运行时映像。
#~#来自Golang的编译映像(~500MB)#:1.14-阿尔卑斯山作为后端#添加源码ADD。。#执行编译运行Go build-o myapp。#~##Runtime Image(~30 MB)#来自Alpine:3.11#Very Minimal Image#从以前的映像COPY--from=backend/app/myapp./myappCMD[";./myapp";]编译成二进制文件COPY--from=backend/app/myapp./myappCMD[";./myapp";]。
作为参考,高山码头图像为6MB,Golang:Alpine图像为300MB。仅通过从不同的基本映像开始即可节省294MB的空间-不需要依赖项管理!🤯。
#~#编译和运行时图像(490MB)#来自Golang:1.14-alpineWORKDIR/appADD。.RUN Go build-o myapp.CMD[";./myapp";]
在上面的GO示例中使用多阶段构建将我们的映像大小从490MB降到只有26MB!在你兴奋之前,重要的是要记住,通过仔细管理和清理依赖关系,我们本可以达到类似的结果,但老实说,如果没有几个小时的不必要的研究,我甚至不知道如何做到这一点。多阶段构建是更好的解决方案。
多阶段构建不一定是线性的。例如,大多数Web应用程序需要编译多个组件,其中最明显的划分是前端和后端。要实现这一点,我们可以为每个位置创建一个编译映像,然后将两个位置的资源复制到最终的运行时映像中。
这里是我的个人博客中的一个真实示例,它使用基于节点的映像来构建前端资产,使用Golang基本映像来构建后端服务器。从那里,运行时映像将编译的工件复制到自身。
#~~~#前端图像#来自节点:12-高山作为前端ADD。./appWORKDIR/APP/FrontendRun NPM install&;&;npm运行Build#~~~#Backend Image#from golang:1.14-alpine as backendWORKDIR/appADD。.RUN Go Install./...#~~~#Runtime Image#from Alpine:3.11COPY--FROM=前端/APP/前端/静态。/前端/静态COPY--FROM=后端/GO/BIN/WEB。/WebCMD[";./WEB";]。
上面的示例使用了Go,因为它在运行时依赖项之间有明确的分离,但是对于更复杂的情况又如何呢?例如,Python需要Python解释器才能执行。Python包还经常依赖于C库来执行诸如数据库交互和图像操作之类的性能关键型任务。一般来说,Python的策略与Go相同,但由于Python应用程序有更多的运行时依赖项需要考虑,这使得策略变得复杂。
#~~~#编译镜像#来自Python:3.8-alpine as编译#编译Python包所需的安装依赖项Run apk add--no-cache postgreSQL-dev libffi-dev MUSL-dev GCC#创建PythonVirtualenv并使用它运行python-m venv/opt/venvENV path=";/opt/venv/bin:$path";#add requirements fileADD requirements.txt.#安装要求(通过不缓存来节省空间)运行pip install-r requirements.txt--no-cache-dir#~~~#运行时映像##注意:Python库很大,但需要FROM python:3.8-alpine#安装所需的运行时依赖项Run apk add--无缓存postgreSQL-libs#将代码复制到映像中#提示:使用.dockerIgnore排除大项目或机密COPY。.#从viralenv复制依赖项并使用itCOPY--from=编译/opt/venv/opt/venvENV path=";/opt/venv/bin:$path";#定义命令以运行Django appCMD Daphne--port 8000--bind 0.0.0.0 project.asgi:application。
您将注意到,分离Python应用程序的编译和运行时依赖项比Go复杂得多。运行时映像的大小约为200MB,而我们的GO映像约为20MB。这是解释型语言需要更多依赖项的一个不幸的副作用,而且无法真正避免。然而,它仍然比使用未经优化的Python映像要好,后者将超过500MB。
Docker的.dockerIgnore应该与多阶段构建结合使用。使用添加或复制命令将文件添加到映像时,可以使用.dockerIgnore排除包含敏感机密的不必要文件夹或文件。缓存目录、Git数据和文档等内容占用大量空间,应该忽略。最重要的是,应该忽略包含秘密或敏感信息的文件,这样它们就不会散布在图像内部(特别是如果它是公开可访问的)。
这真的是所有关于Docker的多阶段构建的知识。这是我最喜欢的现代Docker功能之一,我仍然不敢相信与过去的糟糕时代相比,它做了这么多优化。