wip: contenido inicial + test de reusable workflow / composite action
This commit is contained in:
1 parent
e9fe7d0567
commit
262c0f271a
22 files changed
+1480
No files matched your search
@@ -0,0 +1,111 @@
|
||||
# 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"]
|
||||
@@ -0,0 +1,94 @@
|
||||
# Basado en el Dockerfile real de commerce-backend (Medusa v2 / Express).
|
||||
# Multi-stage: build con devDependencies, runtime solo con lo necesario
|
||||
# para correr -- sin compilador, sin devDependencies, sin npm/npx.
|
||||
#
|
||||
# ARGs a completar por tu app (todos tienen un default razonable):
|
||||
# NODE_IMAGE -- versión exacta de Node. Fijala a una versión concreta
|
||||
# (no "node:22-slim"), así un rebuild no te cambia la
|
||||
# versión de Node por sorpresa un día cualquiera.
|
||||
# APP_PORT -- puerto en el que tu app escucha.
|
||||
# START_CMD -- cómo arrancar tu app en runtime. Por default invoca
|
||||
# "node dist/index.js" -- CAMBIALO al entrypoint real de
|
||||
# tu build. No uses "npm start"/"npx algo": el runtime
|
||||
# de esta imagen NO tiene npm (ver más abajo, por qué).
|
||||
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
ARG NODE_IMAGE=node:22.18.0-bookworm-slim
|
||||
ARG APP_PORT=3000
|
||||
|
||||
#############################################################
|
||||
# Builder
|
||||
#############################################################
|
||||
FROM ${NODE_IMAGE} AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV CI=true \
|
||||
NODE_ENV=development \
|
||||
NPM_CONFIG_UPDATE_NOTIFIER=false
|
||||
|
||||
COPY package.json package-lock.json* .npmrc* ./
|
||||
|
||||
# --legacy-peer-deps: sacalo si tu proyecto no lo necesita. Medusa v2 sí
|
||||
# lo necesita hoy (conflictos de peer deps conocidos del ecosistema).
|
||||
RUN --mount=type=cache,id=node-api-npm-cache,target=/root/.npm \
|
||||
npm ci \
|
||||
--legacy-peer-deps \
|
||||
--prefer-offline \
|
||||
--no-audit \
|
||||
--no-fund
|
||||
|
||||
# CAMBIAR: copiá acá los archivos/carpetas fuente reales de tu app
|
||||
# (tsconfig, src/, config específico). Estos son solo un ejemplo.
|
||||
COPY tsconfig.json ./
|
||||
COPY src ./src
|
||||
|
||||
RUN npm run build
|
||||
|
||||
RUN npm prune --omit=dev \
|
||||
&& npm cache clean --force
|
||||
|
||||
#############################################################
|
||||
# Runtime
|
||||
#############################################################
|
||||
FROM ${NODE_IMAGE}
|
||||
|
||||
ARG APP_PORT
|
||||
ENV NODE_ENV=production \
|
||||
PORT=${APP_PORT} \
|
||||
NPM_CONFIG_UPDATE_NOTIFIER=false
|
||||
|
||||
# El CLI global de npm que trae la imagen base node:*-slim arrastra su
|
||||
# propia copia de "tar" con historial de CVEs — no hace falta en
|
||||
# runtime (nunca corrés npm/npx acá, solo "node algo.js"). Borrarlo
|
||||
# reduce lo que Trivy tiene que escanear al set real que corre en
|
||||
# producción.
|
||||
#
|
||||
# OJO si tu app usa un init container que corre "npx <algo>" contra
|
||||
# esta misma imagen (ej. migraciones): eso se rompe si borrás npm acá.
|
||||
# Cambiá ese comando a invocar el binario directo con "node", como el
|
||||
# CMD de abajo — no vuelvas a agregar npm solo para eso.
|
||||
RUN rm -rf /usr/local/lib/node_modules/npm
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# CAMBIAR: qué carpetas copiar del builder depende de tu build real
|
||||
# (dist/, .output/, node_modules). Este ejemplo asume un build típico
|
||||
# de tsc/esbuild a dist/ + node_modules de producción.
|
||||
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
|
||||
COPY --from=builder --chown=node:node /app/dist ./dist
|
||||
|
||||
# La imagen base node:*-bookworm-slim ya trae un usuario "node" (uid
|
||||
# 1000) sin privilegios -- no hace falta crear uno nuevo, a diferencia
|
||||
# de nextjs.Dockerfile.
|
||||
USER node
|
||||
|
||||
EXPOSE ${APP_PORT}
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=8s --start-period=20s --retries=3 \
|
||||
CMD ["node", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/health').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
|
||||
|
||||
# CAMBIAR: el entrypoint real. Nunca "npm start"/"npx algo" -- ver nota
|
||||
# de arriba sobre por qué npm no existe en esta imagen.
|
||||
CMD ["node", "dist/index.js"]
|
||||
@@ -0,0 +1,66 @@
|
||||
# Basado en el Dockerfile real de docs-portal (sitio MkDocs Material).
|
||||
# Mejora respecto al original: nginx corre como usuario NO-root acá
|
||||
# (nginxinc/nginx-unprivileged) -- el docs-portal real del lab todavía
|
||||
# corre nginx:1.27-alpine estándar (como root), un gap real encontrado
|
||||
# al armar este template, pendiente de aplicar al repo real por
|
||||
# separado.
|
||||
#
|
||||
# Este template asume MkDocs -- es el único generador de sitio estático
|
||||
# que existe hoy en el lab. Si tu app usa otro generador (Hugo, Astro,
|
||||
# un build de Vite sin backend), el STAGE 1 es lo único que cambia:
|
||||
# reemplazalo por el build real de tu herramienta, el STAGE 2
|
||||
# (nginx no-root) sirve igual para cualquier sitio 100% estático.
|
||||
#
|
||||
# ARGs:
|
||||
# PYTHON_IMAGE -- versión del builder (solo aplica al ejemplo MkDocs).
|
||||
# NGINX_IMAGE -- versión de nginx no-root.
|
||||
# APP_PORT -- OJO: esto solo cambia el EXPOSE/healthcheck de este
|
||||
# Dockerfile, NO el puerto real en el que nginx
|
||||
# escucha -- eso lo define el nginx.conf que trae la
|
||||
# propia imagen nginx-unprivileged, fijo en 8080. Si
|
||||
# cambiás este ARG a otro valor, tenés que agregar un
|
||||
# nginx.conf propio (COPY) que escuche en ese mismo
|
||||
# puerto, o quedan desalineados. Dejalo en 8080
|
||||
# mientras no necesites tocar nginx.conf.
|
||||
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
ARG PYTHON_IMAGE=python:3.12-slim
|
||||
ARG NGINX_IMAGE=nginxinc/nginx-unprivileged:1.27-alpine
|
||||
ARG APP_PORT=8080
|
||||
|
||||
# --- Stage 1: build del sitio estático (ejemplo: MkDocs Material) ---
|
||||
FROM ${PYTHON_IMAGE} AS build
|
||||
|
||||
WORKDIR /site
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY mkdocs.yml .
|
||||
COPY docs/ docs/
|
||||
|
||||
# --strict: cualquier link roto o warning de nav rompe el build, para
|
||||
# no publicar nunca un sitio con referencias muertas. Si tu generador
|
||||
# no tiene un equivalente a --strict, considerá agregar un paso de
|
||||
# validación de links aparte (ej. linkinator) antes de este build.
|
||||
RUN mkdocs build --strict
|
||||
|
||||
# --- Stage 2: sirve el sitio estático con nginx, sin root ---
|
||||
FROM ${NGINX_IMAGE}
|
||||
|
||||
ARG APP_PORT
|
||||
# ARG no alcanza para el HEALTHCHECK -- ese CMD corre en runtime, no en
|
||||
# build time, y ahí un ARG ya no existe. Hace falta ENV.
|
||||
ENV APP_PORT=${APP_PORT}
|
||||
|
||||
# CAMBIAR "/site/site" al directorio real de salida de tu build si no
|
||||
# usás MkDocs (ej. "dist/", "public/", "build/", ".output/public/").
|
||||
COPY --from=build /site/site /usr/share/nginx/html
|
||||
|
||||
EXPOSE ${APP_PORT}
|
||||
|
||||
# nginx-unprivileged trae wget (busybox) -- alcanza para un healthcheck
|
||||
# simple sin instalar nada más.
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget -q -O /dev/null "http://127.0.0.1:${APP_PORT}/" || exit 1
|
||||
Reference in new issue
Block a user