Docker 的采用率不断上升📈,许多人都熟悉它,但并不是每个人都按照最佳实践使用 Docker。 👀


在继续之前,如果你不知道 Docker 是什么,你可以在这个免费的 Docker Crash Course 中学习所有你需要的东西🐳


为什么使用最佳实践? 🤷u200d♀️

因此,在我的新视频“8 大 Docker 生产最佳实践”中,我想向您展示在项目中以正确方式使用 Docker 的 8 种方法:

  • 提高安全性,

  • 优化 图像大小,

  • ✅ 利用一些有用的 Docker 功能

  • ✅ 并且还编写了更清洁、更易于维护的 Dockerfile


1️⃣ 最佳实践

使用经过验证的官方 Docker 镜像作为基础镜像,只要可用。

假设您正在开发一个 Node.js 应用程序并希望将其构建并作为 Docker 映像运行。

无需获取基本操作系统映像并安装 node.js、npm 和您的应用程序所需的任何其他工具,而是为您的应用程序使用官方节点映像。

[第一个最佳实践](https://res.cloudinary.com/practicaldev/image/fetch/s--Zc-ArYbv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/spd6rkqkwnitp2riio29.png)

改进:

  • 更清洁的 Dockerfile

  • 官方且经过验证的镜像,已使用最佳实践构建


2️⃣最佳实践

使用特定的 Docker 镜像版本

好的,所以我们已经选择了基础镜像,但是现在当我们从这个 Dockerfile 构建我们的应用程序镜像时,它将始终使用节点镜像的latest标签。

现在为什么这是一个问题? 🤔

❌ - 您可能会获得与之前版本不同的图像版本

❌ - 新的图像版本可能会破坏东西

❌ -latest标签不可预测,导致意外行为

因此,您希望固定版本而不是随机的最新图像标签,就像您使用特定版本部署自己的应用程序一样,您希望使用具有特定版本的官方图像。

这里的规则是:越具体越好

[第二个最佳实践](https://res.cloudinary.com/practicaldev/image/fetch/s--vIWh6vcV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/zikxyawtnrwk526xov6z.png)

改进:

  • 透明度,以准确了解您正在使用的基本映像的版本

3️⃣最佳实践

使用小尺寸官方图片

选择 Node.js 镜像时,您会看到实际上有多个官方镜像。不仅有不同的版本号,还有不同的操作系统发行版:

[节点映像版本](https://res.cloudinary.com/practicaldev/image/fetch/s--zXYUDcn---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/n4szjwytxzttkc4wovxz.png)

所以问题是:你选择哪一个,为什么它很重要? 🤷🏻u200d♂️

1) 图像尺寸

❌ 好吧,如果映像基于 成熟的 OS 发行版,例如 Ubuntu 或 Centos,您将拥有大量已打包在映像中的工具。所以图像尺寸会更大,但您的应用程序图像中不需要大多数这些工具

✅ 相比之下,较小的图像意味着您在图像存储库以及部署服务器上需要更少的存储空间,当然,当从存储库中拉取或推送图像时,您可以更快地传输图像。

2) 安全问题

❌除此之外,内部安装了很多工具,您需要考虑安全方面。因为这样的基础镜像通常包含数百个已知漏洞并且基本上会为您的应用程序镜像创建一个更大的攻击面

这样,您基本上最终会从一开始就将不必要的安全问题引入您的图像! 🙉

✅ 相比之下,使用较小的图像和更精简的 OS 发行版,只捆绑必要的系统

工具和库,您还可以最大限度地减少攻击面并确保构建更安全的图像

因此,这里的最佳做法是选择具有特定版本的映像,该映像基于更精简的 OS 发行版,例如 alpine:

[第三个最佳实践](https://res.cloudinary.com/practicaldev/image/fetch/s--hNbFYYmk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/36oou4hn3wq55n14u9dz.png)

Alpine 拥有在容器中启动应用程序所需的一切,但更轻量级。对于您在 Docker Hub 上查看的大多数图像,您会看到一个带有 alpine 分布的版本标签。

它是 Docker 容器最常见和流行的基础镜像之一。


4️⃣ 最佳实践

在构建图像时优化图像层的缓存

那么什么是图像层,缓存和图像层是什么意思呢? 🤔

1) 什么是图像层?

Docker 镜像是基于 Dockerfile 构建的。

在 Dockerfile 中,每个命令或指令都会创建一个镜像层:

[Docker 镜像层](https://res.cloudinary.com/practicaldev/image/fetch/s--sxX5q4x3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/dmtb90pbzqygh0h86a10.png)

因此,当我们像上面的示例一样使用节点 alpine 的基础镜像时,它已经有层,因为它已经使用自己的 Dockerfile 构建。另外,在我们的 Dockerfile 中,我们还有一些其他命令,每个命令都会为这个镜像添加一个新层。

2) 现在缓存呢?

每一层都会被 Docker 缓存。 👍

所以当你重建你的镜像时,如果你的 Dockerfile 没有改变,Docker 只会使用缓存的层来构建镜像。

缓存图像层的优点:

✅ - 更快的图像构建

✅ - 更快地拉取和推送新图像版本:

如果我拉取同一个应用程序的新镜像版本,假设新版本中添加了 2 个新层:只会下载新添加的层,其余的已经被 Docker 本地缓存。

3) 优化缓存

所以要优化缓存,你需要知道:

一旦图层更改,所有后续或下游图层也必须重新创建。换句话说:当您更改 Dockerfile 中的一行内容时,以下所有行或层的缓存将被破坏并失效。 😣

