【Docker】学习 Docker 的过程中,我是这样把镜像越做越小的

最近在学习 Docker,练习给一个 Node.js 项目打包镜像。 一开始我也没多想,就照着教程写了个 Dockerfile,结果构建出来的镜像体积巨大,上传和部署都非常慢。于是我就顺着问题一路优化下去,也踩了不少坑。这里把我的学习过程整理一下。

镜像构建命令: docker build -t name:tag -f filename .

举例: docker build -t nest:first .


第一步:从 .dockerignore 开始

刚开始我发现,构建出来的镜像里竟然有 node_modules.git、甚至 .vscode 等乱七八糟的文件。后来才知道,Docker 会把整个构建上下文打包进去,除非手动排除。

于是我加了个 .dockerignore

.dockerignore 复制代码
*.md # 忽略所有 md 结尾的文件
!README.md # 不包括 README.md
node_modules/ # 忽略 node_modules 下 的所有文件
[a-c].txt # 忽略 a.txt、b.txt、c.txt 这三个文件
.git/
.DS_Store
.vscode/
.dockerignore
.eslintignore
.eslintrc
.prettierrc
.prettierignore

这样就不会把无关文件带进去了。镜像体积立马降了一些。


第二步:尝试多阶段构建

最初的 Dockerfile 写得很"直白":

  • 基于 node20 的镜像
  • 指定 /ahao 目录为容器内的工作目录
  • 将 package.json 复制到容器内的 "工作目录" 中,
  • 执行 npm install
  • 将当前目录下的所有文件复制到容器内的 "工作目录" 中
  • 运行 npm run build
  • 暴露端口 3000
  • 容器跑起来以后执行 node ./dist/main.js 命令

对应 dockerfile:

Dockerfile 复制代码
FROM node:20
​
WORKDIR /ahao
​
COPY package.json .
​
RUN npm install
​
COPY . .
​
RUN npm run build
​
EXPOSE 3000
​
CMD [ "node", "./dist/main.js" ]

执行命令: docker build -t nest:first .

构建结果:

镜像大小:

镜像内容:

这样虽然能跑,但问题也很明显:

  • 镜像里包含了整个源码,没必要。
  • 所有依赖都装了,体积太大。

而源码只是在构建时需要,运行时只需要 dist 目录就可以了,这个时候就需要采用多阶段构建

多阶段构建:

思路就是:

  • 第一个阶段只负责编译,把 dist 目录生成好。
  • 第二个阶段才是真正的运行环境,只带需要的文件和依赖。
Dockerfile 复制代码
# build stage
FROM node:20 as build-stage
​
WORKDIR /ahao
​
COPY package.json .
​
RUN npm install
​
COPY . .
​
RUN npm run build
​
# production stage
FROM node:20 as production-stage
​
COPY --from=build-stage /ahao/dist /ahao
COPY --from=build-stage /ahao/package.json /ahao/package.json
​
WORKDIR /ahao
​
RUN npm install --production
​
EXPOSE 3000
​
CMD ["node", "/ahao/main.js"]

dockerfile 解析:

  • 通过 FROM 继承镜像时,给当前镜像指定一个名字,比如 build stage
  • 第一个镜像执行 build
  • 再通过 FORM 继承 node 镜像创建一个新的镜像
  • 通过 COPY --from=build-stage 从上一个镜像内复制 /ahao/dist 的文件到当前镜像的 /ahao 下
  • package.json 同理
  • 执行 npm install --production 只安装 dependencies 依赖
  • 暴露端口 3000
  • 容器跑起来以后执行 node ./ahao/main.js 命令

执行命令: docker build -t nest:second .

构建结果:

镜像大小:

镜像内容:


第三步: 更换基础镜像为 alpine 版本

继续优化,我发现 基础镜像本身就很大 。比如 node:20 默认镜像有几百 MB,而 node:20-alpine 只有几十 MB。

所以我把 Dockerfile 改成了 alpine 版本:

vbnet 复制代码
FROM node:20-alpine as build-stage
...
FROM node:20-alpine as production-stage
...

执行命令: docker build -t nest:third .

构建结果:

镜像大小:

这一改,镜像体积再次缩小。效果非常明显。

学习总结

这一路优化下来,我的收获是:

  1. 学会用 .dockerignore 控制上下文,不要把没用的东西打包进去。
  2. 多阶段构建 能把编译环境和运行环境分开,减少镜像体积。
  3. 选择合适的基础镜像(比如 alpine),能显著减小体积。
相关推荐
80530单词突击赢2 分钟前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
爬山算法22 分钟前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
WeiXiao_Hyy39 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇1 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
忆~遂愿1 小时前
CANN metadef 核心解析:计算图原型定义、算子元数据抽象与异构系统互操作机制
docker·容器
吃杠碰小鸡1 小时前
高中数学-数列-导数证明
前端·数学·算法
long3161 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
rannn_1111 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习