apiVersion: apps/v1 kind: Deployment metadata: name: app-deployment labels: app: frontend spec: replicas: 2 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: # runAsUser explícito -- ver comentario largo más abajo, junto al # container, sobre por qué runAsNonRoot solo no alcanza acá. securityContext: runAsUser: 1001 containers: - name: app image: gitea.cruzcloud.net/devops/ecommerce-frontend:v1.0.178 env: - name: HEADLESS_PROVIDER value: medusa - name: MEDUSA_BACKEND_URL value: http://medusa-svc:9000 - name: MEDUSA_REGION_ID value: reg_01KZT86WPAH7A7ZZRDSX3350V2 envFrom: - secretRef: name: commerce-storefront # El base usa "/" por default. El Dockerfile de frontend ya # define su propio HEALTHCHECK contra /api/health -- usamos # el mismo endpoint acá en vez del genérico del base, ya # sabemos que existe y responde. readinessProbe: httpGet: path: /api/health port: http livenessProbe: httpGet: path: /api/health port: http # El Dockerfile de frontend ya corre como usuario "nextjs" no-root # (a diferencia de docs-portal) -- pero runAsNonRoot: true del # base SOLO no alcanza: probado en vivo, falló con "container # has runAsNonRoot and image has non-numeric user (nextjs), # cannot verify user is non-root". El Dockerfile define el # usuario por NOMBRE (useradd ... nextjs), y sin un runAsUser # numérico Kubernetes no puede confirmar que no sea root sin # arrancar el contenedor -- y por eso rechaza arrancarlo. El # UID real es 1001 (useradd --uid 1001 ... nextjs en el # Dockerfile). # # Sí hace falta NET_BIND_SERVICE: el puerto real es 80 # (privilegiado, <1024). El Dockerfile ya hace setcap sobre el # binario de node para esto, pero eso solo funciona si el # bounding set del contenedor lo permite -- drop: ALL del base # lo bloquea igual, root o no root. Mismo motivo que docs-portal. # # readOnlyRootFilesystem: true -- revisado en código (2026-08-24, # aún no probado en vivo con este cambio) que esta imagen sí # escribe a disco en runtime, no solo en build: el servidor # standalone de Next.js cachea a ./.next/cache tanto el # optimizador de imágenes (next.config.ts trae remotePatterns, # sin "unoptimized: true") como el revalidateTag("medusa-products") # de app/api/revalidate/route.ts (cache handler por default, sin # uno custom tipo Redis). De ahí los dos volumeMounts de abajo: # sin ellos, con root filesystem de solo lectura, ambas rutas # fallarían al intentar escribir. securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL add: - NET_BIND_SERVICE resources: requests: cpu: 100m memory: 256Mi limits: cpu: "1" memory: 768Mi volumeMounts: - name: next-cache mountPath: /app/.next/cache - name: tmp mountPath: /tmp volumes: - name: next-cache emptyDir: {} - name: tmp emptyDir: {}