三个技巧,将Docker镜像体积减小90%

在构建Docker容器时,应该尽量想办法获得体积更小的镜像,因为传输和部署体积较小的镜像速度更快。

但RUN语句总是会创建一个新层,而且在生成镜像之前还需要使用很多中间文件,在这种情况下,该如何获得体积更小的镜像呢?

你可能已经注意到了,大多数Dockerfiles都使用了一些奇怪的技巧:

FROM Ubuntu RUN apt-get update && apt-get install vim

为什么使用&&?而不是使用两个RUN语句代替呢?比如:

FROM ubuntu RUN apt-get update RUN apt-get install vim

从Docker 1.10开始,COPY、ADD和RUN语句会向镜像中添加新层。前面的示例创建了两个层而不是一个。

三个技巧,将Docker镜像体积减小90%

镜像的层就像Git的提交(commit)一样。

Docker的层用于保存镜像的上一版本和当前版本之间的差异。就像Git的提交一样,如果你与其他存储库或镜像共享它们,就会很方便。

实际上,当你向注册表请求镜像时,只是下载你尚未拥有的层。这是一种非常高效地共享镜像的方式。

但额外的层并不是没有代价的。

层仍然会占用空间,你拥有的层越多,最终的镜像就越大。Git存储库在这方面也是类似的,存储库的大小随着层数的增加而增加,因为Git必须保存提交之间的所有变更。

过去,将多个RUN语句组合在一行命令中或许是一种很好的做法,就像上面的第一个例子那样,但在现在看来,这样做并不妥。

1. 通过Docker多阶段构建将多个层压缩为一个

当Git存储库变大时,你可以选择将历史提交记录压缩为单个提交。

事实证明,在Docker中也可以使用多阶段构建达到类似的目的。

在这个示例中,你将构建一个Node.js容器。

让我们从index.js开始:

const express = require('express') const app = express() app.get('/', (req, res) => res.send('Hello World!')) app.listen(3000, () => { console.log(`Example app listening on port 3000!`) })

和package.json:

{ "name": "hello-world", "version": "1.0.0", "main": "index.js", "dependencies": { "express": "^4.16.2" }, "scripts": { "start": "node index.js" } }

你可以使用下面的Dockerfile来打包这个应用程序:

FROM node:8 EXPOSE 3000 WORKDIR /app COPY package.json index.js ./ RUN npm install CMD ["npm", "start"]

然后开始构建镜像:

$ docker build -t node-vanilla .

然后用以下方法验证它是否可以正常运行:

$ docker run -p 3000:3000 -ti --rm --init node-vanilla

你应该能访问:3000,并收到“Hello World!”。

Dockerfile中使用了一个COPY语句和一个RUN语句,所以按照预期,新镜像应该比基础镜像多出至少两个层:

$ docker history node-vanilla IMAGE CREATED BY SIZE 075d229d3f48 /bin/sh -c #(nop) CMD ["npm" "start"] 0B bc8c3cc813ae /bin/sh -c npm install 2.91MB bac31afb6f42 /bin/sh -c #(nop) COPY multi:3071ddd474429e1… 364B 500a9fbef90e /bin/sh -c #(nop) WORKDIR /app 0B 78b28027dfbf /bin/sh -c #(nop) EXPOSE 3000 0B b87c2ad8344d /bin/sh -c #(nop) CMD ["node"] 0B <missing> /bin/sh -c set -ex && for key in 6A010… 4.17MB <missing> /bin/sh -c #(nop) ENV YARN_VERSION=1.3.2 0B <missing> /bin/sh -c ARCH= && dpkgArch="$(dpkg --print… 56.9MB <missing> /bin/sh -c #(nop) ENV NODE_VERSION=8.9.4 0B <missing> /bin/sh -c set -ex && for key in 94AE3… 129kB <missing> /bin/sh -c groupadd --gid 1000 node && use… 335kB <missing> /bin/sh -c set -ex; apt-get update; apt-ge… 324MB <missing> /bin/sh -c apt-get update && apt-get install… 123MB <missing> /bin/sh -c set -ex; if ! command -v gpg > /… 0B <missing> /bin/sh -c apt-get update && apt-get install… 44.6MB <missing> /bin/sh -c #(nop) CMD ["bash"] 0B <missing> /bin/sh -c #(nop) ADD file:1dd78a123212328bd… 123MB

但实际上,生成的镜像多了五个新层:每一个层对应Dockerfile里的一个语句。

现在,让我们来试试Docker的多阶段构建。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/12581.html