feat(docs-portal): pipeline GitOps y manifiestos K8s para el portal MkDocs
Agrega lo que faltaba para desplegar workloads/docs-portal/ (ya existente sin commitear): deployment/service/ingress + kustomization siguiendo el patrón de workloads/nginx, Applications de workload y gobernanza separadas, y el workflow de Gitea Actions (build+push a Gitea Registry, bump de versión en el manifiesto) siguiendo el mismo patrón que build.yaml/build-medusa.yaml.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# Incidente: imágenes de producto en blanco (proxy host de `media.cruzcloud.net` mal configurado en NPM) (2026-08)
|
||||
|
||||
!!! info "Origen"
|
||||
Migrado desde `apps-registry/docs/playbooks/incidente-medusa-imagenes-proxy-npm-2026-08.md`.
|
||||
Contenido técnico preservado sin cambios de fondo — solo formato adaptado a MkDocs Material.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Ventana del incidente** | 2026-08-13, resuelto el mismo día |
|
||||
| **Servicio afectado** | `media.cruzcloud.net` (Nginx Proxy Manager → MinIO, `minio-svc` en namespace `ecommerce`) |
|
||||
| **Impacto** | Las imágenes subidas a productos desde el Admin de Medusa no se visualizaban en el detalle de producto del storefront (`shop.cruzcloud.net`) — el resto del producto (título, precio, subtítulo, botón añadir al carrito) se renderizaba bien |
|
||||
|
||||
## Síntoma
|
||||
|
||||
Al agregar imágenes a un producto desde el Admin de Medusa (sección
|
||||
Media), estas se veían correctamente cargadas en el Admin, pero en la
|
||||
página de detalle de producto del frontend los thumbnails aparecían
|
||||
vacíos/en blanco.
|
||||
|
||||
## Diagnóstico
|
||||
|
||||
1. **Storage:** confirmado que Medusa usa el provider
|
||||
`@medusajs/medusa/file-s3` apuntando a MinIO interno
|
||||
(`S3_ENDPOINT=http://minio-svc:9000`, bucket `ari-products`), con
|
||||
URLs públicas construidas sobre
|
||||
`S3_FILE_URL=https://media.cruzcloud.net/ari-products` (env en
|
||||
configmap `commerce-config`, namespace `ecommerce`).
|
||||
2. **API:** el producto traía URLs absolutas válidas, ej.
|
||||
`https://media.cruzcloud.net/ari-products/<archivo>.jpg`.
|
||||
3. **Prueba directa de la URL:**
|
||||
- Contra el ingress interno del cluster (bypaseando el proxy
|
||||
externo, `Host: media.cruzcloud.net` directo a la IP del
|
||||
`k3d-proxy`) → `200 OK`, MinIO sirve el JPEG correctamente.
|
||||
- Contra `https://media.cruzcloud.net/...` (la ruta real que usa el
|
||||
navegador del cliente, vía Cloudflare) → `500 Internal Server
|
||||
Error`, `Server: openresty`.
|
||||
|
||||
!!! tip "Patrón de diagnóstico clave"
|
||||
Ese contraste (funciona interno, falla público, con
|
||||
`Server: openresty` — el motor de Nginx Proxy Manager) aisló el
|
||||
problema al **proxy externo**, no a Medusa/MinIO/frontend.
|
||||
|
||||
## Causa raíz
|
||||
|
||||
En Nginx Proxy Manager (`/DATA/AppData/nginxproxymanager/`, contenedor
|
||||
`nginxproxymanager`), el proxy host de `media.cruzcloud.net`
|
||||
(`id=13` en la tabla `proxy_host` de `database.sqlite`) estaba mal
|
||||
configurado:
|
||||
|
||||
```text
|
||||
forward_host = "http://192.168.68.61/" -- esquema y barra final metidos dentro del campo (formato inválido)
|
||||
forward_port = 5005 -- puerto donde no escucha ningún proceso en el host
|
||||
```
|
||||
|
||||
Los proxy hosts que sí funcionan (`shop.cruzcloud.net` id 23,
|
||||
`commerce.cruzcloud.net` id 28) apuntan correctamente a:
|
||||
|
||||
```text
|
||||
forward_host = "192.168.68.61"
|
||||
forward_port = 90 -- puerto publicado por k3d-lab-cluster-serverlb hacia Traefik
|
||||
```
|
||||
|
||||
Se confirmó con `ss`/`docker ps` en el host que nada escuchaba en el
|
||||
puerto `5005` — este proxy host nunca estuvo apuntando correctamente
|
||||
al cluster, muy probablemente un typo desde que se creó.
|
||||
|
||||
## Fix aplicado
|
||||
|
||||
1. Backups previos: `database.sqlite.bak-pre-media-fix` y
|
||||
`13.conf.bak-pre-media-fix`.
|
||||
2. Corrección directa en la base SQLite de NPM:
|
||||
```sql
|
||||
UPDATE proxy_host SET forward_host='192.168.68.61', forward_port=90 WHERE id=13;
|
||||
```
|
||||
3. NPM regeneró automáticamente `nginx/proxy_host/13.conf` con los
|
||||
valores correctos (la app Node de NPM detecta el cambio en la base
|
||||
y reescribe el `.conf`; no hace falta editarlo a mano).
|
||||
4. Recarga de nginx dentro del contenedor:
|
||||
```bash
|
||||
docker exec nginxproxymanager nginx -t
|
||||
docker exec nginxproxymanager nginx -s reload
|
||||
```
|
||||
|
||||
Adicionalmente, se seteó el campo `thumbnail` del producto de prueba
|
||||
desde el Admin (estaba en `null`) — Medusa no lo autocompleta a partir
|
||||
del array `images`, y el frontend lo usa como imagen hero en listados/
|
||||
tarjetas de catálogo.
|
||||
|
||||
## Validación
|
||||
|
||||
Confirmado visualmente en
|
||||
`shop.cruzcloud.net/product/cybertron-sentinel-figura-de-coleccion-test-revalidate`
|
||||
que tanto la galería del detalle como el thumbnail en listados/tarjetas
|
||||
se renderizan correctamente. También se validó que ediciones de
|
||||
descripción/título/precio desde el Admin se reflejan automáticamente
|
||||
en el storefront sin necesidad de restart ni intervención manual, vía
|
||||
el subscriber `revalidate-storefront.ts`
|
||||
(`workloads/commerce-backend/src/subscribers/`) que procesa el evento
|
||||
`product.updated` de Medusa.
|
||||
|
||||
## Lecciones aprendidas
|
||||
|
||||
**Aislar interno vs. público antes de sospechar de la app.** Medusa,
|
||||
MinIO y el frontend estaban sanos todo el tiempo — la falla estaba en
|
||||
una capa de infraestructura fuera de los repos de GitOps (Nginx Proxy
|
||||
Manager, gestionado por fuera de `apps-registry`/`platform-infra`, sin
|
||||
versionar). Comparar la misma petición por la ruta interna del cluster
|
||||
contra la ruta pública real fue lo que aisló la causa en minutos, sin
|
||||
necesidad de tocar Medusa ni el frontend.
|
||||
|
||||
Ver [Red y exposición](../arquitectura/red-y-exposicion.md) para la
|
||||
topología completa Cloudflare Tunnel → Nginx Proxy Manager → k3d y el
|
||||
patrón a seguir si otro subdominio (`*.cruzcloud.net`) presenta el
|
||||
mismo síntoma (`500`/`Server: openresty` público pero sano
|
||||
internamente).
|
||||
Reference in New Issue
Block a user