Skip to main content

Docker-第四部分:Docker映像

· 13 分钟阅读

原文作者:Jeff Hale

原文地址:https://towardsdatascience.com/slimming-down-your-docker-images-275f0ca9337e

翻译:付新圆

在本文中,您将学习如何加快Docker构建周期并创建轻量级映像。遵循之前的文章中的食物隐喻,我们将沙拉隐喻为Docker映像,同时减少Docker映像的数量。

在本系列的第3部分中,我们介绍了十几个Dockerfile指令。如果您错过了,请在这里查看文章:

《Docker-第3部分:十二个Dockerfile指令》

FROM—指定基本(父)图像。

LABEL—提供元数据,包括维护者信息。

ENV—设置持久性环境变量。

RUN—运行命令并创建图像层,用于将软件包安装到容器中。

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

ADD-将文件和目录复制到容器,可以支持本地.tar文件。

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

WORKDIR—为以下说明设置工作目录。

ARG—定义在构建时传递给Docker的变量。

ENTRYPOINT—为执行中的容器提供命令和参数。争论依然存在。

EXPOSE—暴露端口。

VOLUME—创建目录安装点以访问和存储持久数据。

现在让我们来看看如何设计Dockerfiles,以节省开发映像和拉取容器时的时间。

缓存

Docker的优势之一是它提供了缓存,帮助您更快地迭代映像构建。

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

如果缓存失效,则使缓存失效的指令和所有后续的Dockerfile指令都会生成新的中间映像。一旦缓存失效,Dockerfile中的其余指令就都失效了。

因此,从Dockerfile的顶部开始,如果基本映像已经在缓存中,就会重复使用它。否则,缓存将失效。

图:击中

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

大多数新指令都只是与中间图像中的指令进行比较。如果存在匹配项,则使用缓存的副本。

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

如果您更新要求,则此行​​为可能会出现问题requirements.txt带有新软件包的文件并使用RUN pip install并使用新的软件包名称重新运行软件包安装。我将在稍后展示一些解决方案。

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

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

  • 可以通过传递--no cache=True关闭docker build
  • 如果要对指令进行更改,则随后的每一层都将被频繁重建。要利用缓存,请在Dockerfile中放置可能变化尽可能小的指令。
  • ChainRUN apt-get updateapt-get install命令以避免缓存未命中问题。
  • 如果使用的是包安装程序(如pip)并带有requirements.txt文件,则请遵循以下模型,以确保您不会因使用requirements.txt中列出的旧软件包而收到陈旧的中间映像。
CCOPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY . /tmp/

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

缩小尺寸

Docker映像会变大,所以需要将它们保持的较小,以便可以快速拉出来并使用很少的资源。

让我们瘦下来的物品!

图:沙拉

Alpine基本映像是一个完整的Linux发行版,没有太多其他内容。下载通常小于5MB,但它需要花费更多的时间来编写构建一个工作应用程序所需的依赖项的代码。

图:阿尔卑斯山

如果您的容器中需要Python,则可以使用Python Alpine构建。它包含Linux和Python,其他大部分都由您提供。

使用最新的Python Alpine构建并带有print(“ 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网站上,基本映像被列为29 MB。构建子映像后,它会下载并安装Python,使其变得更大。 除了使用Alpine基本映像外,另一种减小图像大小的方法是使用多级构建,该技术技术增加了Dockerfile的复杂性。

多阶段构建

图:一个阶段+另一个阶段=多阶段

多级构建使用多个FROM指令。您可以有选择地将文件(成为构建工件)从一个阶段复制到另一个阶段,可以在最后的图像中留下任何你不想要的内容。此方法可以减小整体图像大小。

每个FROM指令

  • 开始构建的新阶段;
  • 保留了先前阶段中创建的任何状态;
  • 可以使用其他基础;

以下是Docker docs中多级构建的修改示例:

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

.dockerignore文件是您应该知道的一些知识。

.dockerignore类似于.gitignore。这是一个包含模式列表的文件,Docker可以使用这些模式与文件名进行匹配,并在制作映像时将其排除。

图:.dockerginore

将.dockerginore文件与Dockerfile和其他构建上下文放在同一个文件夹中。

当您运行docker build创建映像时,Docker会检查.dockerignore文件。如果找到一个,它将逐行遍历文件并使用Go的filepath.Match规则 ,以及Docker的一些规则,匹配要排除的文件名考虑Unix风格的glob模式,而不是正则表达式。

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

您可以用以#开头的注释来解释您在.dockrignore中所做的工作。

使用.dockergnore从Docker映像中排除不需要的文件是一个好方法。.dockerignore可以:

  • 帮助你保守秘密。没有人想要在图像中输入密码。
  • 缩小图像大小。更少的文件意味着更小更快的图像。
  • 减少生成缓存失效。如果日志或其他文件正在更改,而您的映像因此而使其缓存失效,则会减慢构建周期。

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

尺寸检查

如何从命令行找到Docker图像和容器的大小。

  • 要查看正在运行的容器的大致大小,可以使用命令docker container ls-s
  • 运行docker image ls显示图像的大小。
  • 要查看构成您的图像的中间图像的大小docker image history my_``image:my_tag
  • 运行docker image inspect my_``image:tag将显示有关图像的许多信息,包括每一 层的大小。图层与构成整个图像的图像有细微的不同。但在大多数情况下,你可以把它们看作是相同的。
  • 安装和使用dive包可以很容易地查看层内容。

现在,让我们看一些简化操作的最佳实践。

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

  1. 尽可能使用官方的基本图像。官方图片定期更新,比f非官方图片更安全;
  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. 在RUN指令的末尾包含&& rm -rf /var/lib/apt/lists/*,以清理apt缓存,使其不存储在层中。

  2. 通过在Dockerfile中较低位置的指令来使用缓存。

  3. 使用.dockerignore文件将不需要的和不必要的文件排除在图像之外。

  4. 检查 dive - 一个非常酷的工具,用于检查Docker图像层。

  5. 不要安装不需要的软件包。

结尾

以上就是Docker映像快速构建,以及快速下载并且不占用太多空间的内容。学习是成功的一半。在本系列的下一篇文章中,将深入探讨基本的Docker命令,希望对你有帮助。