# Visión general
Este lab corre sobre un NAS ZimaOS doméstico, sin IP pública ni
presupuesto de nube — cada capa del diagrama de abajo existe por una
razón concreta de ese contexto, no por moda arquitectónica. Esta
página explica el "por qué" de cada frontera; el "cómo" vive en las
páginas enlazadas al final de cada sección.
## Flujo de una petición pública
```mermaid
flowchart LR
User(["Usuario"]) -->|HTTPS| CF["Cloudflare
(DNS proxied + túnel)"]
CF -->|HTTP/2, red host| CFD["cloudflared
(app ZimaOS)"]
CFD --> NPM["Nginx Proxy Manager
192.168.68.61:90"]
NPM --> LB["k3d-lab-cluster-serverlb
(k3d-proxy, 90→80)"]
LB --> TR["Traefik
(ingress del cluster)"]
TR -->|Host: shop.cruzcloud.net| FE["frontend-svc
(Next.js)"]
TR -->|Host: commerce.cruzcloud.net| MED["medusa-svc
(Medusa API)"]
TR -->|Host: media.cruzcloud.net| MIN["minio-svc
(imágenes de producto)"]
TR -->|Host: gitea.cruzcloud.net| GIT["Gitea"]
TR -->|Host: docs.cruzcloud.net| DOCS["docs-portal-svc
(este sitio)"]
subgraph GitOps["Control plane GitOps"]
ARGO["Argo CD"] -->|sync| FE
ARGO -->|sync| MED
ARGO -->|sync| DOCS
GIT -.->|watch repos| ARGO
end
```
## Componentes
| Capa | Qué es | Dónde vive |
|---|---|---|
| Cloudflare Tunnel | Expone `*.cruzcloud.net` a internet sin abrir puertos en el NAS | Fuera del cluster, app nativa ZimaOS (`cloudflared`) |
| Nginx Proxy Manager | Enruta por dominio hacia el cluster | Fuera del cluster, contenedor `nginxproxymanager` |
| k3d | Cluster Kubernetes local (multi-nodo, Docker) | Host ZimaOS |
| Traefik | Ingress controller del cluster | Dentro de k3d |
| Argo CD | Sincroniza el estado del cluster con Git (patrón App of Apps) | Namespace `argocd` |
| Gitea | Origen de verdad de los manifiestos y del código de las apps | Contenedor propio, expuesto vía túnel |
## Por qué cada frontera está donde está
**Cloudflare Tunnel, no port-forwarding.** El NAS vive detrás de NAT
residencial, sin IP pública fija. En vez de abrir puertos en el router
(superficie de ataque directa contra el hogar), `cloudflared` abre una
conexión **saliente** hacia Cloudflare y todo el tráfico público viaja
hacia adentro por esa misma conexión — el origen nunca expone un
puerto escuchando en internet. El transporte de esa conexión se fijó a
HTTP/2 en vez del QUIC por defecto tras un incidente real de
inestabilidad; ver [ADR 0003](../decisiones/0003-por-que-http2-sobre-quic-tunnel.md).
**Nginx Proxy Manager, fuera del cluster.** No todo lo que sirve
`*.cruzcloud.net` vive dentro de k3d — Gitea, por ejemplo, corre como
contenedor Docker aparte. NPM es el único punto de enrutamiento por
dominio que necesita conocer *ambos* mundos (cluster y no-cluster), así
que vive fuera de Kubernetes a propósito, no por descuido.
**`k3d-lab-cluster-serverlb`, la frontera de entrada al cluster.** Es
el load balancer que el propio k3d monta automáticamente para publicar
puertos del host hacia los nodos internos — no es un componente que
este lab configuró a mano, viene con la topología multi-nodo elegida
en [ADR 0001](../decisiones/0001-por-que-k3d.md).
**Traefik, enrutamiento por `Host` dentro del cluster.** Una vez el
tráfico entra al cluster por un único puerto (90/9443), Traefik es
quien decide a qué Service mandarlo según el header `Host` de cada
petición — así NPM solo necesita saber "todo `*.cruzcloud.net` va al
puerto 90 del cluster", sin tener que aprender un forward_host nuevo
cada vez que se agrega un servicio interno. Cuando esta capa falla en
silencio (`forward_host`/`forward_port` mal apuntado en NPM), el
síntoma es siempre el mismo: "internamente sano, públicamente roto" —
ver [Red y exposición](red-y-exposicion.md).
**Argo CD, el único escritor del cluster.** Ningún pipeline de CI ni
ninguna persona corre `kubectl apply` contra producción — Argo CD es
la única pieza con permisos de escritura, y reconstruye
automáticamente cualquier diferencia entre Git y el cluster real
(`selfHeal: true`). El porqué completo de esa elección está en
[ADR 0002](../decisiones/0002-por-que-argocd-vs-manual.md); el paso a
paso de un commit real hasta quedar corriendo, en
[Flujo GitOps](gitops-flujo.md).
**Gitea, autoalojado.** Además de alojar el código, Gitea corre el CI
(Gitea Actions) y el registry de imágenes en el mismo lugar — un solo
componente autoalojado en vez de depender de la disponibilidad y los
límites de un servicio externo gratuito para un lab que ya de por sí
no tiene presupuesto de nube.
Ver también: [Red y exposición](red-y-exposicion.md) · [Flujo GitOps](gitops-flujo.md) · [Glosario](glosario.md)