优化 Next.js Docker 镜像:减少镜像大小的有效方法

前言

Next.js 除了在 vercel 上部署之外,更多的是部署在自己的服务器上,为此官方提供了 Docker 的打包运行方式。这里分享的是如何优化打包的 Docker 镜像大小。

新建默认项目

安装官方的命令建立项目。

1
npx create-next-app@latest

然后在项目根目录新建Dockerfile 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM node:20

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

EXPOSE 3000

CMD npm start

之后运行打包命令

1
docker image build -t image-size-initial .

https://pan.micromatrix.org/file/365b3b43f6077cd5f756a.png

然后会发现近 2GB 的大小。

第一步优化 使用较小的基础版本镜像

修改 node 版本,使用较小的基础版本镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM node:20-apline

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

EXPOSE 3000

CMD npm start

之后运行打包命令

1
docker image build -t image-size-apline .

https://pan.micromatrix.org/file/cf093a6406bb0c55eb9db.png

发现少了一半的大小。

第二步 多阶段构建

https://pan.micromatrix.org/file/03b1d485fbf0da8508251.png

也就每个阶段只构建需要的文件,而不是全部都包含在一起

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
31
32
33
## 这里安装node_modules
FROM node:20-apline AS deps

WORKDIR /app

COPY package*.json ./

RUN npm ci

## 这里是Nextjs打包输出的版本
FROM node:20-apline AS builder

WORKDIR /app

## 拷贝来自deps阶段的/app/node_modules文件
COPY --from=deps /app/node_modules ./node_modules

COPY . .

RUN npm run build

## 这里就是打包完之后运行的版本
FROM node:20-apline AS runner
WORKDIR /app

COPY --from=builder /app/next.config.mjs ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next

EXPOSE 3000

CMD ["node_modules/.bin/next", "start"]

之后运行打包命令,修改标签名

https://pan.micromatrix.org/file/68c7479ee527cdcfbce61.png

可观察到镜像少了 300MB 的样子。

第三步 Next.js 输出文件配置

修改 next.js.config.mjs 的内容,设置output的值为standalone 。这里要注意的是官方提醒

We do not recommend using the runtimeConfig option, as this does not work with the standalone output mode. Instead, we recommend incrementally adopting the App Router. 我们不建议使用 runtimeProtect 选项,因为这不适用于独立输出模式。相反,我们建议逐步采用应用程序路由器。

1
2
3
4
5
6
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
};

export default nextConfig;

之后运行npm run build 命令,会在.next文件夹中发现一个stanalone文件夹,里面包含了所有的文件。

修改 Dockerfile 文件

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
31
32
33
34
## 这里安装node_modules
FROM node:20-apline AS deps

WORKDIR /app

COPY package*.json ./

RUN npm ci

## 这里是Nextjs打包输出的版本
FROM node:20-apline AS builder

WORKDIR /app

## 拷贝来自deps阶段的/app/node_modules文件
COPY --from=deps /app/node_modules ./node_modules

COPY . .

RUN npm run build

## 这里就是打包完之后运行的版本
FROM node:20-apline AS runner
WORKDIR /app

COPY --from=builder /app/.next/standalone ./

EXPOSE 3000

ENV PORT=3000

ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

之后打包可得镜像

https://pan.micromatrix.org/file/4f7033ecbd5fb3704f83e.png

看,现在只有135.56mb内容。实际上官方给的镜像文件就是这样的,这里正好分解学习一下为什么要这么写。