• 我是如何将1.16GB的Docker镜像缩小到到162MB的
  • 发布于 2个月前
  • 395 热度
    0 评论

确保拥有轻量级的 Docker 镜像可以加快 Docker 容器的构建和部署。在构建 Docker 镜像时,你可以使用一些方法,在不影响性能的情况下减小其大小。本文将向你传授瘦身 Docker 镜像的最佳策略和技巧。如果你正在寻找一种更简单的方法来管理你的 Docker 镜像,那么本文就是为你量身定制的。


Docker 镜像可以让你轻松地将应用程序部署到不同的基础设施,如 Kubernetes 集群、云平台和 CI/CD 管道。在部署和管理应用程序时,这些 Docker 镜像的大小很重要。大的镜像会带来限制,必须扩大存储容量,从而导致费用增加。应用程序的 Docker 镜像大小会影响性能、可扩展性、可移植性等关键方面,并可能导致安全漏洞。


构建大型 Docker 镜像并将其推送到 DockerHub 和 ECR 等注册中心需要大量时间。此外,当你需要从注册表中提取应用程序镜像时,大型镜像也会减慢下载速度,从而增加构建和部署应用程序的时间。让我们深入了解为 Docker 镜像瘦身的最佳策略,以及如何使用这些策略来减小 Docker 镜像的大小。

一.先决条件
要最小化和精简 Docker 镜像,请确保:
1.你的计算机上安装并运行了 Docker 和 Docker Desktop。

2.具备使用 Docker 的基本知识。


二.最小化和精简 Docker 镜像最佳策略

优化 Docker 镜像并将其缩小到最小尺寸,可以大大减少构建和推送镜像所需的成本和时间。在本节中,我们将讨论缩小 Docker 镜像并减小其大小的不同策略。在此之前,你需要创建一个可以使用的 Docker 镜像。


创建 Docker 镜像
本节要创建的 Docker 镜像用于一个简单的 Node.js 应用程序。你将在本文的不同章节中使用该 Docker 镜像。你将按照接下来章节中讨论的策略缩小镜像的大小。你可以在 GitHub 代码库中找到该应用程序的代码。
GitHub:https://github.com/Rose-stack/Typescript-Nodejs
基于这个 Node.js 应用程序,下面将是创建镜像的理想 Dockerfile:
FROM node:19
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 4000
CMD npm start
运行以下命令来构建应用程序镜像:
# 堆代码 duidaima.com
docker build -t node_example .
使用以下命令检查镜像是否创建成功:
docker images node_example
为该应用程序创建的镜像大小如下:
REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
node_example   latest    a10aa8869a21   39 seconds ago   1.16GB
要运行这么小的应用程序,这个镜像实在是太大了。让我们为这个 Docker 镜像瘦身吧。

选择基础镜像

基础镜像是创建 Docker 镜像的起点,通常包括底层操作系统以及任何必要的软件和软件包。Docker 的常用基础镜像包括 Alpine Linux、Ubuntu、Debian、CentOS 和 Fedora。在 Docker 中运行 Node.js 应用程序时,一个常见的基础镜像是 Docker Hub 上提供的 Node.js 官方镜像。不过,Node.js 还提供其他带有不同标签的变体镜像发行版,从而使 Docker 基础镜像更加精简。

这些标签包括:  

Bullseye:提供 Debian 发行版,以减少镜像需要安装的软件包数量,从而减小自定义镜像的整体大小。
Alpine:任何 Node.js Alpine 标签均源自 Alpine Linux,可提供约 5MB 的较小基础镜像发行版。
Slim:Slim 标签只包含运行 Node.js 应用程序所需的基本软件包,通过消除不必要的软件包有效减小镜像的大小。  
所有这些标签都可以在 Docker Hub 上的 Node.js 官方镜像页面上找到,你可以根据要使用的发行版来选择标签,以减小 Docker 镜像的大小。  
下面是使用 Alpine 标签分发的示例:
FROM node:19-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 4000
CMD npm start
将此应用到 Dockerfile 中,可减少镜像大小,如下所示:
REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
node_example   latest    a10aa8869a21   39 seconds ago   338.7MB
你使用的每个基础镜像都会提供其发行版。请确保你经常检查提供小型镜像构建的基础镜像标签。

