Skip to content

Docker 部署 Next.js

Next.js 的 Docker 部署很有代表性,因为它涉及构建过程(Build)和运行过程(Run)。

通过本章,学会生产环境级别的部署,我们将使用 Docker 的多阶段构建 (Multi-stage Build) 技术。这能让你的最终镜像体积非常小(通常从 1GB+ 缩减到 100MB 左右)。

准备工作:Next.js 项目配置

在开始写 Dockerfile 之前,我们需要对 Next.js 项目做一个微小的配置调整,以大幅优化 Docker 镜像大小。

修改 next.config.js (或 next.config.mjs)

需要开启 standalone 模式。这告诉 Next.js 在构建时,自动分析并只打包实际用到的文件。

javaScript
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "standalone", // <--- 加上这一行关键配置
};

export default nextConfig;

1.创建 .dockerignore 文件

这一步很重要!我们不希望把本地庞大的 node_modules 文件夹或者 .git 文件夹复制到 Docker 镜像里,这会拖慢构建速度。

在项目根目录下新建 .dockerignore 文件

txt
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.git
.next

2.编写 Dockerfile

在项目根目录下新建 Dockerfile。我们将把构建过程分为三个阶段

  1. 依赖安装 (deps)
  2. 代码构建 (builder)
  3. 最终运行 (runner)

请直接复制以下代码:

Dockerfile
# ---------------------------------------------------
# 1. 依赖阶段:只用来安装依赖
# ---------------------------------------------------
FROM node:18-alpine AS deps
WORKDIR /app

# 复制 package.json 和 lock 文件
COPY package.json package-lock.json ./

# 安装依赖 (使用 ci 命令比 install 更快更稳定)
RUN npm ci

# ---------------------------------------------------
# 2. 构建阶段:编译 Next.js 代码
# ---------------------------------------------------
FROM node:18-alpine AS builder
WORKDIR /app

# 从上一个阶段(deps)拷贝 node_modules
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 开始构建 (这会生成 .next 文件夹)
RUN npm run build

# ---------------------------------------------------
# 3. 运行阶段:最终的生产镜像
# ---------------------------------------------------
FROM node:18-alpine AS runner
WORKDIR /app

# 设置环境变量为生产模式
ENV NODE_ENV=production

# 创建一个非 root 用户是为了安全 (可选,但推荐)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# 把构建产物里的 public 文件夹拷过来 (图片等静态资源)
COPY --from=builder /app/public ./public

# 关键步骤:只拷贝 standalone 构建出来的核心文件
# 这部分文件非常小,不包含多余的开发依赖
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

# 切换用户
USER nextjs

# 暴露端口
EXPOSE 3000

# 启动命令 (注意:standalone 模式下,启动入口是 server.js)
CMD ["node", "server.js"]

3.构建并运行

  1. 构建镜像

给这个镜像取名叫 my-next-app

bash
docker build -t my-next-app .

❗注意:第一次构建可能需要几分钟,因为要下载 Node.js 基础镜像并安装依赖。

  1. 运行容器

把本机的 3000 端口映射到容器的 3000 端口

bash
docker run -p 3000:3000 my-next-app
  1. 验证

打开浏览器访问 http://localhost:3000。 你应该能看到你的 Next.js 页面正在运行!

Q&A

为什么不直接把代码拷进去运行 npm run start

  1. 体积差异巨大:
  • 普通写法:镜像可能 > 1GB (包含了数万个开发依赖文件)。
  • 这种写法:镜像可能只有 120MB 左右。
  1. 安全性: 生产环境镜像里没有源码,只有编译后的产物。

  2. 缓存优化: 如果你只改了代码没改 package.json,Docker 会自动跳过“安装依赖”这一步,构建速度飞快。

拓展

使用 pnpm or yarn

默认情况下使用 RUN npm ci 如果使用 pnpm or yarn

pnpm

Dockerfile
# 先开启 corepack (Node 16/18+ 自带) 激活corepack 默认不激活
RUN corepack enable 
# 好处
# Corepack 无需手动安装:“自动档” 你不需要再运行 npm install -g yarn 或 pnpm。激活 Corepack 后,这些命令会自动可用。
#版本锁定(最重要): Corepack 会读取你项目 package.json 中的 packageManager 字段。
#如果你的 package.json 写着 "packageManager": "yarn@3.2.0"。
#当你运行 yarn install 时,Corepack 会自动下载并使用 Yarn 3.2.0,而不是你本地可能安装的 Yarn 1.x。
#这保证了团队所有成员和 CI/CD 环境使用的包管理器版本完全一致,避免了“我这边能跑,你那边报错”的问题。

# 复制 pnpm.lock
COPY package.json pnpm-lock.yaml ./
# 使用 pnpm 安装依赖
RUN pnpm install --frozen-lockfile

yarn

Dockerfile
RUN corepack enable
# 复制 yarn.lock
COPY package.json yarn.lock ./
# 使用 yarn 安装依赖
RUN yarn install --frozen-lockfile

注意:后面的步骤中,如果遇到 npm run build 也可以改成 yarn build,但通常 npm run 也能兼容调用脚本。


关于 --frozen-lockfile

“环境一致性”

  • npm install : 宽容。 如果 package.json 里写了 "react": "^18.0.0",而 package-lock.json 里锁定了 18.2.0。

  • npm ci : 全称 Clean Install,它是严厉的。它只看 package-lock.json

Yarn 和 pnpm 的设计哲学稍微不同它们没有专门搞一个 ci 命令,而是通过给 install 命令加参数来实现相同的“严厉模式”。 这就是 --frozen-lockfile 的含义。

注: 在 Yarn 2+ (Berry) 版本中,这个参数改名为 --immutable,意思是一样的:不可变。

总结对比表

包管理器本地开发用 (宽容)Docker/生产环境用 (严厉)作用
npmnpm installnpm ci必须有 lock 文件,死板,速度快
Yarnyarn installyarn install --frozen-lockfile冻结 lock 文件,禁止更新
pnpmpnpm installpnpm install --frozen-lockfile同上