feat: add initial multi-environment CI/CD pipeline POC #1

Merged
nietzshn merged 20 commits from dev into staging 2026-06-02 22:04:02 -06:00
2 changed files with 38 additions and 32 deletions
Showing only changes of commit d4c3affa2f - Show all commits
+37 -32
View File
@@ -2,7 +2,30 @@
Prueba de concepto de un pipeline CI/CD multi-ambiente con Gitea Actions, Docker y Nginx. Prueba de concepto de un pipeline CI/CD multi-ambiente con Gitea Actions, Docker y Nginx.
## Arquitectura ## 📌 Resumen de la Arquitectura
Implementación de una estrategia de Integración y Despliegue Continuo (CI/CD) para múltiples entornos (QA, Staging, Producción) alojada en un único servidor VPS (Debian/Ubuntu). La arquitectura utiliza **Gitea** (como control de versiones, Registry de contenedores y Runner de acciones), **Docker** para la contenerización de la aplicación estática, y un servidor **Nginx** en el host (Host Nginx) que actúa como Reverse Proxy y terminador SSL.
## 🏗️ Estrategia de Infraestructura y Red
Para aislar los entornos en el mismo servidor sin conflictos de red, se implementó el siguiente flujo:
* **Puertos Internos Dockerizados:** Cada entorno corre en un contenedor Docker independiente, exponiendo un puerto único a nivel de `localhost` en el VPS (ej. QA en `8081`, Staging en `8082`, Prod en `8083`).
* **Reverse Proxy:** El servidor Nginx principal del host escucha el puerto `80` y `443` (públicos). Mediante bloques `server_name`, intercepta las peticiones a subdominios específicos (`practicas.qa.kubistudio.cloud`, `practicas.staging.kubistudio.cloud`, `practicas.prod.kubistudio.cloud`) y hace un `proxy_pass` hacia el puerto interno correspondiente.
* **Terminación SSL:** La seguridad HTTPS se maneja exclusivamente en el Nginx del host utilizando **Certbot (Let's Encrypt)**. Los contenedores de Docker solo manejan tráfico HTTP interno, evitando colisiones en el puerto `443`.
## ⚙️ Inyección Dinámica de Variables (Frontend Estático)
Para cumplir con la premisa de *Build Once, Deploy Anywhere* en una aplicación estática (HTML/JS/CSS servida por Nginx), se solucionó el problema de las variables de entorno de la siguiente manera:
1. **Variables en el Pipeline:** El pipeline de Gitea Actions pasa variables de entorno (Commit SHA, Branch, App Env, Build Date) mediante la bandera `-e` en el comando `docker run`.
2. **Entrypoint Interceptor:** Se utiliza un script personalizado `docker-entrypoint.sh` en el contenedor.
3. **Generación Runtime:** Antes de que el Nginx del contenedor arranque, el script lee las variables de entorno de Linux y genera dinámicamente un archivo estático `/usr/share/nginx/html/env-config.js` inyectando el objeto `window.__ENV__`.
4. **Consumo en Cliente:** El archivo `index.html` carga este script de configuración antes de ejecutar la lógica de la aplicación, permitiendo que la interfaz cambie de aspecto y muestre la metadata correcta según el entorno (QA, Staging o Prod).
## 🚀 Flujo de las Pipelines de CD y Ramas
El despliegue está dividido en tres flujos principales basados en ramas:
``` ```
feature/* feature/*
@@ -26,40 +49,22 @@ Prueba de concepto de un pipeline CI/CD multi-ambiente con Gitea Actions, Docker
└───────┘ (aprobación) (puerto 8083) └───────┘ (aprobación) (puerto 8083)
``` ```
## Ambientes ### Detalle por Entorno
| Ambiente | URL | Puerto | Rama | Trigger | * **QA (Entorno de Pruebas):**
|-----------|------------------------------------------|--------|---------|---------------| * **Triggers:** Push a la rama `dev`.
| QA | https://practicas.qa.kubistudio.cloud | 8081 | `dev` | Push autom. | * **Build & Push:** Construye la imagen Docker inyectando argumentos de compilación (`--build-arg` con el Git SHA y Build Date) y la sube al Gitea Container Registry con las etiquetas `qa-latest` y `sha-<hash>`.
| Staging | https://practicas.staging.kubistudio.cloud | 8082 | `staging` | Push autom. | * **Deploy (SSH):** Se conecta por SSH al VPS, detiene y elimina el contenedor anterior (`cicd-qa`), levanta el nuevo en el puerto `8081` con `APP_ENV=qa` y valida el despliegue con un health check.
| Production| https://practicas.prod.kubistudio.cloud | 8083 | `main` | Push + aprob. |
## Configuración en Gitea * **Staging (Entorno de Pre-producción):**
* **Triggers:** Push a la rama `staging`.
* **Build & Push:** Genera la imagen etiquetada para staging (`staging-latest`).
* **Deploy (SSH):** Detiene el contenedor anterior (`cicd-staging`), levanta el nuevo en el puerto `8082` con `APP_ENV=staging` y realiza smoke tests comprobando la variable de entorno expuesta en el de salud.
### Secretos (Settings > Secrets) * **Production (Entorno de Producción):**
* **Triggers:** Push a la rama `main`.
| Secreto | Descripción | * **Build & Push:** Genera la imagen inmutable de producción (`production-latest`, `stable` y `sha-<hash>`).
|--------------------|---------------------------------------------------| * **Deploy (SSH):** Guarda la imagen actual para rollback, levanta el nuevo contenedor en el puerto `8083` con `APP_ENV=production`. Si el health check falla, realiza un **rollback automático** restaurando la imagen anterior.
| `TOKEN` | Token de Gitea con permisos de escritura al registry |
| `DEPLOY_SSH_KEY` | Clave SSH privada para acceder al servidor de deploy |
| `DEPLOY_USERNAME` | Usuario SSH para conexión al servidor |
| `DEPLOY_HOST` | Host/IP del servidor de deploy |
### Variables (Settings > Variables)
| Variable | Descripción | Ejemplo |
|------------------|------------------------------------------------------|------------------------------------------------|
| `REGISTRY_URL` | URL del Gitea Container Registry | `git.kubistudio.cloud` |
| `IMAGE_NAME` | Nombre de la imagen en el registry | `kubistudio/cicd-multi-env-pipeline-poc` |
## Flujo de Trabajo
1. Crear rama `feature/*` desde `dev`
2. Desarrollar y hacer commit
3. Abrir PR a `dev` → CI ejecuta lint, build, security scan
4. Merge a `dev` → CI + Deploy automático a QA
5. PR de `dev``staging` → Deploy automático a Staging
6. PR de `staging``main` → Requiere aprobación → Deploy a Producción
## Ejecución Local ## Ejecución Local
+1
View File
@@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kubistudio - Multi-Environment Pipeline POC</title> <title>Kubistudio - Multi-Environment Pipeline POC</title>
<script src="/env-config.js"></script>
<style> <style>
:root { :root {
--accent: #F59E0B; --accent: #F59E0B;