|
1 | | -# syntax=docker.io/docker/dockerfile:1 |
2 | | - |
| 1 | +# 指定基础镜像版本,确保每次构建都是幂等的 |
3 | 2 | FROM node:18-alpine AS base |
4 | 3 |
|
5 | | -# Install dependencies only when needed |
6 | | -FROM base AS deps |
| 4 | +FROM base AS builder |
| 5 | + |
7 | 6 | # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. |
8 | 7 | RUN apk add --no-cache libc6-compat |
| 8 | + |
| 9 | +# Node v16.13 开始支持 corepack 用于管理第三方包管理器 |
| 10 | +# 锁定包管理器版本,确保 CI 每次构建都是幂等的 |
| 11 | +# RUN corepack enable && corepack prepare pnpm@latest --activate |
| 12 | +RUN corepack enable && corepack prepare [email protected] --activate |
| 13 | + |
9 | 14 | WORKDIR /app |
10 | 15 |
|
11 | | -# Install dependencies based on the preferred package manager |
12 | | -COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ |
13 | | -RUN \ |
14 | | - if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ |
15 | | - elif [ -f package-lock.json ]; then npm ci; \ |
16 | | - elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ |
17 | | - else echo "Lockfile not found." && exit 1; \ |
18 | | - fi |
| 16 | +# pnpm fetch does require only lockfile |
| 17 | +# 注意还需要复制 `.npmrc`,因为里面可能包含 npm registry 等配置,下载依赖需要用到 |
| 18 | +COPY pnpm-lock.yaml ./ |
19 | 19 |
|
| 20 | +# 推荐使用 pnpm fetch 命令下载依赖到 virtual store,专为 docker 构建优化 |
| 21 | +# 参考:https://pnpm.io/cli/fetch |
| 22 | +RUN pnpm fetch |
20 | 23 |
|
21 | | -# Rebuild the source code only when needed |
22 | | -FROM base AS builder |
23 | | -WORKDIR /app |
24 | | -COPY --from=deps /app/node_modules ./node_modules |
| 24 | +# 将本地文件复制到构建上下文 |
25 | 25 | COPY . . |
26 | 26 |
|
27 | | -# Next.js collects completely anonymous telemetry data about general usage. |
28 | | -# Learn more here: https://nextjs.org/telemetry |
29 | 27 | # Uncomment the following line in case you want to disable telemetry during the build. |
30 | | -# ENV NEXT_TELEMETRY_DISABLED=1 |
| 28 | +ENV NEXT_TELEMETRY_DISABLED 1 |
31 | 29 |
|
32 | | -RUN \ |
33 | | - if [ -f yarn.lock ]; then yarn run build; \ |
34 | | - elif [ -f package-lock.json ]; then npm run build; \ |
35 | | - elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ |
36 | | - else echo "Lockfile not found." && exit 1; \ |
37 | | - fi |
| 30 | +# 基于 virtual store 生成 node_modules && 打包构建 |
| 31 | +# 此处不需要与 package registry 进行通信,因此依赖安装速度极快 |
| 32 | +# 注意 PNPM v8.4.0 版本有一个 breaking change |
| 33 | +# 当 `node_modules` 存在,运行 `pnpm install` 会出现命令行交互操作,导致 CI 挂掉 |
| 34 | +# 这里加上 `--force` 参数,关闭命令行交互操作 |
| 35 | +RUN pnpm install --offline --force && pnpm build |
38 | 36 |
|
39 | | -# Production image, copy all the files and run next |
40 | 37 | FROM base AS runner |
41 | | -WORKDIR /app |
42 | 38 |
|
43 | | -ENV NODE_ENV=production |
44 | | -# Uncomment the following line in case you want to disable telemetry during runtime. |
45 | | -# ENV NEXT_TELEMETRY_DISABLED=1 |
| 39 | +# RUN apk update && apk add --no-cache git |
| 40 | +RUN apk add --no-cache curl |
| 41 | + |
| 42 | +# 如果需要是用 TZ 环境变量 实现时区控制,需要安装 tzdata 这个包 |
| 43 | +# debian 的基础镜像默认情况下已经安装了 tzdata,而 ubuntu 并没有 |
| 44 | +# RUN apk add --no-cache tzdata |
46 | 45 |
|
| 46 | +ARG RUNTIME_ENV |
| 47 | +ENV RUNTIME_ENV=$RUNTIME_ENV |
| 48 | +ENV NODE_ENV production |
| 49 | + |
| 50 | +# Docker 容器不推荐用 root 身份运行 |
| 51 | +# 这边先建立一个特定的用户和用户组,为它分配必要的权限,使用 USER 切换到这个用户 |
| 52 | +# 注意,如果不是 root 权限,对于可执行文件,需要修改权限,确保文件可以执行 |
47 | 53 | RUN addgroup --system --gid 1001 nodejs |
48 | 54 | RUN adduser --system --uid 1001 nextjs |
49 | 55 |
|
50 | | -COPY --from=builder /app/public ./public |
| 56 | +# 设置时区 |
| 57 | +# 在使用 Docker 容器时,系统默认的时区就是 UTC 时间(0 时区),和我们实际需要的北京时间相差八个小时 |
| 58 | +ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8 TZ=Asia/Shanghai |
| 59 | +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone |
| 60 | + |
| 61 | +WORKDIR /app |
| 62 | + |
| 63 | +# PNPM 有一个全局 store,项目中的 node_modules 实际上是全局 store 的 symlink |
| 64 | +# 正常需要从上一阶段同时复制 `node_modules` 和全局 store,这样才能正常运行 |
| 65 | +# 但是由于 `standalone` 目录里面包含所有运行时依赖,且都是独立目录 |
| 66 | +# 因此可以直接复制该目录,无需复制全局 store(如果复制还会增加镜像体积) |
| 67 | +# 另外运行需要的配置文件、dotfile 也都在 `standalone` 目录里面,无需单独复制 |
51 | 68 |
|
52 | | -# Automatically leverage output traces to reduce image size |
53 | | -# https://nextjs.org/docs/advanced-features/output-file-tracing |
| 69 | +# `standalone` 模式打包,默认包含服务端代码,没有客户端代码 |
| 70 | +# 因为官方建议通过 CDN 托管,但也可以手动复制 `public`、`.next/static` 目录 |
| 71 | +COPY --from=builder /app/public ./public |
54 | 72 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ |
55 | 73 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static |
56 | 74 |
|
| 75 | +# 注意,`standalone` 目录下已经包含了服务端代码,无需再复制 `.next/server` |
| 76 | +# COPY --from=builder /app/.next/server ./.next/server |
| 77 | + |
57 | 78 | USER nextjs |
58 | 79 |
|
59 | | -EXPOSE 3000 |
| 80 | +# Uncomment the following line in case you want to disable telemetry during runtime. |
| 81 | +ENV NEXT_TELEMETRY_DISABLED 1 |
| 82 | +ENV PORT 3000 |
60 | 83 |
|
61 | | -ENV PORT=3000 |
| 84 | +# 默认暴露 80 端口 |
| 85 | +EXPOSE 3000 |
62 | 86 |
|
63 | | -# server.js is created by next build from the standalone output |
64 | | -# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output |
65 | | -ENV HOSTNAME="0.0.0.0" |
| 87 | +# 用 standalone 模式打包后,生成的 `standalone/node_modules` 目录下缺少 `.bin` 目录 |
| 88 | +# 导致无法用 `next` 命令启动项目,但可以用 `node server.js` 启动 |
| 89 | +# 参考:https://nextjs.org/docs/advanced-features/output-file-tracing |
66 | 90 | CMD ["node", "server.js"] |
0 commit comments