ADR 0011 — Ambiente de Staging para el storefront¶
Estado: aceptada — desplegado y verificado en vivo Fecha: 2026-09-14
Contexto¶
Hasta este punto, todo cambio de frontend se probaba directamente
contra shop.cruzcloud.net (producción): el mismo push a main que
disparaba el build de imagen también actualizaba el manifiesto real
que sirve tráfico. Cada iteración de UI (rediseño de la página de
producto, zoom estilo Amazon, ajustes mobile) implicaba validar en
vivo sobre el sitio real, sin una red de contención — un cambio roto
llegaba directo al usuario final antes de poder revertirlo.
El usuario pidió explícitamente un ambiente de Staging para dejar de probar en producción ("Me interesa crear el ambiente Staging para no estar probando en Prod").
Opciones consideradas¶
| Opción | A favor | En contra |
|---|---|---|
| Namespace + Ingress propios, backend compartido con Prod — elegida | Cero costo adicional de Postgres/Medusa/Meilisearch en un NAS ya ajustado de recursos; rápido de armar reusando el mismo patrón workload+governance de ecommerce |
Staging y Prod comparten datos reales (catálogo, inventario, órdenes) — no aísla pruebas que muten datos de negocio |
| Backend propio para Staging (Postgres/Medusa/Meilisearch duplicados) | Aislamiento completo, se podrían probar migraciones de datos sin riesgo | Duplica el consumo de recursos de la NAS sin necesidad todavía (decisión explícita del usuario de descartar esto por ahora) |
| Feature flags / preview deploys por PR en vez de un ambiente fijo | Sin infraestructura nueva que mantener | Gitea Actions en este lab no tiene un mecanismo de preview-per-branch ya armado; más esfuerzo de tooling que un namespace nuevo |
Decisión¶
Namespace ecommerce-staging nuevo, con su propio Ingress
(staging.cruzcloud.net) y su propio Deployment de frontend (misma
imagen Docker que Prod), pero apuntando al mismo backend Medusa que
producción (medusa-svc.ecommerce.svc.cluster.local:9000 — FQDN
cross-namespace, un nombre corto solo resuelve dentro del mismo
namespace). Flujo de promoción: todo push a main despliega a
Staging automáticamente; promover a Producción es una acción manual
separada (scripts/promote-to-production.sh).
Qué se armó¶
apps-registry/workloads/ecommerce-staging/— overlay Kustomize: 1 réplica (no 2 como Prod — ambiente de verificación, no de tráfico real), requests/limits más chicos, mismosecurityContextque Prod (mismas restricciones del Dockerfile:runAsUsernumérico,NET_BIND_SERVICE,readOnlyRootFilesystem).apps-registry/apps/ecommerce-staging-app.yaml+ecommerce-staging-governance-app.yaml— Applications de Argo CD (workload + governance separadas, misma convención que el resto del lab).platform-infra/ecommerce-staging-governance/— ResourceQuota, LimitRange y NetworkPolicy propios del namespace nuevo..gitea/workflows/build.yaml— el paso de CD (Actualizar Manifiesto GitOps) ahora apunta aworkloads/ecommerce-staging/frontend/patches/deployment.yamlen vez del manifiesto de Prod: cada push amainbumpea el tag de imagen (y, desde el mismo día, la variableAPP_VERSION— ver nota de implementación) de Staging automáticamente.scripts/promote-to-production.sh— lee el tag actualmente desplegado en Staging, pide confirmación explícita, y lo aplica al manifiesto real de Producción (workloads/ecommerce/frontend/patches/deployment.yaml). Nunca corre automáticamente: es la única vía para que un cambio llegue ashop.cruzcloud.net.- Secrets
commerce-storefrontygitea-registry-secretcopiados (no regenerados) desde el namespaceecommercehaciaecommerce-staging, para que el frontend de Staging tenga las mismas credenciales reales contra el backend compartido.
Descubrimiento de red real (no solo Kubernetes)¶
Confirmado en vivo el mismo día: aunque Cloudflare tiene un wildcard
*.cruzcloud.net, eso no alcanza para exponer un Ingress nuevo. La
cadena real es Cloudflare → túnel cloudflared → Nginx Proxy
Manager → k3d-lab-cluster-serverlb (puerto 90) → Traefik → Service.
Cada subdominio que ya funciona (shop, commerce, media, etc.)
tiene su propia fila de Proxy Host en NPM apuntando a
192.168.68.61:90. Un Ingress nuevo sin esa fila devuelve la página
por defecto de NPM aunque Traefik ya esté enrutando bien internamente
— verificado con curl -H "Host: staging.cruzcloud.net"
http://192.168.68.61:90/ (funcionaba) contra el dominio público
(seguía devolviendo la página por defecto hasta que se agregó la fila
en NPM). Ver Red y exposición.
Esta fila de NPM la agregó el usuario manualmente — no hay credenciales
de ese panel disponibles para automatizarlo.
Nota de implementación (2026-09-14): versión real visible en el footer¶
El footer mostraba un string fijo (v4.1), inútil para distinguir a
simple vista si Staging y Producción están en la misma versión.
Se agregó una variable de entorno APP_VERSION (leída en
footer.tsx vía process.env.APP_VERSION, sin necesidad de prefijo
NEXT_PUBLIC_ por ser un Server Component) a ambos manifiestos de
Deployment; el mismo paso del CD que bumpea el tag de imagen ahora
también bumpea APP_VERSION (vía perl -0777 para una sustitución
multilínea, ya que el nombre y el valor de la variable viven en líneas
distintas del YAML — un sed de una sola línea no alcanza).
promote-to-production.sh hace el mismo bump al promover. Verificado
en vivo con Playwright: tras el primer push, Staging mostró v1.0.263
mientras Producción seguía en v4.1 (la imagen vieja no leía la
variable); tras promover, ambos ambientes quedaron visualmente
distinguibles por versión real en su footer.
Consecuencias¶
- Ganado: ningún cambio de frontend vuelve a validarse por primera vez contra tráfico real; Staging y Producción ahora se distinguen a simple vista por la versión mostrada en el footer.
- Deuda aceptada: Staging comparte datos reales de negocio con Producción (mismo Medusa/Postgres) — una prueba que cree o modifique órdenes/inventario en Staging afecta datos reales. Si esto se vuelve un problema, la opción descartada de backend propio para Staging queda como camino de escalamiento.
- Para revertir: apuntar de nuevo
MANIFEST_FILE/KUSTOMIZE_DIRenbuild.yamlhaciaworkloads/ecommerce/...y eliminar el namespaceecommerce-staging— el namespace de Producción no cambia de forma en ningún momento de este ADR.