如何给 Docker 镜像瘦身?

阅读数:4348 2019 年 3 月 12 日 15:00

如何给Docker镜像瘦身?

在本文中,你将了解如何加快 Docker 构建周期并创建轻量级镜像。还是用比喻来说吧,给 Docker 镜像瘦身就跟我们减肥一样,减肥时期我们吃沙拉,拒绝披萨、甜甜圈和百吉饼。

这是备忘单。

FROM:指定基础 (父) 镜像。

标签:提供元数据。这里是包含维护者信息的好地方。

ENV:设置一个持久环境变量。

RUN:运行一个命令并创建一个图像层。用于将包安装到容器中。

COPY:将文件和目录复制到容器中。

ADD:将文件和目录复制到容器中。可以上传本地.tar 文件中。

CMD:为正在执行的容器提供命令和参数。可以覆盖参数。只能有一个 CMD。

WORKDIR:为下面的指令设置工作目录。

ARG:在构建时传递给 Docker 的变量。

ENTRYPOINT:为正在执行的容器提供命令和参数。关于这件事的争论一直在持续。

EXPOSE:公开端口。

VOLUME:创建一个目录挂载点来访问和存储持久数据。

现在让我们看看如何设计 dockerfile,以便在开发镜像和提取容器时节省时间。

缓存

Docker 的优点之一是提供缓存,帮助你更快地迭代镜像构建。

在构建映像时,Docker 按步骤遍历 Dockerfile 中的指令,按顺序执行每个指令。在检查每个指令时,Docker 会在其缓存中寻找一个可以重用的现有中间镜像,而不是创建一个新的 (重复的) 中间映像。

如果缓存无效,让无效的指令和所有后续 Dockerfile 指令生成新的中间镜像。一旦缓存失效,Dockerfile 中其余的指令也就失效了。

因此,从 Dockerfile 的顶部开始,如果基础镜像已经在缓存中,那么它将被重用。这将是高效命中。否则,缓存无效。

如何给Docker镜像瘦身?

也是一击

然后将下一条指令与从该基础镜像派生的缓存中的所有子镜像进行比较。比较每个缓存的中间镜像,看指令是否在缓存命中。如果缓存失败,则缓存无效。重复相同的过程,直到到达 Dockerfile 的末尾。

大多数新指令只是简单地与中间镜像中的指令进行比较。如果匹配,则使用缓存的副本。

例如,当在 Dockerfile 中找到RUN pip install -r requiremtes .txt指令时,Docker 会在本地缓存的中间镜像中搜索相同的指令。不比较新旧 requirements.txt 文件的内容。

如果使用新包来更新requirements.txt文件,并使用RUN pip install并希望使用新包名称重新运行包安装,则此行为可能会出现问题。我一会儿会展示一些解决方案。

与其他 Docker 指令不同,ADD 和 COPY 指令确实需要 Docker 查看文件的内容,以确定是否存在缓存命中。将引用文件的校验和与现有中间镜像中的校验和进行比较。如果文件内容或元数据发生了更改,则缓存无效。

下面是一些有效使用缓存的技巧。

  • 可以通过传递--no-cache=Truedocker build关闭缓存。

  • 如果你要对指令进行更改,那么接下来的每一层都将频繁地重新构建。要利用缓存,请在 Dockerfile 中尽可能低地放置可能更改的指令。

  • 链接RUN apt-get updateapt-get install命令,以避免缓存丢失问题。

  • 如果你正在使用带有requirements.txt文件的包安装程序 (如 pip),那么请遵循如下模型,以确保你不会收到带有requirements.txt中列出的旧包的陈旧的中间镜像。

复制代码
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY . /tmp/

这些是有效使用 Docker 构建缓存的建议。

减小尺寸

Docker 镜像可能会变得很大。你想让它们尺寸变小,这样它们就可以快速拉起,使用很少的资源。让我们给你的镜像瘦身!

如何给Docker镜像瘦身?

吃顿沙拉而不是百吉饼

Alpine base 镜像是一个完整的 Linux 发行版,没有太多其他东西。下载通常小于 5mb,但是它需要更多的时间为构建应用程序所需的依赖项编写代码。

如何给Docker镜像瘦身?

Alpine 源自阿尔卑斯山脉

如果你的容器中需要 Python, Python Alpine 构建是一个不错的折衷方案。它包含 Linux 和 Python,你可以提供大多数其他东西。

我用最新的 Python Alpine 构建的带有打印脚本 (“hello world”) 的镜像大小为 78.5 MB。下面是 Dockerfile:

复制代码
FROM python:3.7.2-alpine3.8
COPY . /app
ENTRYPOINT [“python”, “./app/my_script.py”, “my_var”]

在 Docker Hub 上,基础镜像的大小为 29MB,当构建子镜像时,需要下载并安装 Python,尺寸就变得很大。

除了使用 Alpine 基础镜像,另一种减小镜像大小的方法是使用多级构建。这种技术还会增加 Dockerfile 的复杂性。

多级构建

如何给Docker镜像瘦身?如何给Docker镜像瘦身?

