在生产环境中使用 Docker 的 8 大最佳实践
Docker 的采用率不断上升📈,许多人都熟悉它,但并非每个人都按照最佳实践使用 Docker。👀
在继续之前,如果您不知道 Docker 是什么,您可以通过这个免费的 Docker 速成课程学习入门所需的一切知识 🐳
为什么要使用最佳实践?🤷♀️
因此,在我的新视频“Docker 生产最佳实践的 8 大要点”中,我想向您展示在项目中正确使用 Docker 的 8 种方法:
- ✅提高安全性,
- ✅优化图像尺寸,
- ✅ 利用一些有用的 Docker 功能
- ✅ 并编写更干净、更易于维护的 Dockerfile
1️⃣ 最佳实践
尽可能使用官方且经过验证的 Docker 镜像作为基础镜像。
假设您正在开发一个 Node.js 应用程序,并希望将其构建并运行为 Docker 镜像。
不要采用基础操作系统映像并安装 node.js、npm 以及应用程序所需的任何其他工具,而是使用应用程序的官方节点映像。
改进:
- 更清洁的 Dockerfile
- 官方且经过验证的镜像,已按照最佳实践构建
2️⃣ 最佳实践
使用特定的 Docker 镜像版本
好的,我们已经选择了基础镜像,但是现在当我们从这个 Dockerfile 构建我们的应用程序镜像时,它将始终使用latest
节点镜像的标签。
现在,为什么这是一个问题?🤔
❌ - 您可能会获得与上一次构建不同的镜像版本
❌ - 新的镜像版本可能会破坏某些内容
❌ -latest
标签不可预测,导致意外行为
因此,与其使用随机的最新镜像标签,不如固定版本,就像部署特定版本的应用程序一样,最好使用特定版本的官方镜像。
这里的规则是:越具体越好。
改进:
- 透明度,准确了解您正在使用的基础镜像的版本
3️⃣最佳实践
使用小尺寸的官方图片
当选择 Node.js 镜像时,你会看到实际上有多个官方镜像。它们不仅版本号不同,而且操作系统发行版也不同:
所以问题是:你选择哪一个?为什么它很重要?🤷🏻♂️
1) 镜像大小
❌ 如果镜像基于成熟的操作系统发行版(例如 Ubuntu 或 Centos),那么镜像中会预装许多工具。因此镜像大小会更大,但您的应用程序镜像中大多数工具都不需要。
✅ 相比之下,拥有较小的图像意味着您在图像存储库以及部署服务器上需要更少的存储空间,当然,您可以在从存储库中提取或推送图像时更快地传输图像。
2) 安全问题
❌ 除此之外,由于安装了大量的工具,你需要考虑安全性问题。因为这样的基础镜像通常包含数百个已知漏洞,这实际上会给你的应用程序镜像带来更大的攻击面。
这样一来,你基本上从一开始就给你的图像带来了不必要的安全问题!🙉
✅ 相比之下,通过使用更小的图像和更精简的操作系统发行版(仅捆绑必要的系统
工具和库),您还可以最大限度地减少攻击面并确保构建更安全的图像。
因此,最佳做法是根据更精简的操作系统发行版(例如 alpine)选择具有特定版本的图像:
Alpine 拥有在容器中启动应用程序所需的一切,但它更加轻量级。在 Docker Hub 上查看的大多数镜像中,你都会看到一个带有 Alpine 发行版的版本标签。
它是 Docker 容器最常见和最流行的基础镜像之一。
4️⃣最佳实践
构建镜像时优化镜像层的缓存
那么什么是镜像层?缓存和镜像层分别是什么意思呢?🤔
1) 什么是镜像层?
Docker 镜像是基于 Dockerfile 构建的。
在 Dockerfile 中,每个命令或指令都会创建一个镜像层:
因此,当我们像上面的例子一样使用 Node.js 的 alpine 基础镜像时,它已经拥有了层,因为它已经使用自己的 Dockerfile 构建好了。此外,在我们的 Dockerfile 中,我们还有一些其他命令,每个命令都会为这个镜像添加一个新层。
2) 那么缓存呢?
Docker 会缓存每个层。👍
因此,当你重建镜像时,如果你的 Dockerfile 没有更改,Docker 就会直接使用缓存的层来构建镜像。
缓存图像层的优点
: ✅ - 更快地构建图像
✅ - 更快地拉取和推送新图像版本:
如果我拉取同一应用程序的新图像版本,并且假设新版本中添加了 2 个新层:则只会下载新添加的层,其余层已由 Docker 本地缓存。
3) 优化缓存
。为了优化缓存,你需要知道:
一旦某个层发生更改,所有后续层或下游层也必须重新创建。换句话说:当你更改 Dockerfile 中某一行的内容时,所有后续行或层的缓存都将被破坏并失效。😣
因此,这里的规则和最佳实践是:按更改频率从最少到最频繁的顺序排列Dockerfile 中的
命令,以利用缓存,并以此方式优化镜像的构建速度。🚀
5️⃣最佳实践
使用
.dockerignore
文件
现在,通常当我们构建镜像时,我们不需要项目中的所有内容来运行其中的应用程序。我们
不需要自动生成的文件夹,例如targets
或build
文件夹,也不需要readme
文件等等。
那么,我们如何排除此类内容,避免它们出现在我们的应用程序映像中呢?🤔👉
使用.dockerignore
文件。
这很简单。我们基本上只需创建这个.dockerignore
文件,列出所有我们想要忽略的文件和文件夹,在构建镜像时,Docker 就会查看其中的内容并忽略其中指定的任何内容。
改进:
- 缩小图像尺寸
6️⃣ 最佳实践
利用多阶段构建
但是现在假设您的项目中有一些内容(如开发、测试工具和库)是您在构建图像时需要的 - 因此在
构建过程中 - 但您不需要在最终图像本身中使用它们来运行应用程序。
如果您将这些工件保留在最终映像中,即使它们对于运行应用程序绝对没有必要,也会再次导致映像大小增加和攻击面增加。🧐
那么,我们如何将构建阶段与运行时阶段区分开来呢?
换句话说,我们如何从镜像中排除构建依赖项,同时又能在构建镜像时保留它们呢?🤷♀️
好吧,为此你可以使用所谓的多阶段构建💡
多阶段构建功能允许您在构建过程中使用多个临时映像,但仅保留
最新映像作为最终工件:
因此这些先前的步骤(上图中标记为“1st”)将被丢弃。
改进:
- 将构建工具和依赖项与运行时所需的内容分离
- 更少的依赖性和更小的图像尺寸
7️⃣ 最佳实践
使用最低权限用户
现在,当我们创建此镜像并最终将其作为容器运行时,将使用哪个操作系统用户
来启动其中的应用程序?🤔 默认情况下,当 Dockerfile 未指定用户时,它将使用root 用户。🙉 但实际上,几乎没有理由以 root 权限运行容器。
❌这实际上引入了一个安全问题,因为当容器在主机上启动时,它可能会拥有 Docker 主机的 root 访问权限。
因此,以 root 用户身份在容器内运行应用程序将使攻击者更容易提升主机权限,并基本上控制底层主机及其进程,而不仅仅是容器本身🤯 尤其是在容器内的应用程序容易受到攻击的情况下。
✅ 为了避免这种情况,最佳做法是在 Docker 镜像中创建一个专用用户和一个专用组来运行该应用程序,并使用该用户在容器内运行该应用程序:
您可以使用以用户名调用的指令USER
,然后方便地启动应用程序。
提示:有些镜像已经绑定了通用用户,您可以直接使用。因此,您无需创建新用户。例如,node.js 镜像已经绑定了一个名为 的通用用户node
,您可以直接使用它在容器内运行应用程序。👍
8️⃣ 最佳实践
扫描图像以查找安全漏洞
最后,如何确保并验证你构建的镜像几乎没有或根本没有安全漏洞?🧐
因此,我的最终最佳实践是,一旦构建了镜像,就使用命令扫描其中的安全漏洞docker scan
。🔍
Docker 实际上在后台使用一个名为 snyk 的服务来对镜像进行漏洞扫描。扫描过程使用一个漏洞数据库,该数据库会不断更新。
您会看到:
1)漏洞类型,
2)更多信息的 URL,
3)此外,还有非常有用且有趣的信息,您可以看到哪个版本的相关库实际上修复了该漏洞。因此,您可以更新库以解决这些问题。👍
自动扫描 🚀
除了使用docker scan
CLI 命令手动扫描镜像外,您还可以配置 Docker Hub,使其在镜像推送到存储库时自动扫描。当然,您也可以在构建 Docker 镜像时将此检查集成到CI/CD 流水线中。
所以,以上就是8 个生产环境最佳实践,你现在就可以应用它们,让你的 Docker 镜像更精简、更安全!🚀😊 希望这些对大家有所帮助!当然,还有更多与 Docker 相关的最佳实践,但我认为在生产环境中使用 Docker 时,应用这些实践已经能带来很好的效果。
你还知道哪些你认为
非常重要且值得一提的最佳实践吗?
请在评论区分享,与大家分享 🙌 👍
完整视频可在此处观看:🤓
喜欢、分享并关注我😍以获取更多内容:
文章来源:https://dev.to/techworld_with_nana/top-8-docker-best-practices-for-using-docker-in-production-1m39