# CI/CD Multi-Environment Pipeline POC Prueba de concepto de un pipeline CI/CD multi-ambiente con Gitea Actions, Docker y Nginx. ## Arquitectura ``` feature/* | PR | ┌───┴───┐ │ dev │ ──(push)──► CI Pipeline ──► Deploy QA └───┬───┘ (lint, build, (puerto 8081) │ security scan) PR docker registry │ │ ┌───┴───┐ │ │staging│ ──(push)────────┼──► Deploy Staging └───┬───┘ │ (puerto 8082) │ │ PR │ │ │ ┌───┴───┐ │ │ main │ ──(push)────────┴──► Deploy Production └───────┘ (aprobación) (puerto 8083) ``` ## Ambientes | Ambiente | URL | Puerto | Rama | Trigger | |-----------|------------------------------------------|--------|---------|---------------| | QA | https://practicas.qa.kubistudio.cloud | 8081 | `dev` | Push autom. | | Staging | https://practicas.staging.kubistudio.cloud | 8082 | `staging` | Push autom. | | Production| https://practicas.prod.kubistudio.cloud | 8083 | `main` | Push + aprob. | ## Configuración en Gitea ### Secretos (Settings > Secrets) | Secreto | Descripción | |--------------------|---------------------------------------------------| | `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 ### Con docker-compose ```yaml version: '3.8' services: cicd-qa: build: . ports: - "8081:80" environment: APP_ENV: qa APP_VERSION: dev-local GIT_COMMIT: local GIT_BRANCH: dev BUILD_DATE: ${BUILD_DATE:-unknown} DEPLOY_TIME: ${DEPLOY_TIME:-unknown} BUILD_NUMBER: "0" cicd-staging: build: . ports: - "8082:80" environment: APP_ENV: staging APP_VERSION: staging-local GIT_COMMIT: local GIT_BRANCH: staging BUILD_DATE: ${BUILD_DATE:-unknown} DEPLOY_TIME: ${DEPLOY_TIME:-unknown} BUILD_NUMBER: "0" cicd-prod: build: . ports: - "8083:80" environment: APP_ENV: production APP_VERSION: 1.0.0 DEPLOY_TIME: ${DEPLOY_TIME:-unknown} ``` ### Manual ```bash # Build docker build \ --build-arg APP_VERSION=1.0.0 \ --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg GIT_COMMIT=$(git rev-parse --short HEAD) \ --build-arg GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) \ -t cicd-poc:latest . # Run docker run -d \ --name cicd-qa \ -p 8081:80 \ -e APP_ENV=qa \ -e APP_VERSION=dev-local \ -e GIT_COMMIT=$(git rev-parse --short HEAD) \ -e GIT_BRANCH=dev \ -e BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ -e DEPLOY_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ -e BUILD_NUMBER="1" \ cicd-poc:latest # Test curl http://localhost:8081/health ``` ## Variables de Entorno del Contenedor | Variable | Obligatorio | Default | Descripción | |-----------------|-------------|---------------|---------------------------------------| | `APP_ENV` | No | `development` | Ambiente: qa, staging, production | | `APP_VERSION` | No | `0.0.0` | Versión de la aplicación | | `BUILD_DATE` | No | *(vacío)* | Fecha ISO del build | | `GIT_COMMIT` | No | *(vacío)* | SHA corto del commit | | `GIT_BRANCH` | No | *(vacío)* | Rama de git | | `DEPLOY_TIME` | No | *(vacío)* | Timestamp del deploy | | `BUILD_NUMBER` | No | *(vacío)* | Número de build de la pipeline | ## Rollback Manual ### Production ```bash ssh user@deploy-host # Rollback a stable tag docker stop cicd-prod && docker rm cicd-prod docker run -d \ --name cicd-prod \ --restart unless-stopped \ -p 8083:80 \ -e APP_ENV=production \ -e APP_VERSION=rollback \ -e DEPLOY_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ registry-url/image-name:stable # O con un SHA específico docker run -d \ --name cicd-prod \ --restart unless-stopped \ -p 8083:80 \ -e APP_ENV=production \ -e APP_VERSION=rollback \ -e DEPLOY_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ registry-url/image-name:sha-SHA_DEL_COMMIT ``` ### QA / Staging ```bash # QA docker stop cicd-qa && docker rm cicd-qa docker run -d --name cicd-qa -p 8081:80 ... registry-url/image-name:sha-SHA_ANTERIOR # Staging docker stop cicd-staging && docker rm cicd-staging docker run -d --name cicd-staging -p 8082:80 ... registry-url/image-name:sha-SHA_ANTERIOR ``` ## Endpoints | Endpoint | Descripción | |-----------|--------------------------------------| | `/` | Aplicación web | | `/health` | Health check (JSON) | ### Ejemplo `/health` ```json { "status": "ok", "env": "production", "version": "release-a1b2c3d", "timestamp": "2024-01-15T14:23:00+00:00" } ``` ## Estructura del Proyecto ``` . ├── src/ │ └── index.html # App web (UI completa self-contained) ├── .gitea/workflows/ │ ├── ci.yml # CI: lint + build + security scan │ ├── deploy-qa.yml # Deploy a QA (push a dev) │ ├── deploy-staging.yml # Deploy a Staging (push a staging) │ └── deploy-prod.yml # Deploy a Producción (push a main + aprobación) ├── docker-entrypoint.sh # Entrypoint que genera env-config.js en runtime ├── Dockerfile # Multi-stage build ├── healthcheck.sh # Script de health check ├── nginx.conf # Configuración de nginx └── README.md # Este archivo ```