docs(docs-portal): Fase 2 - contenido narrativo real de arquitectura/decisiones/aprendizajes
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.
This commit is contained in:
1 parent
b7443a034f
commit
056a7fb76a
7 files changed
+490
-80
No files matched your search
@@ -1,33 +1,67 @@
|
||||
# ADR 0001 — Por qué k3d (y no k3s directo, minikube, o un cluster cloud)
|
||||
|
||||
> **TODO (fase 2+):** completar contexto/opciones/consecuencias con
|
||||
> criterio real (recursos del NAS ZimaOS, necesidad de multi-nodo para
|
||||
> reproducir el blackhole de MTU documentado en `known-issues.md`,
|
||||
> costo cero vs. cluster gestionado).
|
||||
|
||||
**Estado:** aceptada (placeholder — pendiente de redactar)
|
||||
**Fecha:** TODO
|
||||
**Estado:** aceptada
|
||||
**Fecha:** 2026-08 (retroactiva — decisión tomada al arrancar el lab, documentada en Fase 2 del portal)
|
||||
|
||||
## Contexto
|
||||
|
||||
TODO
|
||||
El lab corre sobre un NAS ZimaOS doméstico, sin presupuesto para un
|
||||
cluster gestionado y con recursos modestos y compartidos con otras
|
||||
apps del NAS (Gitea, Nginx Proxy Manager, este mismo portal). El
|
||||
propósito del lab es doble: servir de portafolio técnico real y de
|
||||
material de estudio para el hijo del usuario (7mo semestre de
|
||||
Ingeniería de Sistemas) — ver [guía del estudiante](../guia-estudiante/README.md).
|
||||
Para que ambos objetivos tengan sentido, el cluster necesitaba
|
||||
comportarse como un Kubernetes real y multi-nodo, no como una caja de
|
||||
arena de un solo proceso: patrones como afinidad de pods, StatefulSets
|
||||
con almacenamiento persistente por nodo, y tráfico cross-node debían
|
||||
poder reproducirse y depurarse de verdad, no simularse.
|
||||
|
||||
También pesó la restricción del propio sistema operativo: `/` en
|
||||
ZimaOS es un `squashfs` de solo lectura y `/usr` es un overlay
|
||||
igualmente inmutable — instalar un binario nativo como `k3s` que
|
||||
espera escribir en `/usr/local/bin`, `/etc/rancher`, etc. de forma
|
||||
persistente no encaja bien con ese filesystem sin trabajo extra de
|
||||
por medio.
|
||||
|
||||
## Opciones consideradas
|
||||
|
||||
| Opción | A favor | En contra |
|
||||
|---|---|---|
|
||||
| k3d | | |
|
||||
| k3s directo (sin Docker) | | |
|
||||
| minikube | | |
|
||||
| Cluster gestionado (cloud) | | |
|
||||
| **k3d** | Multi-nodo real corriendo como contenedores Docker (server + agent), se crea/destruye en segundos, no toca el filesystem inmutable de ZimaOS más allá de `/DATA` | Al ser Docker-in-Docker, hereda las capas de red de Docker — un blackhole de MTU/PMTUD real apareció por esto (ver Consecuencias) |
|
||||
| k3s directo (sin Docker) | Sin la capa extra de red de Docker-in-Docker, más cercano a un nodo "real" | Necesita instalar servicios systemd y escribir en rutas de sistema (`/usr/local/bin`, `/etc/rancher`) que en ZimaOS son de solo lectura o requieren workarounds — solo `/DATA` y `/etc` quedan escribibles |
|
||||
| minikube | Instalación simple, muy documentado, ideal para aprender lo básico | Single-node por defecto — no permite reproducir tráfico cross-node, afinidad de pods, ni el tipo de incidentes de infraestructura real que este lab busca documentar como aprendizaje |
|
||||
| Cluster gestionado (cloud) | Cero mantenimiento de infraestructura, SLA real | Costo recurrente que este lab no tiene presupuestado; además reduce el valor de aprendizaje — gran parte de lo documentado en `playbooks/` (MTU, DNS, límites de recursos) es justamente lo que un proveedor gestionado abstrae |
|
||||
|
||||
## Decisión
|
||||
|
||||
TODO
|
||||
k3d, multi-nodo (hoy: 1 server + 1 agent, `k3d-lab-cluster-server-0` /
|
||||
`k3d-lab-cluster-agent-0`), corriendo como contenedores Docker sobre
|
||||
el propio NAS.
|
||||
|
||||
## Consecuencias
|
||||
|
||||
TODO — mencionar explícitamente el blackhole de red cross-node
|
||||
(MTU/PMTUD) como consecuencia real y documentada de correr multi-nodo
|
||||
sobre Docker-in-Docker sin ajuste de MTU (ver
|
||||
[playbook del crashloop de Medusa](../playbooks/incidente-crashloop-medusa.md)).
|
||||
- **Se ganó** un cluster real y gratuito, reproducible con un solo
|
||||
script (`scripts/deploy-lab.sh`), y suficientemente parecido a un
|
||||
Kubernetes de producción como para que los incidentes documentados
|
||||
en `playbooks/` sean transferibles a un entorno cloud real.
|
||||
- **Se pagó** con un blackhole de red cross-node real: cualquier pod
|
||||
agendado en un nodo distinto al de `commerce-postgres-0`
|
||||
(StatefulSet, fijo a un nodo) fallaba de forma consistente al hablar
|
||||
con Postgres — diagnosticado hasta el bit exacto (payload interno
|
||||
1420 bytes OK, 1450 bytes blackhole total, sin ICMP "frag needed")
|
||||
y resuelto ajustando el MTU real de la interfaz `flannel.1` a 1400
|
||||
en cada nodo. Ver [known-issues.md](https://gitea.cruzcloud.net/devops/apps-registry/src/branch/main/docs/known-issues.md)
|
||||
y el [playbook del crashloop de Medusa](../playbooks/incidente-crashloop-medusa.md)
|
||||
para el diagnóstico completo. El fix no persiste solo — está
|
||||
automatizado en `scripts/deploy-lab.sh` (función `fix_flannel_mtu`),
|
||||
se reaplica cada vez que el cluster arranca o se recrea.
|
||||
- **Deuda aceptada a propósito**: el `podAffinity` que fuerza a
|
||||
`medusa-deploy` a compartir nodo con Postgres se dejó como red de
|
||||
contención extra después del fix de fondo — no es estrictamente
|
||||
necesario, pero es barato mantenerlo.
|
||||
- **Para revertir esta decisión** haría falta migrar a un cluster
|
||||
gestionado o a `k3s` nativo, lo que implicaría asumir el costo
|
||||
recurrente (cloud) o resolver las restricciones de filesystem
|
||||
inmutable de ZimaOS (k3s nativo) — ninguna de las dos se justifica
|
||||
hoy con el tamaño real del lab.
|
||||
@@ -1,30 +1,80 @@
|
||||
# ADR 0002 — Por qué Argo CD (y no `kubectl apply` manual o un script de CI)
|
||||
|
||||
> **TODO (fase 2+):** completar con el criterio real detrás de elegir
|
||||
> reconciliación continua (`selfHeal`) sobre despliegue imperativo desde
|
||||
> CI.
|
||||
|
||||
**Estado:** aceptada (placeholder — pendiente de redactar)
|
||||
**Fecha:** TODO
|
||||
**Estado:** aceptada
|
||||
**Fecha:** 2026-08 (retroactiva — decisión tomada al arrancar el lab, documentada en Fase 2 del portal)
|
||||
|
||||
## Contexto
|
||||
|
||||
TODO
|
||||
Desde el principio el lab iba a tener más de un repo Git (hoy:
|
||||
`apps-registry` para las Applications y los manifiestos reales de los
|
||||
workloads, `platform-infra` para la gobernanza de cada namespace,
|
||||
`scripts` para bootstrap/operación) y más de una app corriendo en el
|
||||
cluster (ecommerce, docs-portal, nginx, cada una con su propia
|
||||
gobernanza). Se necesitaba una forma de mantener el estado del
|
||||
cluster sincronizado con lo declarado en Git que escalara a "agregar
|
||||
una app nueva" sin volverse un proceso manual que dependiera de que
|
||||
una sola persona recordara qué `kubectl apply` correr y en qué orden.
|
||||
|
||||
También importaba la trazabilidad: en un lab que sirve de portafolio,
|
||||
poder señalar "este commit es exactamente lo que está corriendo en
|
||||
producción" (y por qué) tiene valor por sí mismo — no solo operativo.
|
||||
|
||||
## Opciones consideradas
|
||||
|
||||
| Opción | A favor | En contra |
|
||||
|---|---|---|
|
||||
| Argo CD (GitOps, pull-based) | | |
|
||||
| `kubectl apply` manual | | |
|
||||
| `kubectl apply` desde el pipeline de CI (push-based) | | |
|
||||
| **Argo CD (GitOps, pull-based)** | Reconciliación continua: un proceso dentro del cluster compara el estado real contra Git y corrige la diferencia solo, sin que nadie tenga que acordarse de aplicar nada. Patrón App of Apps nativo — agregar una app nueva es un archivo YAML más en `apps/`, no un paso manual nuevo | Componente extra que mantener sano (su propio namespace, su propio consumo de recursos) |
|
||||
| `kubectl apply` manual | Cero infraestructura extra, control total en cada comando | Sin detección de drift — si alguien edita algo a mano y lo olvida, nadie se entera hasta que rompe; no escala más allá de un solo operador que recuerde el estado completo de memoria |
|
||||
| `kubectl apply` desde el pipeline de CI (push-based) | No agrega un componente nuevo al cluster, reutiliza el CI que ya existe para construir imágenes | El pipeline de CI necesitaría credenciales de escritura amplias sobre el cluster — más superficie de riesgo si esas credenciales se filtran. Push-based además significa que nada vuelve a comparar el estado después de aplicado: un drift posterior (alguien con `kubectl` directo) queda invisible hasta el siguiente deploy |
|
||||
|
||||
## Decisión
|
||||
|
||||
TODO
|
||||
Argo CD, patrón App of Apps: una Application raíz
|
||||
(`apps-registry/application.yaml`) vigila la carpeta `apps/`, y cada
|
||||
archivo ahí dentro es una Application hija que apunta a un workload
|
||||
real. Toda Application se declara con el mismo `syncPolicy`:
|
||||
|
||||
```yaml
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
```
|
||||
|
||||
(configuración real de `apps/ecommerce-app.yaml`, repetida en cada
|
||||
Application del repo)
|
||||
|
||||
## Consecuencias
|
||||
|
||||
TODO — mencionar el patrón App of Apps (`application.yaml` +
|
||||
`apps/*.yaml`) y cómo `selfHeal:true` cambia el modelo mental de
|
||||
"quién puede tocar el cluster".
|
||||
- **`selfHeal: true` cambia el modelo mental de quién puede tocar el
|
||||
cluster de verdad.** No es solo que Argo CD detecte diferencias y
|
||||
las muestre como `OutOfSync` — las revierte activamente. Si alguien
|
||||
corre `kubectl scale` o `kubectl edit` directo contra un recurso
|
||||
gestionado, en el siguiente ciclo de reconciliación (poll periódico,
|
||||
o inmediato si el commit llegó por webhook) Argo CD lo vuelve a
|
||||
dejar exactamente como dice Git. Git deja de ser "la intención" y
|
||||
pasa a ser literalmente la única fuente de la que el cluster puede
|
||||
aprender su propio estado — ver [flujo GitOps](../arquitectura/gitops-flujo.md).
|
||||
- **`prune: true`** significa que borrar un recurso del repo (por
|
||||
ejemplo, quitar `meilisearch.yaml` de una `kustomization.yaml`) lo
|
||||
borra también del cluster en el siguiente sync, sin un paso manual
|
||||
de limpieza — confirmado en la práctica en varias fases de este lab
|
||||
(ver los ADRs de rediseño del storefront y de Meilisearch).
|
||||
- **El rol de CI se redujo a "construir, publicar imagen, y dejar un
|
||||
commit en Git"** — nunca aplica nada directo al cluster. Esto evitó
|
||||
tener que darle a CI credenciales de cluster, y dejó un problema real
|
||||
documentado (la condición de carrera de promoción cuando dos
|
||||
pipelines corren en serie sobre el mismo commit) resoluble
|
||||
comparando contra Git (`git merge-base --is-ancestor`) en vez de
|
||||
contra el estado del cluster.
|
||||
- **Se paga con un componente más que mantener sano**: Argo CD corre
|
||||
en su propio namespace, con su propio consumo de recursos y su
|
||||
propia superficie de fallo — un Argo CD caído no rompe lo que ya
|
||||
está corriendo (el cluster sigue con el último estado aplicado),
|
||||
pero sí detiene la sincronización de cambios nuevos.
|
||||
- **Para revertir esta decisión** haría falta volver a un modelo
|
||||
push-based (CI aplicando directo) o puramente manual, perdiendo la
|
||||
reconciliación continua y aceptando que un drift manual pueda pasar
|
||||
desapercibido.
|
||||
Reference in new issue
Block a user