夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node基础

  • 《MySQL》学习笔记

  • Midway

  • Nest

    • 开篇词
    • 学习理由
    • nest概念扫盲
    • 快速掌握 nestcli
    • 5种http数据传输方式
    • IoC 解决了什么痛点问题?
    • 如何调试 Nest 项目
    • Provider注入对象
    • 全局模块和生命周期
    • AOP 架构有什么好处?
    • 一网打尽 Nest 全部装饰器
    • Nest如何自定义装饰器
    • Metadata和Reflector
    • ExecutionContext切换上下文
    • Module和Provider的循环依赖处理
    • 如何创建动态模块
    • Nest和Express,fastify
    • Nest的Middleware
    • RxJS和Interceptor
    • 内置Pipe和自定义Pipe
    • ValidationPipe验证post请求参数
    • 如何自定义 Exception Filter
    • 图解串一串 Nest 核心概念
    • 接口如何实现多版本共存
    • Express如何使用multer实现文件上传
    • Nest使用multer实现文件上传
    • 图书管理系统
    • 大文件分片上传
    • 最完美的 OSS 上传方案
    • Nest里如何打印日志
    • 为什么Node里要用Winston打印日志
    • Nest 集成日志框架 Winston
    • 通过Desktop学Docker也太简单了
    • 你的第一个 Dockerfile
    • Nest 项目如何编写 Dockerfile
      • dockerfile 和 docker daemon
      • .dockerignore
      • 此外,还有一种减小镜像体积的手段:多阶段构建。
      • 有同学说,但现在镜像依然很大呀,那是因为我们用的基础的 linux 镜像比较大,可以换成 alpine 的,这是一个 linux 发行版,主打的就是一个体积小。
      • 总结
    • 提升 Dockerfile 水平的 5 个技巧
    • Docker 是怎么实现的
    • 为什么 Node 应用要用 PM2 来跑?
    • 快速入门 MySQL
    • SQL 查询语句的所有语法和函数
    • 一对一、join 查询、级联方式
    • 一对多、多对多关系的表设计
    • 子查询和 EXISTS
    • SQL 综合练习
    • MySQL 的事务和隔离级别
    • MySQL 的视图、存储过程和函数
    • Node 操作 MySQL 的两种方式
    • 快速掌握 TypeORM
    • TypeORM 一对一的映射和关联 CRUD
    • TypeORM 一对多的映射和关联 CRUD
    • TypeORM 多对多的映射和关联 CRUD
    • 在 Nest 里集成 TypeORM
    • TypeORM保存任意层级的关系
    • 生产环境为什么用TypeORM的migration迁移功能
    • Nest 项目里如何使用 TypeORM 迁移
    • 如何动态读取不同环境的配置?
    • 快速入门 Redis
    • 在 Nest 里操作 Redis
    • 为什么不用 cache-manager 操作 Redis
    • 两种登录状态保存方式:JWT、Session
    • Nest 里实现 Session 和 JWT
    • MySQL + TypeORM + JWT 实现登录注册
    • 基于 ACL 实现权限控制
    • 基于 RBAC 实现权限控制
    • access_token和refresh_token实现无感登录
    • 单token无限续期实现登录无感刷新
    • 使用 passport 做身份认证
    • passport 实现 GitHub 三方账号登录
    • passport 实现 Google 三方账号登录
  • 其他

  • 服务端
  • Nest
夜猫子
2025-03-10
目录

Nest 项目如何编写 Dockerfile

# dockerfile 和 docker daemon

首先思考一个问题:

dockerfile 是在哪里 build 的,在命令行工具里,还是在 docker 守护进程呢?

答案是在守护进程 docker daemon。

我没启动 docker daemon 的时候是不能 build 的,启动之后才可以:

命令行工具会和 docker daemon 交互来实现各种功能。

比如 docker build 的时候,会把 dockerfile 和它的构建上下文(也就是所在目录)打包发送给 docker daemon 来构建镜像。

比如我们会执行这样的命令:

docker build -t name:tag -f filename .
1

这个 . 就是构建上下文的目录,你也可以指定别的路径。

# .dockerignore

而镜像自然是越小性能越好,所以 docker 支持你通过 .dockerignore 声明哪些不需要发送给 docker daemon。

.dockerignore 是这样写的:

*.md
!README.md
node_modules/
[a-c].txt
.git/
.DS_Store
.vscode/
.dockerignore
.eslintignore
.eslintrc
.prettierrc
.prettierignore
1
2
3
4
5
6
7
8
9
10
11
12

*.md 就是忽略所有 md 结尾的文件,然后 !README.md 就是其中不包括 README.md

node_modules/ 就是忽略 node_modules 下 的所有文件

[a-c].txt 是忽略 a.txt、b.txt、c.txt 这三个文件

.DS_Store 是 mac 的用于指定目录的图标、背景、字体大小的配置文件,这个一般都要忽略

eslint、prettier 的配置文件在构建镜像的时候也用不到

此外,还有注释的语法:

这就是 dockerfile 的全部语法,没多少东西。

docker build 时,会先解析 .dockerignore,把该忽略的文件忽略掉,然后把剩余文件打包发送给 docker daemon 作为上下文来构建产生镜像。

这就像你在 git add 的时候,.gitignore 下配置的文件也会被忽略一样。

忽略这些用不到的文件,是为了让构建更快、镜像体积更小。

# 此外,还有一种减小镜像体积的手段:多阶段构建。

