切,写个 dockerfile 能有多难,这不是有手就行?
于是就有了以下 dockerfile
FORM node:alpine
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
ENV PORT 3000
CMD ["node_modules/.bin/next", "start"]
p.s. 我咋这么想打人呢🤨
编写 Dockerfile 时,最关键的是要考虑缓存机制。每次根据 Dockerfile 构建 Docker 镜像时,Docker 都会保存构建过程中产生的缓存。当你再次构建镜像时,如果缓存可用,可以显著加快构建速度。例如,执行 npm install 命令可能需要几分钟时间来下载和安装 Node.js 项目的所有依赖。因此,你希望在执行 docker build 命令时能够利用这个缓存,以便在下一次构建时能够迅速从缓存中提取,而不是每次都要等待几分钟,这样既烦人又低效。因此我们将打包构建过程拆分为以下三个步骤:
- 拉取依赖项
- 打包构建
- 配置生产环境
1. 拉取项目依赖
在 docker 构建流程中,为了构建过程的优雅,通常我们会手动指定工作目录,由于拉取项目所需依赖,我们仅需要版本控制文件 package.json pnpm-lock.yaml 拷贝到我们工作目录下
由于项目使用 pnpm 进行依赖管理,而在 node:alpine 的最小镜像中,并未提供 pnpm 包管理工具,因此首先全局安装一下 pnpm,再拉取项目所需依赖
FROM node:alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm --registry=http://registry.npmmirror.com
RUN pnpm install --frozen-lockfile --registry=http://registry.npmmirror.com
由于国内网络问题,npm 与 pnpm 我们采用阿里云镜像进行加速
2. 打包构建
打包构建及 NextJS 提供的打包能力,如果需要 nginx 进行部署可能需要 NextJS 的静态导出 | Next.js 中文网能力
在这一步中,我们将上一步中 docker 构建缓存中所拉取到的依赖拷贝到当前镜像的工作目录下,作为构建流程的依赖
# Rebuild the source code only when needed
FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build
由于 node:alpine 并没有提供 pnpm 包管理工具,因此我们使用了 npm 进行打包,其实本质上 npm 与 pnpm 打包构建流程并没有太大区别
3. 配置生产环境
在这一步中,首先我们对生产环境的用户、用户组以及用户权限进行分配
FROM node:alpine AS runner:创建一个名为runner的阶段,基于同样的 Node.js 映像。WORKDIR /app:将工作目录设置到/app。ENV NODE_ENV production:将环境变量NODE_ENV设置为'production',使 Next.js 在生产模式下运行。RUN addgroup -g 1001 -S nodejs:添加一个名为nodejs的组。RUN adduser -S nextjs -u 1001:添加一个名为nextjs的用户,并将其加入到nodejs组中。
FROM node:alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
- 我们继续使用刚刚设置好的用户进行 NextJS 应用的启动
- 首先,我们先将打包构建好的文件拷贝到当前镜像的工作目录下
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
如果有依赖到 next.config.js 的部分,需要拷贝,如果不需要可以选择忽略
- 其次,我们指定 docker 容器的用户、NextJS 运行端口与容器对外暴露的端口
USER nextjs
EXPOSE 3000
ENV PORT 3000
- 最后的最后啦,我们启动 NextJS 应用就好啦
CMD ["node_modules/.bin/next", "start"]
好啦,最后让我们来看一下我们完整版的 dockerfile 吧🤯
# Install dependecies only when needed
FROM node:alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm --registry=http://registry.npmmirror.com
RUN pnpm install --frozen-lockfile --registry=http://registry.npmmirror.com
# Rebuild the source code only when needed
FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build
# Production image, copy all the files and run next
FROM node:alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# You only need to copy next.config.js if you are NOT using the default configuration
# COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
USER nextjs
EXPOSE 3000
ENV PORT 3000
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry.
# ENV NEXT_TELEMETRY_DISABLED 1
CMD ["node_modules/.bin/next", "start"]