# 构建 + 运行一体镜像。 # # 服务契约来源(交叉验证): # - node_modules/next/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/output.md: # output: 'standalone' 产出可独立部署的目录 + 精简 server.js;该 server **默认不含** public # 与 static,需手工拷到 /public 与 /.next/static(本项目 distDir=dist, # 实测为 /dist/static,见下);监听地址/端口由 HOSTNAME / PORT 环境变量决定。 # - 本仓库 dist/ 实测:dist/standalone/server.js、dist/standalone/dist/{BUILD_ID,server,...}、 # dist/static/{chunks,media,}。 # # 与 Dockerfile.prod 的分工(避免第三种变体): # - Dockerfile.prod:直接复用**宿主机已构建**的 dist/standalone,宿主为 darwin, # 因此需要 sharp-deps 阶段补 linux-musl 的 @img 二进制。 # - 本文件:镜像内完成 npm ci + next build(依赖原生装到 linux-musl),无需覆盖 @img。 # # 历史缺陷(ACCEPTANCE_REVIEW_2026-09-21 §5):旧版把 /app/dist 当静态 HTML 根 # `COPY --from=builder /app/dist /usr/share/nginx/html` 交给 nginx,standalone 产物里没有 # 可直服的站点根目录(HTML 在 dist/standalone/dist/server/app/*.html,且需运行时渲染), # 用本镜像部署即白屏/404。 FROM node:20-alpine AS builder WORKDIR /app COPY package.json package-lock.json* ./ RUN npm ci && npm cache clean --force COPY . . ENV NEXT_TELEMETRY_DISABLED=1 # 先删除构建上下文里带进来的宿主产物:.dockerignore 为 Dockerfile.prod 放行了 # dist/standalone 与 dist/static,而 next build 不会清空目标目录,残留的 # dist/standalone/node_modules/@img/*-darwin-* 会被下面的 runner 原样拷进 linux 镜像。 RUN rm -rf "${NEXT_DIST_DIR:-dist}" && npm run build FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=3000 # standalone server 只绑 HOSTNAME,默认 localhost 在容器外不可达 ENV HOSTNAME="0.0.0.0" RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs # Next.js 16 standalone 输出:server.js 位于 dist/standalone 根目录(与 Dockerfile.prod 一致) # 注:以下三个路径按 next.config.mjs 的默认 distDir=dist 硬编码;若 CI 用 NEXT_DIST_DIR 改目录, # 需同步这里的 --from 路径(当前 Jenkinsfile 未设置该变量)。 COPY --from=builder /app/dist/standalone ./ # 客户端静态资源,standalone 内的 distDir 影子目录为 dist/ COPY --from=builder /app/dist/static ./dist/static # 公共静态资源(图片、字体、favicon、uploads) COPY --from=builder /app/public ./public RUN chown -R nextjs:nodejs /app USER nextjs EXPOSE 3000 # 应用层安全响应头由 next.config.mjs 的 headers() 发出,本镜像不再叠加 nginx 层, # 因此容器直连(无 nginx)时头部依然完整。 CMD ["node", "server.js"]