使用 .dockerignore 来最小化和精简 Docker 镜像
在创建上述镜像时,COPY.. 命令会复制项目目录中的所有文件和文件夹。在这个简单的例子中,应用程序的结构如下:

仔细观察,Docker 并不需要复制所有这些文件夹和文件。例如,DockerFile 中的 RUN npm run build 命令将创建应用程序构建文件夹,而 RUN npm install 命令将创建 node_modules 文件夹。因此,在构建 Docker 镜像时不需要复制这些文件。复制的每个文件夹和文件都会增加镜像的大小。最好避免不必要的复制。


.dockerignore 文件允许你指定用于构建 Docker 镜像的上下文中应排除的文件和目录。要使用它,请在与 Dockerfile 相同的目录下创建 .dockerignore 文件,并添加镜像构建过程中不需要的文件和目录。
在 .dockerignore 文件中添加以下内容:
node_modules
Dockerfile
build
重建镜像并检查大小:
REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
node_example   latest    c1b36fb72af3   12 seconds ago   264MB
因此,在 .dockerignore 文件中添加不必要的文件可以创建更小的 Docker 镜像。至此,最初 1.16GB 的镜像已减至 264MB。

Docker 镜像层
Docker 镜像层是 Docker 镜像的组成部分。单个镜像会根据 Dockerfile 中的指令分层构建。Dockerfile 中的每一条指令都会逐步创建一个新层。让我们创建一个新的 Dockerfile 来演示如何创建层以及如何减少层。到现在,我们创建的 Dockfile 示例将应用程序与生产和开发所需的所有依赖项打包在一起。创建的 Dockerfile 构建了一个适用于开发层的镜像层,但它并未针对生产进行优化。下面的 Dockerfile 创建的镜像层可用于生产目的:
FROM node:19-alpine
WORKDIR /app
COPY package*.json ./
COPY . .
RUN yarn install
RUN npm run build
RUN rm -rf node_modules
RUN npm install --production
EXPOSE 4000
CMD npm start
这将创建以下大小的图像,以备使用:
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
node_example   latest    fbebed577ab2   2 minutes ago  266.34MB
这些新命令为 Docker 镜像添加了更多层。

在本地计算机的 Docker 桌面上,导航到已创建的镜像,就能清楚地看到镜像层,如下所示:

Docker 显示该镜像有 18 层。这包括用于打包基础镜像的层。不过,你需要负责十个层,你可以根据用于构建此镜像的 Dockerfile 来控制这些层。
层是根据它们执行的命令来管理的。这意味着你可以将执行相同功能的连续命令组合在一起。上面的示例中有多个连续的 RUN 命令。每个命令都会创建一个自己的层,在本例中就创建了 4 个层。不过,你可以按如下方式将它们合并为一个图层:
FROM node:19-alpine
WORKDIR /app
COPY package*.json ./
COPY . .
RUN npm install && \
    npm run build && \
    rm -rf node_modules && \
    npm install --production
EXPOSE 4000
CMD npm start

当你基于此构建镜像时,镜像层数将减少到 15:

这将整体镜像大小从 266MB 大幅减少到 194MB:
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
node_example   latest    bd92d29f12ff   2 minutes ago   194MB
使用多阶段构建来精简 Docker 镜像

Dockerfile 是构建 Docker 镜像的基础。它们为 Docker 打包应用程序指定了必要的指令。传统上,应用程序遵循构建模式来捆绑其资源,这意味着你需要构建应用程序代码,以确定如何在生产环境中提供服务。在遵循构建模式的同时使用 Docker 意味着你必须创建两个 Dockerfile 才能完全打包应用程序。因此,最终会创建两个镜像,每个镜像都占用各自的磁盘空间。  


以本指南中使用的 TypeScript 应用程序为例。TypeScript 必须编译成 JavaScript 后才能执行。因此,你需要两个 Dockerfile 用于开发和生产。一个 Dockerfile 用于打包编译阶段的应用程序,另一个 Dockerfile 用于在生产环境中运行编译后的代码。此外,在许多情况下,生产环境所需的依赖关系与开发环境所需的依赖关系不同。因此,通常使用单独的 Dockerfile 来指定这些依赖关系。  