所以这里的规则和最佳实践是:

在 Dockerfile 中对命令进行排序 从最少到最频繁更改的命令 以利用缓存,从而优化构建映像的速度。 🚀


5️⃣ 最佳实践

使用.dockerignore文件

现在通常当我们构建镜像时,我们不需要项目中的所有东西来运行里面的应用程序。我们

不需要自动生成的文件夹,如targetsbuild文件夹,我们不需要readme文件等。

那么我们如何将这些内容排除在我们的应用程序图像中呢? 🤔

👉 使用.dockerignore文件。

这很简单。我们基本上只是创建这个.dockerignore文件并列出我们想要忽略的所有文件和文件夹,并且在构建映像时,Docker 将查看内容并忽略其中指定的任何内容。

[第 5 个最佳实践](https://res.cloudinary.com/practicaldev/image/fetch/s--yAIQkTEs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/9fso63260yg2o7sddcxa.png)

改进:

  • 缩小的图像尺寸

6️⃣最佳实践

利用多阶段构建

但是现在假设您的项目中有一些内容(如开发、测试工具和库)是构建映像所需的 - 所以在

构建过程 - 但您不需要在最终图像本身中使用它们来运行应用程序。

如果您将这些工件保留在最终映像中,即使它们对于运行应用程序绝对不必要,这将再次导致图像大小增加和攻击面增加。 🧐

那么我们如何将构建阶段与运行时阶段分开

换句话说,我们如何从镜像中排除构建依赖项,同时在构建镜像时仍然可以使用它们? 🤷u200d♀️

好吧,为此你可以使用所谓的多阶段构建💡

多阶段构建功能允许您在构建过程中使用多个临时图像,但只保留

最新的图像作为最终的工件:

[第 6 次最佳实践](https://res.cloudinary.com/practicaldev/image/fetch/s--Qr2zxvmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/trdfkkdl96x1tkj4ffkh.png)

所以这些前面的步骤(上图中标记为“1st”)将被丢弃。

改进:

  • 将构建工具和依赖项与运行时所需的内容分开

  • 更少的依赖和减少的图像大小


7️⃣ 最佳实践

使用最低权限用户

现在,当我们创建此映像并最终将其作为容器运行时,哪个操作系统用户将用于启动其中的应用程序? 🤔

默认情况下,当 Dockerfile 未指定用户时,它使用 root 用户。 🙉 但实际上几乎没有理由以 root 权限运行容器。

❌ 这基本上引入了一个安全问题,因为当容器在主机上启动时,它可能会在 Docker 主机上拥有 root 访问权限。

因此,使用 root 用户在容器内运行应用程序将使 攻击者更容易提升主机上的权限,并且基本上可以控制底层主机及其进程,而不仅仅是容器本身🤯 特别是如果应用程序容器内部很容易被利用。

✅ 为避免这种情况,最佳实践是在 Docker 映像中简单地创建一个专用用户和一个专用组来运行应用程序,并在容器内运行应用程序与该用户** :

[第 7 个最佳实践](https://res.cloudinary.com/practicaldev/image/fetch/s--AD-IE3a9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/zrjr4m1885u1yqbnsjik.png)

您可以使用名为USER的指令和用户名,然后方便地启动应用程序。

提示: 一些图像已经捆绑了一个通用用户,您可以使用它。所以你不必创建一个新的。例如,node.js 映像已经捆绑了一个名为node的通用用户,您可以简单地使用它在容器内运行应用程序。 👍


8️⃣ 最佳实践

扫描您的图像以查找安全漏洞

最后,您如何确保和验证您构建的映像有一些安全漏洞或没有安全漏洞? 🧐

所以我最后的最佳实践是,一旦你构建了镜像,就可以使用docker scan命令扫描它的安全漏洞。 🔍

在后台,Docker 实际上使用了一个名为 snyk 的服务来对镜像进行漏洞扫描。扫描使用漏洞数据库,该数据库会不断更新。

docker scan命令的示例输出:

[docker 扫描输出](https://res.cloudinary.com/practicaldev/image/fetch/s--kS-pa8yP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/tmwear5nj98iyfm63rik.png)

你看:

  1. 漏洞类型,

  2. 获取更多信息的 URL

  3. 还有什么是非常有用和有趣的,您会看到 相关库的哪个版本 实际上修复了该漏洞。因此,您可以更新您的库以摆脱这些问题。 👍

自动扫描🚀

除了在 CLI 上使用docker scan命令手动扫描图像外,您还可以配置 Docker Hub自动扫描图像,当它们被推送到存储库时。当然,在构建 Docker 映像时,您可以在 CI/CD 管道集成此检查。


因此,这些是 8 种生产最佳实践,您现在可以应用它们来使您的 Docker 映像更精简、更安全! 🚀😊 希望对你们中的一些人有所帮助!当然,还有更多与 Docker 相关的最佳实践,但我认为在生产环境中使用 Docker 时,应用这些实践已经会给你带来很好的结果。

你知道其他一些你认为是的最佳实践吗?

超级重要且必须提及?

请在评论中分享给其他人🙌 👍

完整视频可在此处获得:🤓


喜欢、分享和关注我 😍 更多内容:

  • Instagram - 发布许多幕后花絮

  • 私人FB群

  • 领英

  • 推特

  • YouTube

Logo

CI/CD社区为您提供最前沿的新闻资讯和知识内容

更多推荐