我们会先把源码目录发送到 docker daemon 中执行 npm run build 来构建产物,之后再 node ./dist/main.js 把服务跑起来。

也就是这样:

新建个项目:

nest new dockerfile-test -p npm
1

编写 .dockerignore:

*.md
node_modules/
.git/
.DS_Store
.vscode/
.dockerignore
1
2
3
4
5
6

编写 Dockerfile:

FROM node:18

WORKDIR /app

COPY package.json .

RUN npm config set registry https://registry.npmmirror.com/

RUN npm install

COPY . .

RUN npm run build

EXPOSE 3000

CMD [ "node", "./dist/main.js" ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

基于 node 18 的镜像。

指定当前目录为容器内的 /app。

把 package.json 复制到容器里,设置淘宝的 npm registry,执行 npm install。

之后把其余的文件复制过去,执行 npm run build。

指定暴露的端口为 3000,容器跑起来以后执行 node ./dist/main.js 命令。

然后执行 docker build:

docker build -t nest:first .
1

镜像名为 nest、标签为 first,构建上下文是当前目录

然后就可以在 docker desktop 里看到你构建出来的镜像了:

如果你 build 的时候报这个错误:

那需要加一行:

RUN ln -s /sbin/runc /usr/bin/runc

原因如下:

点击 run 把它跑起来:

容器跑成功了:

浏览器访问下也没啥问题:

这样我们就用 docker 把我们的 nest 应用跑起来了!

但现在 docker 镜像还是不完美的。

这样构建出来的镜像有什么问题呢?

明显,src 等目录就不再需要了,构建的时候需要这些,但运行的时候只需要 dist 目录就可以了。

把这些文件包含在内,会让镜像体积变大。

那怎么办呢?

构建两次么?第一次构建出 dist 目录,第二次再构建出跑 dist/main.js 的镜像。那不是要两个 dockerfile?

确实需要构建两次,但只需要一个 dockerfile 就可以搞定。

这需要用到 dockerfile 的多阶段构建的语法。

# build stage
FROM node:18 as build-stage

WORKDIR /app

COPY package.json .

RUN npm config set registry https://registry.npmmirror.com/

RUN npm install

COPY . .

RUN npm run build

# production stage
FROM node:18 as production-stage

COPY --from=build-stage /app/dist /app
COPY --from=build-stage /app/package.json /app/package.json

WORKDIR /app

RUN npm config set registry https://registry.npmmirror.com/

RUN npm install --production

EXPOSE 3000

CMD ["node", "/app/main.js"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

通过 FROM 继承镜像的时候,给当前镜像指定一个名字,比如 build-stage。

然后第一个镜像执行 build。

之后再通过 FROM 继承 node 镜像创建一个新镜像。

通过 COPY --from-build-stage 从那个镜像内复制 /app/dist 的文件到当前镜像的 /app 下。

还要把 package.json 也复制过来,然后切到 /app 目录执行 npm install --production 只安装 dependencies 依赖

这个生产阶段的镜像就指定容器跑起来执行 node /app/main.js 就好了。

执行 docker build,打上 second 标签:

docker build -t nest:second .
1

把之前的容器停掉,把这个跑起来:

这次用 3003 端口来跑:

浏览器访问下:

nest 服务跑成功了。

这时候 app 下就是有 dist 的文件、生产阶段的 node_modules、package.json 这些文件:

对比下镜像体积,明显看出有减小,少的就是 src、test、构建阶段的 node_modules 这些文件:

这就是多阶段构建(multi-stage build)的魅力。

# 有同学说,但现在镜像依然很大呀,那是因为我们用的基础的 linux 镜像比较大,可以换成 alpine 的,这是一个 linux 发行版,主打的就是一个体积小。

FROM node:18.0-alpine3.14 as build-stage

WORKDIR /app

COPY package.json .

RUN npm config set registry https://registry.npmmirror.com/

RUN npm install

COPY . .

RUN npm run build

# production stage
FROM node:18.0-alpine3.14 as production-stage

COPY --from=build-stage /app/dist /app
COPY --from=build-stage /app/package.json /app/package.json

WORKDIR /app

RUN npm config set registry https://registry.npmmirror.com/

RUN npm install --production

EXPOSE 3000

CMD ["node", "/app/main.js"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

node:18-alpine3.14 就是用 alpine 的 linux 的 3.14 版本,用 node 的 18.0 版本。

然后再 docker build 一下。

docker build -t nest:ccc .
1

可以看到现在镜像体积只有 277M 了:

一般情况下,我们都会用多阶段构建 + alpine 基础镜像。

alpine 是一种高山植物,就是很少的养分就能存活,很贴合体积小的含义。

案例代码在小册仓库 (opens new window)。

# 总结

docker build 的时候会把构建上下文的所有文件打包发送给 docker daemon 来构建镜像。

可以通过 .dockerignore 指定哪些文件不发送,这样能加快构建时间,减小镜像体积。

此外,多阶段构建也能减小镜像体积,也就是 build 一个镜像、production 一个镜像,最终保留下 production 的镜像。

而且我们一般使用 alpine 的基础镜像,类似 node:18.10-alpine3.14,这样构建出来镜像体积会小很多。

这就是用 Nest 项目构建 Docker 镜像的方式。

编辑 (opens new window)
上次更新: 2025/5/30 17:50:19
你的第一个 Dockerfile
提升 Dockerfile 水平的 5 个技巧

← 你的第一个 Dockerfile 提升 Dockerfile 水平的 5 个技巧→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式