Reemplaza los placeholders "TODO (fase 2+)" por narrativa completa, grounded en el codigo/manifiestos/incidentes reales del lab (no generico): - ADR 0001 (por que k3d) y ADR 0002 (por que Argo CD): pasan de template vacio a Contexto/Opciones/Decision/Consecuencias reales, apoyados en known-issues.md (blackhole MTU) y en la config real de ecommerce-app.yaml. - vision-general.md: el "por que" de cada frontera de confianza del diagrama. - gitops-flujo.md: ejemplo real con hashes de commit (Fase 3b de ADR 0006, v1.0.171 -> hotfix -> v1.0.172), selfHeal explicado con el syncPolicy real del repo. - red-y-exposicion.md: diagrama de terminacion TLS real, por que todo va "proxied", patron de diagnostico "interno sano, publico roto". - glosario.md: 9 terminos nuevos, cada uno con ejemplo real y verificado del repo (Namespace, ResourceQuota, LimitRange, NetworkPolicy, Deployment, Service, Ingress, StatefulSet, Init container). - notas-sueltas.md: MinIO/S3, limites reales del subscriber revalidate-storefront.ts, que se automatizaria desde cero. Validado con `docker build --target build` (mkdocs build --strict, mismo paso que el pipeline real) -- cero links rotos, cero warnings.
3.7 KiB
Red y exposición
Los 5 dominios de abajo son reales y comparten exactamente el mismo camino de red — ninguno tiene un caso especial.
Dominios activos
| Dominio | Servicio | Namespace | TLS |
|---|---|---|---|
shop.cruzcloud.net |
Frontend Next.js (storefront) | ecommerce |
Cloudflare (edge) |
commerce.cruzcloud.net |
Medusa Store/Admin API | ecommerce |
Cloudflare (edge) |
media.cruzcloud.net |
MinIO (imágenes de producto) | ecommerce |
Cloudflare (edge) |
gitea.cruzcloud.net |
Gitea | — | Cloudflare (edge) |
docs.cruzcloud.net |
Este portal | docs |
Cloudflare (edge) |
Por qué todos van "proxied" (naranja), ninguno "DNS-only"
Un registro DNS-only en Cloudflare expondría directamente la IP real del túnel — pero acá no hay IP pública fija que exponer: el NAS está detrás de NAT residencial. El modo proxied es lo que hace que Cloudflare Tunnel tenga sentido en primer lugar (el tráfico entra por el edge de Cloudflare, nunca directo al origen), así que no hay ningún dominio de este lab para el que DNS-only sea una opción real.
Dónde termina TLS de verdad
flowchart LR
User(["Usuario"]) -->|HTTPS| CF["Cloudflare edge<br/>🔒 TLS termina acá"]
CF -->|túnel cifrado, HTTP/2| CFD["cloudflared"]
CFD -->|HTTP plano| NPM["Nginx Proxy Manager"]
NPM -->|HTTP plano| LB["k3d-proxy :90"]
LB -->|HTTP plano| TR["Traefik"]
TR -->|HTTP plano| SVC["Service interno"]
TLS termina en el edge de Cloudflare, no en Traefik ni en NPM. De
ahí hacia adentro (cloudflared → NPM → k3d-proxy → Traefik →
Service) todo el tráfico va en HTTP plano — seguro en este contexto
porque ese tramo entero vive dentro de la red local del NAS, nunca
sale a internet. Ningún componente interno necesita certificado propio
ni gestionar renovación de TLS.
Por qué HTTP/2 y no QUIC en el túnel
cloudflared puede usar QUIC (UDP) o HTTP/2 (TCP) para sus conexiones
salientes hacia el edge. Este lab fuerza HTTP/2 explícitamente — no es
el default. Un incidente real de 504 Gateway Timeout intermitente en
todos los subdominios (no solo uno) se rastreó hasta fallos
recurrentes del "control stream" de QUIC sobre la red doméstica del
NAS. Ver ADR 0003
para la decisión completa y el playbook del 504
para el diagnóstico paso a paso.
Patrón de diagnóstico: "interno sano, público roto"
Cuando un dominio público falla pero todo dentro del cluster responde
bien (pods Running, probes verdes, kubectl exec con curl al
Service funciona), la causa casi nunca está en Kubernetes — está en la
capa NPM. La forma rápida de confirmarlo es saltarse NPM a propósito:
curl -H "Host: <dominio>" http://192.168.68.61:90/ habla directo con
k3d-proxy/Traefik sin pasar por NPM. Si eso funciona pero el dominio
público no, el problema está específicamente en el forward_host/
forward_port del proxy host en NPM (ej.: un scheme o un path colado
en forward_host, que rompe todo el enrutamiento).
Este patrón se repitió dos veces en este lab con la misma causa raíz
exacta: el incidente de imágenes en media.cruzcloud.net
(playbook completo) y un incidente posterior de commerce.cruzcloud.net
devolviendo 400 por el mismo tipo de forward_host roto (sin playbook
propio todavía). Una lección adicional de ese segundo incidente:
editar el proxy_host a mano en el sqlite de NPM no regenera el
.conf de nginx automáticamente — el cambio tiene que pasar por la
propia UI/API de NPM, o el .conf hay que parchearlo a mano y
recargar nginx.