不过,使用多个 Dockerfiles 运行相同的应用程序并不是优化最终构建的理想选择,因为这会导致镜像体积过大,占用大量磁盘空间。  多阶段构建的概念允许你从不同阶段创建单个 Dockerfile 来创建最终镜像。它定义了多个 FROM 语句。每个语句都会创建一个新的构建阶段,并为该阶段设置基础镜像。通过这种方式,Docker 允许你从不同阶段复制内容,以生成最终镜像。  
使用生产和开发 Dockerfile 示例,我们可以使用下面的多阶段 Dockerfile 构建相同的镜像:
#Stage One: Build
FROM node:19-alpine AS builder
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build

#Stage Two: Final
FROM node:19-alpine AS final
WORKDIR /app
COPY --from=builder ./app/build ./build
COPY package*.json ./
RUN npm install --production
CMD npm start
在本例中,第一阶段创建了应用程序构建。第二阶段将复制该构建,以创建最终镜像。生成器阶段将在第一阶段创建镜像。使用 COPY --from=builder 后,最后阶段将复制第一阶段的结果,并将其用于打包生产中的最终镜像。

在这种情况下,你只需一个 Dockerfile 就能为你构建应用程序,并在一个镜像下创建最终的生产就绪镜像。这样,就可以减少镜像层的数量:

Docker 镜像的总大小:
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
node_example   latest    b066d92ab741   2 minutes ago   181MB

多阶段构建并不局限于使用同一基础镜像的相同 FROM 语句。它可用于同时对多个应用程序进行 dockerize,以最大限度地降低总体资源利用率。这个多阶段构建示例使用 Node。js 和 Nginx 基本镜像的两个 FROM 语句来实现多阶段构建概念。


值得注意的是,在使用多阶段构建时,Docker 并不知道你所处的确切环境。Docker 会按照你的 Dockerfile 中定义的指令来创建最终镜像。不过,你可以为开发和生产构建镜像,使用 Docker --target 标志指定目标阶段,以运行 Docker 构建命令。要同时为开发和生产阶段创建镜像,请按如下方式指定构建阶段:
docker build --target builder -t node_example:dev .
docker build --target final -t node_example:prod .
使用工具缩小 Docker 镜像大小
一些工具可以帮助你进一步缩小镜像大小。它们提供内置的图像压缩功能,可创建比原始镜像更小的镜像。它们包括:
DockerSlim 可以删除不必要的文件和依赖项,并创建只包含必要组件的新的、精简的镜像。
Dive 可以分析你的镜像层元数据。然后识别未使用的依赖项、重复文件和其他你可以删除以减小最终镜像大小的低效内容。
Docker-squash 可以将多个镜像层压缩成单个层。
要使用 Docker-squash 压缩 Docker 镜像,可以在 docker build 命令中添加 --squash 标志。例如:
docker build . -t node_example --squash
这将把最终镜像压缩为一个层。在运行 --squash 之前,请确保 experimental 设置为 true。这是因为 --squash 功能是 Docker 的一项实验性功能。它并不完全受支持,可能会导致与某些 Docker 注册表或工具的兼容性问题。
导航到 Docker 桌面上的 Docker 设置,并按如下方法将 experimental 设置为 true:

镜像进一步缩小至 162MB:
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
node_example   latest    b066d92ab741   1 minutes ago   162MB
你可以查看如何使用 DockerSlim 将当前镜像大小(162MB)进一步缩小到约 91.5%。  
DockerSlim 介绍:https://earthly.dev/blog/docker-slim/
现在,有了新发现的知识,何不使用 GitHub Actions 和 EKS 自动部署 Kubernetes CI/CD,或深入学习 Terraform 功能的所有知识,并结合实例和最佳实践,像专业人士一样提升基础设施工作流程。

三.总结
Docker 可确保你将应用程序打包,以便在 Kubernetes 集群、云平台和 CI/CD 管道等不同基础架构之间更轻松地移植。这使得在不同环境中共享现有应用程序时,更容易对其进行更改,而不会影响兼容性。鉴于 Docker 镜像大小会占用大量磁盘空间,在本指南中,你学到了以下 Docker 镜像优化策略:
1.使用 .dockerignore 文件
2.减少 Docker 镜像层
3.如何选择 Docker 基本镜像
4.如何使用多阶段构建来减少 Docker 镜像大小

5.减少 Docker 镜像大小的工具


使用这些策略,你成功地将 1.16GB 的 Docker 镜像缩小到到 162MB。你已经学会了将 Docker 镜像大小减小到约 85.86% 所需的工具和技巧。希望这篇文章对你有所帮助。
用户评论