# Basado en el Dockerfile real de ecommerce-frontend (Next.js, output # "standalone"). Tu next.config.* necesita "output: 'standalone'" para # que este Dockerfile funcione -- sin eso, .next/standalone no existe # y el build de la imagen falla en el "test -f" del stage builder. # # ARGs a completar por tu app: # NODE_IMAGE -- versión exacta de Node, fijá una concreta. # APP_PORT -- puerto de escucha (Next.js con HOSTNAME/PORT del # runtime standalone). Default 3000; el frontend real # del lab lo corre en 80 vía este mismo ARG. # syntax=docker/dockerfile:1.7 ARG NODE_IMAGE=node:24.18.0-bookworm-slim ARG APP_PORT=3000 # ============================================================ # 1. Dependencias # ============================================================ FROM ${NODE_IMAGE} AS deps WORKDIR /app ENV NODE_ENV=development \ NEXT_TELEMETRY_DISABLED=1 \ NPM_CONFIG_AUDIT=false \ NPM_CONFIG_FUND=false COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm,sharing=private \ npm ci \ --include=prod \ --include=dev \ --no-audit \ --no-fund \ --foreground-scripts # ============================================================ # 2. Compilación Next.js # ============================================================ FROM ${NODE_IMAGE} AS builder WORKDIR /app ENV NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ NODE_OPTIONS=--max-old-space-size=2048 COPY --from=deps /app/node_modules ./node_modules COPY package.json package-lock.json ./ COPY . . RUN --mount=type=cache,target=/app/.next/cache,sharing=private \ npm run build # Falla temprano y con un mensaje claro si next.config.* no tiene # "output: 'standalone'" -- mejor que un COPY silencioso más abajo # fallando con "no existe el archivo". RUN test -f /app/.next/standalone/server.js \ && test -d /app/.next/static \ || { echo "ERROR: falta 'output: standalone' en next.config.*"; exit 1; } # ============================================================ # 3. Runtime de producción # ============================================================ FROM ${NODE_IMAGE} AS runner ARG APP_PORT WORKDIR /app ENV NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ HOSTNAME=0.0.0.0 \ PORT=${APP_PORT} # setcap: solo hace falta si APP_PORT < 1024 (un puerto "privilegiado" # que normalmente requiere root para bindear). Si tu APP_PORT es 3000 # o cualquier valor >= 1024, podés borrar el setcap y libcap2-bin. RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates libcap2-bin \ && rm -rf /var/lib/apt/lists/* \ && groupadd --gid 1001 nodejs \ && useradd --uid 1001 --gid 1001 --create-home --shell /usr/sbin/nologin nextjs \ && setcap 'cap_net_bind_service=+ep' /usr/local/bin/node # El runtime solo ejecuta "node server.js" -- nunca npm/npx/corepack. # Sacarlos reduce lo que Trivy escanea al set real de producción. RUN rm -rf \ /usr/local/lib/node_modules/npm \ /usr/local/lib/node_modules/corepack \ /usr/local/bin/npm \ /usr/local/bin/npx \ /usr/local/bin/corepack COPY --from=builder --chown=nextjs:nodejs /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE ${APP_PORT} # CAMBIAR: /api/health tiene que existir en tu app (una route handler # de Next.js que devuelva 200). Si no la tenés todavía, es la primera # ruta a agregar -- este healthcheck y el readinessProbe/livenessProbe # de k8s-manifests/base/deployment.yaml asumen que existe. HEALTHCHECK --interval=30s --timeout=8s --start-period=30s --retries=3 \ CMD ["node", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/api/health').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"] CMD ["node", "server.js"]