Saltar a contenido

1. Fuente única de verdad

Principio

La infraestructura y la configuración de SmallCountry se definen como código:
se escriben en code-server, se versionan en Forgejo, se planifican con OpenTofu, se despliegan mediante Forgejo Actions y se aplican declarativamente con Ansible usando roles idempotentes.
Cualquier cambio que no recorra este flujo completo es una divergencia que detiene el sistema y exige reconciliación humana.

Ciclo de vida de un cambio

  1. **Escritura en code-server
    Desde el entorno de desarrollo accesible por navegador se editan simultáneamente los módulos de OpenTofu, los roles de Ansible, la documentación y los prompts de IA. Es el único punto de entrada para cualquier modificación del sistema.

  2. **Versionado en Forgejo
    Todo el código se aloja en el repositorio central, que actúa como fuente de verdad, registro OCI de imágenes y motor de CI/CD (integración y despliegue continuo).
    El flujo de trabajo es: feature-branch → Pull Request → revisión humana → merge a main.

  3. **Planificación con OpenTofu
    Cada Pull Request ejecuta tofu plan. El resultado se compara con el estado real de Proxmox VE a través de su API (interfaz de programación).
    Si existe deriva —cambios ejecutados fuera de Git— el flujo se detiene, se genera un commit forense en una rama quarantine y se notifica al administrador. Este mecanismo garantiza que la deriva nunca quede sin auditar.

  4. **Aplicación declarativa con Ansible
    Tras la creación o actualización de los LXC (contenedor ligero de Proxmox) por OpenTofu, Ansible entra en juego aplicando roles declarativos. Cada rol describe el estado final deseado (paquetes instalados, ficheros de configuración, contenedores Docker activos, certificados TLS (cifrado de comunicaciones) presentes) sin preocuparse del estado actual.
    La idempotencia está verificada: ejecutar el mismo rol cien veces produce exactamente el mismo resultado seguro que ejecutarlo una sola vez.

  5. Consolidación
    Al finalizar el despliegue, el estado real del sistema coincide exactamente con el declarado en Forgejo. Cualquier intervención manual futura que se salte este flujo será detectada como deriva y forzará una reconciliación documentada.

    flowchart TD
        subgraph Escritura [Escritura]
            direction LR
            CodeServer[code-server] --> Modulos[Módulos OpenTofu / Roles Ansible / Documentación]
        end

        subgraph Versionado [Versionado]
            direction LR
            ForgejoNode[Forgejo] --> PR[Pull Request]
            PR --> Revision[Revisión humana]
            Revision --> Merge[Merge a main]
        end

        subgraph Planificacion [Planificación]
            direction LR
            Actions[Forgejo Actions] --> Plan[tofu plan]
            Plan --> Drift[Detección de drift]
            Drift --> Decision{¿Divergencia?}
            Decision -->|Sí| Forense[Commit forense + notificación]
            Decision -->|No| Apply[tofu apply]
        end

        subgraph Aplicacion [Aplicación declarativa]
            direction LR
            OpenTofu[OpenTofu] --> LXCs[Creación / actualización de LXCs]
            LXCs --> Semaphore[Ansible Semaphore]
            Semaphore --> Roles[Roles declarativos idempotentes]
        end

        subgraph Consolidacion [Consolidación]
            direction LR
            Estado[Estado real = Estado declarado] --> Snapshot[ZFS snapshot]
        end

        Escritura --> Versionado
        Versionado --> Planificacion
        Planificacion --> Aplicacion
        Aplicacion --> Consolidacion

        style Escritura fill:#f9a8d4,stroke:#c2185b,color:#000
        style Versionado fill:#f48fb1,stroke:#c2185b,color:#000
        style Planificacion fill:#90caf9,stroke:#1565c0,color:#000
        style Aplicacion fill:#a5d6a7,stroke:#2e7d32,color:#000
        style Consolidacion fill:#ffe082,stroke:#f57f17,color:#000
    ```
## Ansible como motor declarativo

En <img src="/assets/logo-accent.svg" style="height:1.2em;vertical-align:text-bottom;" alt=""> **SmallCountry**, [**Ansible**](../../fichas/programas/ansible.md) no se usa como un simple ejecutor de comandos, sino como **garante del estado deseado**. Cada servicio tiene su propio rol, estructurado según las mejores prácticas:

| Archivo / Carpeta | Propósito |
|---|---|
| `roles/authentik/tasks/main.yml` | Tareas declarativas: instalar paquetes, crear volúmenes, desplegar docker-compose |
| `roles/authentik/handlers/main.yml` | Acciones reactivas: reiniciar el servicio si cambia la configuración |
| `roles/authentik/templates/` | Plantillas de configuración (authentik.conf.j2, docker-compose.yml.j2) |
| `roles/authentik/vars/main.yml` | Variables específicas del rol (versión de la imagen, puertos, secretos referenciados desde Vault) |
| `roles/authentik/meta/main.yml` | Dependencias: requiere que el rol `docker` esté aplicado primero |
### Propiedades clave

| Propiedad                           | Descripción                                                                                                                                                                                                                            |
| :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Declaratividad estricta**         | Las tareas describen el estado final deseado, no los pasos para alcanzarlo.                                                                                                                                                            |
| **Idempotencia real**               | Se emplean condiciones como `creates`, `when` o `changed_when` para que [**Ansible**](../../fichas/programas/ansible.md) decida si debe actuar o si el estado ya es correcto, evitando reinicios innecesarios.                                                              |
| **Roles atómicos y desacoplados**   | El rol `authentik` no sabe en qué red ni en qué LXC se ejecuta; esa información se la proporciona [**OpenTofu**](../../fichas/programas/opentofu.md). Así se puede reutilizar el mismo rol en distintos entornos sin modificarlo.                                            |
| **Secretos como código**            | Las variables sensibles (contraseñas, tokens OIDC (protocolo de autenticación OpenID Connect), claves API) se generan, se cifran con [**Ansible Vault**](../../fichas/programas/ansible.md) y se versionan en [**Forgejo**](../../fichas/stacks/forgejo.md). En el momento del despliegue se inyectan sin que nunca existan en texto plano en producción. |
| **Separación de responsabilidades** | [**OpenTofu**](../../fichas/programas/opentofu.md) crea y dimensiona el LXC (recursos, red, almacenamiento); [**Ansible**](../../fichas/programas/ansible.md) lo provisiona (Docker, configuración, certificados). No se mezclan responsabilidades.                                                               |

## Stack necesario

- [**code-server**](../../fichas/programas/codeserver.md) – Entorno de desarrollo desde navegador.
- [**Forgejo**](../../fichas/stacks/forgejo.md) – Repositorio Git, CI/CD y registro OCI.
- [**OpenTofu**](../../fichas/programas/opentofu.md) – Infraestructura como código (módulos declarativos).
- [**Forgejo Actions**](../../fichas/stacks/forgejo.md) – Pipelines de planificación y despliegue.
- [**Ansible**](../../fichas/programas/ansible.md) + [**Semaphore**](../../fichas/programas/semaphore.md) – Configuración declarativa con roles.
- [**Ansible Vault**](../../fichas/programas/ansible.md) – Cifrado de secretos.
- **Script de detección de drift** – Compara plan de [**OpenTofu**](../../fichas/programas/opentofu.md) con estado real de [**Proxmox VE**](../../fichas/programas/proxmox.md).
- [**ZFS**](../../fichas/programas/zfs.md) – Snapshots pre‑cambio para rollback instantáneo.

## Relaciones con otros principios

- [2. Reconstruibilidad total desde cero](02-reconstruibilidad.md): el repositorio central contiene todos los módulos y roles necesarios para regenerar el sistema desde un Proxmox limpio.
- [8. Observabilidad integral desde el origen](08-observabilidad.md): la configuración de los agentes de observabilidad se despliega con roles de [**Ansible**](../../fichas/programas/ansible.md) versionados, asegurando que cada servicio nazca instrumentado.
- [11. Contención de recursos y degradación planificada](11-contencion.md): los límites de RAM/CPU de cada LXC están definidos en código [**OpenTofu**](../../fichas/programas/opentofu.md) y versionados aquí.
- [12. Conocimiento compartido y autonomía de las personas usuarias](12-conocimiento.md): la documentación (`docs.sc`) se escribe en [**code-server**](../../fichas/programas/codeserver.md), se versiona en [**Forgejo**](../../fichas/stacks/forgejo.md) junto al código y se publica con [**MkDocs**](../../fichas/programas/mkdocs.md).

### Flujo de remediación automática

Cuando el sistema detecta una degradación —un servicio caído, un recurso al límite, una métrica fuera de SLO (objetivo de nivel de servicio)— se activa una cadena de corrección que atraviesa todas las capas del ecosistema:

```mermaid
flowchart TD
    SkyEye[SkyEye detecta anomalía] --> n8nRecibe[n8n recibe el evento]
    n8nRecibe --> n8nConsulta[n8n consulta el runbook]
    n8nConsulta --> Decision{¿Acción automática?}
    Decision -->|Sí| Semaphore[Semaphore ejecuta playbook]
    Decision -->|Requiere humano| Notifica[Notifica al administrador]
    Semaphore --> Converge[Ansible converge estado deseado]
    Converge --> Restaurado([Sistema restaurado])
    Notifica --> Espera([Espera intervención humana])
  1. SkyEye detecta — una alerta de Prometheus, un healthcheck fallido de Uptime Kuma, o una anomalía detectada por los agentes nocturnos de IA
  2. n8n interpreta — recibe el evento, consulta el runbook correspondiente y decide la acción (reiniciar servicio, escalar recurso, notificar)
  3. Semaphore ejecuta — lanza el playbook de Ansible apropiado de forma controlada y registrada
  4. Ansible converge — aplica el estado deseado de forma idempotente, devolviendo el sistema a su condición normal

Todo este flujo deja trazabilidad completa en Forgejo. Si en algún punto se requiere intervención humana, el sistema notifica y espera. La automatización acelera, pero nunca sustituye el criterio cuando la situación lo exige.


Mecanismo 10: Perfiles de Energía   |   Principio 2: Reconstruibilidad total


Secciones relacionadas