改进你的 Dockerfile,最佳实践深入了解 Dockerfile

2025-05-24

改进你的 Dockerfile,最佳实践

 深入了解 Dockerfile

在Twitter上关注我,很高兴接受您对主题或改进的建议/Chris

 深入了解 Dockerfile

好了,现在你已经了解了 Docker 的使用方法。你可能已经在我的Docker 系列文章(共五部分)或其他资源中学习过。无论如何,你都处于从理解基础知识到精通 Docker 的阶段。本文将向你展示如何提升 Docker 的基础知识,特别是 Dockerfiles 方面的知识。

 资源

 我们对 Dockerfile 的了解

我们知道,Dockerfile 就像一个配方文件,我们可以在其中指定所需的内容,例如要基于的操作系统镜像、需要安装的库、环境变量、要运行的命令等等。所有内容都写在了 Dockerfile 中,清晰地展现了你想要的内容。这比以前那些只能在自己的机器上安装,或者需要花费数小时甚至数天安装的旧方法,无疑是一个巨大的进步——这本身就是进步。

我们的 Dockerfile 示例

我们创建了一个 Dockerfile,以便让您大致了解它的样子。为了更好地理解它,让我们讨论一下该文件的各个部分。具体如下:

// Dockerfile
FROM node:latest

WORKDIR /app

COPY . .

RUN npm install

EXPOSE 3000

ENTRYPOINT ["node", "app.js"]

Enter fullscreen mode Exit fullscreen mode

这是一个非常典型的文件。我们选择一个操作系统镜像,设置一个工作目录,复制所需的文件,安装一些库,打开一个端口,最后运行应用程序。那么,这有什么问题呢?

 操作系统映像大小

乍一看,一切都符合我们的预期,但仔细观察,我们可以看到我们正在使用node:latest一个镜像。让我们尝试使用以下命令将其构建到 Docker 镜像中:

docker build -t optimize/node .
Enter fullscreen mode Exit fullscreen mode

好的,现在让我们运行docker images来查看我们的图像并获取更多统计数据:

它的重量为899 MB
好的,我们没有什么可比较的,但是让我们将图像更改为名为的图像node:alpine并重建我们的图像:


77.7 MB,哇!差别太大了,我们的 Docker 镜像小了 10 倍。这是为什么呢?

此镜像基于Alpine Linux 项目,
通常 Alpine Linux 镜像比普通发行版小得多。它有一些限制,请阅读此处。但总的来说,这是一个安全的选择。

 缓存

Dockerfile 中指定的每个命令都会创建另一个镜像层。然而,Docker 会先检查缓存,看看现有层是否可以重用,然后再尝试创建。

当我们讨论 ADD 和 COPY 之类的指令时,我们应该了解它们在缓存上下文中是如何操作的。对于这两个命令,Docker 都会为每个文件计算校验和并将其存储在缓存中。在构建新的 Docker 镜像时,会比较每个文件的校验和,如果由于文件更改而导致校验和不同,则重新计算校验和并执行命令。此时,它会创建一个新的镜像层。

秩序很重要

Docker 的运作方式是尽可能地重复使用。我们能做的最好的事情就是将 Dockerfile 中的指令按更改可能性从低到高排列。

这意味着什么?

让我们看一下 Dockerfile 的顶部:

FROM node:alpine

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode

这里我们可以看到,FROM 命令首先执行,然后是 WORKDIR。这两个命令不太可能改变操作系统,因为它们被正确地放置在顶部。

但什么可能会改变呢?

好吧,您正在构建一个应用程序,因此应用程序的源文件或您意识到可能突然需要的库(例如npm install)应该放在文件的更下方。

我们这样做有什么好处?

速度,当我们构建 Docker 镜像并尽可能高效地放置命令时,速度会提升。总而言之,ADD、COPY、RUn 等命令应该在 Dockerfile 的后期执行。

 最小化层数

输入的每个命令都会创建一个新的图像层。请确保将命令数量保持在最低限度。如果可以,请将它们分组。不要这样写:

RUN command
RUN command2
Enter fullscreen mode Exit fullscreen mode

像这样组织它们:

RUN command && \
    command2
Enter fullscreen mode Exit fullscreen mode

 仅包含您需要的内容

当你构建一个应用程序时,它很容易包含大量的文件,但当你真正需要创建 Docker 镜像时,最终文件数量会少得多。如果你创建一个.dockerignore文件,你可以定义一些模式,以确保在引入文件时,只获取容器所需的文件。

 定义启动脚本

无论使用 CMD 还是 ENTRYPOINT 命令,都不应像这样直接调用应用程序node app.js。相反,请尝试像这样定义一个启动脚本npm start

你为什么这么问?

我们希望确保程序足够灵活,不太可能更改这条指令。实际上,我们最终可能会通过逐步添加标志来改变应用的启动方式,就像这样node app.js --env=dev --seed=true。你懂的,这可能是一个不断变化的目标。然而,通过依赖npm start启动脚本,我们可以获得更灵活的方式。

 使用标签

使用 LABEL 命令可以更好地描述你的 Dockerfile。你可以用它来组织文件,帮助实现自动化和潜在的用例,你最清楚哪些信息应该放在那里,但它的存在是为了帮助你整理所有镜像,所以充分利用它。标签值是一个键值对,如下所示LABEL [key]-[value]。每个标签命令都可以有多个标签。实际上,它被认为是将所有标签收集在一个标签命令下。你可以通过使用空格字符分隔每个键值对来实现这一点,或者如下所示:

LABEL key=value \
      key2=value2
Enter fullscreen mode Exit fullscreen mode

 使用 EXPOSE 依赖默认端口

EXPOSE 命令用于在容器上开放端口。为了确保能够与该端口上的容器通信,我们可以-p结合 Docker run 命令使用该命令docker run -p [external]: [exposed docker port]。最佳做法是将暴露的端口设置为您正在使用的服务器的默认端口,例如,对于 Apache 服务器,设置为 80 端口;对于 Mongo DB 数据库,设置为 27017 端口。

明确一点,使用 COPY 而不是 ADD

乍一看,COPY 和 ADD 的作用似乎相同,但两者之间还是有区别的。ADD 可以提取 TAR 文件,而 COPY 却不行。因此,如果您需要复制文件,请务必明确使用 COPY;如果您需要使用某些特定功能(例如上述 TAR 提取),请务必仅使用 ADD。

 概括

关于 Dockerfile,还有很多最佳实践可以遵循,但我在这篇文章中提到的最大好处是使用尽可能小的镜像,比如 alpine。它可以显著减少镜像大小,尤其是在您需要支付存储空间的情况下。

阅读Dockerfile 最佳实践文档,了解更多实用技巧

文章来源:https://dev.to/azure/improve-your-dockerfile-best-practices-5ll
PREV
Serverless 真的像大家说的那么便宜吗?
NEXT
我写了一本关于 React.js 的免费书,刚刚在 GitHub 上发布