# Known issues ## Blackhole de red cross-node en el cluster k3d (MTU/PMTUD, flannel VXLAN) **Estado:** RESUELTO (2026-08-16). Fix definitivo aplicado y validado; el `podAffinity` se deja como red de contención extra, no como requisito. **Síntoma:** cualquier pod que necesite hablar con `commerce-postgres-0` (StatefulSet, sin réplicas, fijo a un nodo) falla de forma consistente y reproducible al 100% cuando queda agendado en un nodo **distinto** al de Postgres. El handshake TCP (SYN/ACK, `nc`, `ping`) funciona perfecto entre nodos, pero el primer paquete de datos real de la sesión (protocolo Postgres, `pg_isready` incluido) nunca llega — se cuelga hasta hacer timeout. **Diagnóstico:** - Confirmado aislando el nodo con `nodeName` en pods de prueba: 100% de fallos (186/186 intentos) agendando cross-node (`agent-0` → Postgres en `server-0`); 100% de éxito agendando en el mismo nodo. - MTU dentro de los pods (interfaz `flannel.1`/VXLAN) es 1450 en ambos nodos — consistente, no es un mismatch obvio a nivel flannel. - `ping` sin `-M do` (permite fragmentación) no muestra pérdida de paquetes hasta payloads de 2000 bytes entre los contenedores Docker de los nodos. - Encaja con un blackhole de PMTU Discovery: el bridge Docker externo que conecta los contenedores de los nodos k3d probablemente tiene un MTU real menor a 1500, y los paquetes TCP con el bit DF puesto (como los de la sesión de Postgres) se pierden en vez de fragmentarse o generar el ICMP "fragmentation needed" que permitiría el ajuste automático. **Workaround temporal (aplicado en `workloads/ecommerce/commerce/medusa.yaml`):** `podAffinity` con `requiredDuringSchedulingIgnoredDuringExecution` sobre `medusa-deploy`, forzando que sus pods se agenden siempre en el mismo nodo que `commerce-postgres-0` (`matchLabels: app: commerce-postgres`, `topologyKey: kubernetes.io/hostname`). Esto evita el tráfico cross-node entre Medusa y Postgres, pero no resuelve el problema de fondo — cualquier otro workload que necesite cruzar nodos para hablar con Postgres (u otro servicio) puede pisar el mismo blackhole. **Fix definitivo aplicado (2026-08-16):** Confirmado empíricamente con un pod de debug (`nicolaka/netshoot`, tiene `ping` con soporte real de bit DF — el `ping` de BusyBox en la imagen de nodo de k3s no lo soporta) haciendo un sweep de payloads con `ping -M do` cross-node hacia el pod de `commerce-postgres-0`: el límite real está entre payload interno **1420 (0% pérdida)** y **1450 (100% pérdida, sin ningún ICMP "frag needed" — blackhole real, no fragmentación normal)**. Coincide casi exactamente con el MTU que la propia interfaz `flannel.1` ya reportaba (1450, calculado como `eth0` 1500 − 50 de overhead VXLAN) — la aritmética de flannel es correcta, pero el último tramo no soporta en la práctica el paquete VXLAN a tamaño completo. `k3s` (que embebe flannel, no lo corre como binario/DaemonSet separado) no expone un flag `--flannel-mtu` en esta versión (confirmado con `k3s server --help`, ver `docs/gitops` o `scripts/deploy-lab.sh`). El único punto de ajuste real es la interfaz `flannel.1` en vivo dentro de cada contenedor de nodo: `docker exec ip link set flannel.1 mtu 1400`. No persiste en ningún archivo — hay que reaplicarlo cada vez que el cluster arranca. Automatizado en `scripts/deploy-lab.sh` (función `fix_flannel_mtu`, llamada tanto en `create_cluster` como en `start_cluster`). Aplicar el nuevo MTU solo afecta pods **nuevos** — los que ya estaban corriendo mantienen el MTU viejo en su `veth` hasta que se recrean. Se hizo `kubectl rollout restart` de todo el namespace `ecommerce` (`commerce-postgres`, `commerce-redis`, `commerce-minio`, `medusa-deploy`, `catalog-api-deploy`, `frontend-deploy`) para que todos tomaran el MTU nuevo de inmediato. **Validación post-fix:** el mismo sweep de `ping -M do` ahora devuelve `Frag needed and DF set (mtu = 1400)` — PMTU Discovery funcionando correctamente, ya no hay blackhole silencioso. Confirmado además con tráfico TCP real cross-node hacia Postgres (conexión exitosa + transferencia de 50 KB sin colgarse, muy por encima del umbral que antes rompía). El `podAffinity` en `workloads/ecommerce/commerce/medusa.yaml` que fija `medusa-deploy` al mismo nodo que `commerce-postgres-0` **se deja tal cual** — ya no es necesario para evitar el blackhole, pero no tiene costo real y sirve como red de contención si el MTU real del entorno vuelve a cambiar (por ejemplo, tras una actualización de ZimaOS/Docker). ## Topología de red: Cloudflare Tunnel → Nginx Proxy Manager → k3d **Estado:** referencia de arquitectura — no es un issue abierto, pero el patrón de falla que describe ya se repitió una vez (ver `docs/playbooks/incidente-medusa-imagenes-proxy-npm-2026-08.md`) y puede volver a aparecer con otro subdominio. **Ruta real de una petición pública a `*.cruzcloud.net`:** ``` Internet → Cloudflare (DNS proxied, túnel) → contenedor `cloudflared` (app nativa de ZimaOS, red Host) → Nginx Proxy Manager (contenedor `nginxproxymanager`, config en /DATA/AppData/nginxproxymanager/data/) → 192.168.68.61:90 → `k3d-lab-cluster-serverlb` (k3d-proxy, publica 90→80 y 9443→443) → Traefik (ingress del cluster) → Service correspondiente, enrutado por el header `Host`. ``` Los proxy hosts de NPM viven en `/DATA/AppData/nginxproxymanager/data/database.sqlite` (tabla `proxy_host`) y NPM regenera solo el `.conf` correspondiente en `data/nginx/proxy_host/.conf` cuando cambia la fila en la base — no hace falta editar el `.conf` a mano, y si se edita a mano puede sobreescribirse en cualquier cambio posterior desde la UI/DB. **Patrón de diagnóstico:** si un subdominio `*.cruzcloud.net` responde `500 Internal Server Error` con header `Server: openresty` cuando se accede públicamente, pero el mismo path funciona bien contra el ingress interno del cluster (`curl -H "Host: " http:///...`), el problema **no está en el cluster ni en la app** — está en el proxy host de NPM para ese subdominio. Revisar `forward_host`/`forward_port` en la tabla `proxy_host` y compararlos contra un host que sí funcione (ej. `shop.cruzcloud.net`, `commerce.cruzcloud.net`, ambos con `forward_host='192.168.68.61'`, `forward_port=90`). ## Nota de arquitectura: Medusa v2 Store API requiere `region_id` explícito **Estado:** no es un issue, es un requisito de la API que causó un incidente real por no ser conocido — ver `docs/playbooks/incidente-medusa-precios-region-id-2026-08.md`. El endpoint `/store/products` de Medusa v2 (validado por `StoreGetProductsParams`, `.strict()`) **no acepta `currency_code` ni `country_code`** como parámetros para resolver el contexto de precio (`calculated_price`). El único parámetro válido para ese fin es `region_id`, apuntando a una región existente y configurada vía Admin API. Cualquier integración nueva contra el Store API de Medusa v2 que necesite precios calculados debe pasar `region_id` explícito — no asumir que `currency_code` o `country_code` son suficientes, aunque lo sean en Medusa v1 o en otras APIs de e-commerce.