一个舞台 + 另一个舞台 = 多级舞台

多阶段构建使用多个 FROM 指令。你可以有选择地将文件 (称为构建工件) 从一个阶段复制到另一个阶段。你可以在最终的镜像中扔掉任何你不想要的东西。这种方法可以减少整体镜像的大小。

每个 FROM 指令

  • 开始构建的新阶段

  • 去掉在之前阶段留下的任何状态

  • 可以用不同的基础镜像

下面是 Docker 文档中经过修改的多级构建示例:

复制代码
FROM golang:1.7.3 AS build
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=build /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

注意,我们通过给 FROM 指令增加一个名字来命名第一阶段。然后,被命名的阶段会通过COPY --from=指令引用到 Dockerfile 中。

在制造大量容器的情况下,多级构建是有意义的。多级构建可以帮助你从镜像大小中挤出每一寸空间。然而,有时多阶段构建会增加复杂性,使镜像更难维护,因此你可能不会在大多数构建中使用它们。参见此处对折衷高级模式的进一步讨论。

相反,每个人都应该使用.dockerignore 文件来帮助保持 Docker 镜像的简洁。

.dockerignore

如果你对 Docker 了解得足够多,那么 *.dockerignore* 就是一些你应该知道的东西。

.dockerignore 类似于.gitignore。它是一个包含 Docker 模式列表的文件,Docker 在生成镜像时需要匹配文件名并排除这些模式。
如何给Docker镜像瘦身?

来.dockerignore 一下

将.dockerignore 文件与 Dockerfile 和构建上下文的其余部分放在同一个文件夹中。

运行 docker build 创建镜像时,docker 检查.dockerignore 文件。如果找到一个,则逐行检查文件并使用 Go 的 filepath。匹配规则——以及 Docker 自己的一些规则——以匹配文件名以进行排除。想想 unix 风格的 glob 模式,而不是正则表达式。

因此*.jpg将排除扩展名为.jpg 的文件。并且视频将排除视频文件夹及其内容。

您可以使用以#开头的注释来解释您在.dockerignore 中所做的事情。

使用.dockerignore 从 Docker 镜像中排除不需要的文件是个好主意。

  • 帮助你保守秘密。没有人想在镜像中使用密码。

  • 减少镜像大小。更少的文件意味着更小、更快的镜像。

  • 减少构建缓存失效。如果日志或其他文件正在发生变化,而你的镜像缓存因此而失效,则会减慢构建周期。

这就是使用.dockerignore 文件的原因。

尺寸检查

让我们看看如何从命令行找到 Docker 镜像和容器的大小。

  • 要查看正在运行的容器的大致大小,可以使用docker container ls -s命令。

  • 运行docker image ls显示镜像的大小。

  • 要查看组成镜像的中间镜像大小,请使用docker image history my_image:my_tag

  • 运行Docker image inspect my_image:tag:该标签将显示跟镜像有关的信息,包括每个层的大小。图层与构成镜像略有不同。但是你可以把它们看成是一样的。

  • 安装和使用 dive 可以很容易地看到你的层内容。

现在,让我们来看看一些最佳实践来给镜像瘦身。

减少镜像大小和构建时间的八个最佳实践

  1. 尽可能使用正式的基础镜像。官方镜像定期更新,比非官方镜像更安全。

  2. 在可能的情况下使用不同的 Alpine 镜像,以保持你的镜像轻量级。

3. 如果使用 apt,请在同一指令中将 RUN apt-get update 与 apt-get install 结合使用。然后在该指令中链接多个包。用\字符在多行上按字母顺序列出包。例如:

复制代码
RUN apt-get update && apt-get install -y \
package-one \
package-two
&& rm -rf /var/lib/apt/lists/*

这种方法减少了要构建的层的数量,并保持简洁。

  1. 在运行指令的末尾包含&& rm -rf /var/lib/apt/lists/*,以清理 apt 缓存,使其不存储在层中。

  2. 聪明地使用缓存,将可能发生更改的指令放在 Dockerfile 中。

  3. 使用.dockerignore 文件将不需要的和不必要的文件从镜像中删除。

  4. 查看 dive ——它的一个非常酷的工具,可以检查你的 Docker 镜像层,并帮助你削减多余的部分。

  5. 不要安装你不需要的软件包。唉!但这种现象很常见。

总结

现在你知道了如何使 Docker 镜像快速构建、快速下载,并且不占用太多空间。和健康饮食一样,知道是成功的一半。享受你的蔬菜。

如何给Docker镜像瘦身?

健康和美味

查看英文原文链接: https://towardsdatascience.com/slimming-down-your-docker-images-275f0ca9337e

收藏

评论

微博

用户头像
发表评论

注册/登录 InfoQ 发表评论

最新评论

用户头像
Major 2019 年 03 月 13 日 14:21 1 回复
配图已挂!!
用户头像
已修复。 1 回复
用户头像
lyuehh 2019 年 03 月 12 日 21:06 1 回复
骷髅图和沙拉不太搭配啊。。。
用户头像
非礼勿听、非礼勿言、非礼勿视? 1 回复
没有更多了