发布于 2026-01-06 6 阅读
0

针对生产环境优化 Docker 镜像的速成课程

针对生产环境优化 Docker 镜像的速成课程

本月赞助商是Zeet。

免责声明:Zeet赞助本博文一个月。我前几天试用了一下。它类似于无服务器架构,但用于运行整个后端。您可以自动托管和扩展应用程序。非常棒。


部署应用耗时太久是不是很烦人?一个容器镜像就超过1GB,这可不是最佳实践。每次部署新版本都要推送数十亿字节的数据,我觉得不太合理。

太长不看

本文将向您展示如何通过几个简单的步骤来优化您的 Docker 镜像,使其体积更小、速度更快,更适合生产环境。

本次活动的目的是向您展示使用默认 Node.js 图片和使用优化后的对应图片在文件大小和性能方面的差异。以下是活动议程。

  • 为什么选择Node.js?
  • 使用默认的 Node.js 镜像
  • 使用 Node.js Alpine 镜像
  • 不包括开发依赖项
  • 使用 Alpine 底图
  • 使用多阶段构建

让我们开始吧。

为什么选择Node.js?

Node.js 目前是后端开发中最通用、对新手最友好的环境,而且我的主要编程语言就是它,所以你们就得忍受它了。随便你们怎么说吧。😙

作为一种解释型语言,JavaScript 没有像 Go 那样的编译目标。你很难减小 Node.js 图片的大小。或者说,真的没有办法吗?

我来这里就是要证明这种说法是错误的。选择合适的镜像作为基础,只在生产镜像中安装生产依赖项,当然还有使用多阶段构建,这些都是可以大幅降低镜像大小的方法。

在下面的示例中,我使用了我之前编写的一个简单的Node.js API 。

使用默认的 Node.js 镜像

当然,一开始我使用的是从Docker Hub拉取的默认 Node.js 镜像。哦,我当时真是太无知了。

FROM node
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

猜猜它有多大?我简直惊呆了。一个简单的 API 居然要727MB ?!

别这样做,求你了。你真的没必要这样做,真的,别这样做。

使用 Node.js Alpine 镜像

大幅减小镜像大小最简单快捷的方法是选择一个更小的基础镜像。Alpine一个体积小巧的 Linux 发行版,完全可以满足需求。仅仅选择 Alpine 版本的 Node.js 就能带来显著的性能提升。

FROM node:alpine # adding the alpine tag
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

体积缩小了整整六倍!只有123.1MB。这才像话。

不包括开发依赖项

嗯……但肯定还有其他办法。我们现在安装了所有依赖项,尽管最终镜像只需要生产环境的依赖项。要不我们改一下?

FROM node:alpine
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install --production # Only install prod deps
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

好了,又节省了30MB!现在只剩91.6MB了。我们终于有点进展了。

这让我颇为自豪,我正准备收工。但突然灵光一闪。如果我直接从原始的 Alpine 镜像开始呢?如果我获取基础的 Alpine 镜像并自行安装 Node.js,文件大小或许会更小。我的想法是对的!

使用 Alpine 底图

你可能会觉得这样的改动不会有什么区别,但它却比之前的版本又节省了 20MB 的空间。

FROM alpine # base alpine
WORKDIR /usr/src/app
RUN apk add --no-cache --update nodejs nodejs-npm # install Node.js and npm
COPY package.json package-lock.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

现在只有70.4MB了。这比我们最初的大小缩小了整整 10 倍!

现在我们能做的也没什么多了,对吧?对吧……?

使用多阶段构建

其实是有的。我们来谈谈层次结构吧。

每个 Docker 镜像都是由多个层构建而成的。Dockerfile 中的每一层都是一个命令。以下是上述 Dockerfile 的内容:

FROM alpine # base alpine
WORKDIR /usr/src/app
RUN apk add --no-cache --update nodejs nodejs-npm # install Node.js and npm
COPY package.json package-lock.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

FROM 指令会创建一个层,WORKDIR 和 RUN 等指令也会创建层。所有层都是只读的,只有最后一个层 CMD 是可写的。只读层可以在容器之间共享,这意味着同一个镜像可以在多个容器之间共享。

这里发生的情况是,Docker 使用存储驱动程序来管理只读层和可写容器层。这个可写容器层是临时层,会在容器删除后被删除。这真是太棒了。但这为什么重要呢?

通过减少图层数量,我们可以获得更小的图像。这就是多阶段构建技术的作用所在。

FROM alpine AS multistage
WORKDIR /usr/src/app
RUN apk add --no-cache --update nodejs nodejs-npm
COPY package.json package-lock.json ./
RUN npm install --production
#
FROM alpine
WORKDIR /usr/src/app
RUN apk add --no-cache --update nodejs
COPY --from=multistage /usr/src/app/node\_modules ./node\_modules
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

我们仅使用第一个镜像来安装依赖项,然后在最终镜像中,我们复制所有 node_modules 目录,而不进行任何构建或安装。我们甚至可以在最终镜像中跳过npm 的安装!

想猜猜最终尺寸吗?来吧!

我认为我们做得很好,文件大小降到了48.6MB,比之前提高了15 倍,这值得骄傲。

判决

别天真了,生产环境中完全没必要使用几GB大小的镜像。一个很好的第一步是使用小的基础镜像。从小处着手,循序渐进就好。

选择优化的基础镜像将大有裨益。如果您确实需要提升部署速度,并且饱受缓慢的 CI/CD 流水线困扰,不妨了解一下多阶段构建。相信您以后会爱上这种方法。

:我省略了一个包含开发依赖项的示例,该示例用于在部署到生产环境之前运行测试,因为它与最终生产环境运行时的体积缩减无关。当然,这是一个有效的用例!欢迎在下方评论区分享您的想法。我很想听听您的意见!

如果您想查看我之前撰写的有关 Docker 和 Kubernetes 的 DevOps 相关文章,欢迎访问我的个人资料页面

希望你们喜欢这篇文章,就像我喜欢写它一样。你们觉得这篇教程对别人有用吗?欢迎分享。如果你喜欢这篇文章,请点赞下面的独角兽图标,这样更多人就能在 DEV.to 上看到它了。


文章来源:https://dev.to/adnanrahic/a-crash-course-on-optimizing-your-docker-images-for-production-2f9l