CI/CD

Integración y Entrega Continua

1. Concepto Base

¿Qué es CI?

Continuous Integration (Integración Continua)

La CI es la práctica de integrar cambios de código en un repositorio compartido múltiples veces al día, con validación automática mediante builds y tests.

Objetivos de CI

  • Integrar código frecuentemente para detectar conflictos temprano
  • Ejecutar pruebas automáticas en cada commit
  • Compilar y validar el código de forma automática
  • Generar feedback rápido a los desarrolladores (< 10 minutos)

Ejemplos de uso de CI

  • Ejecutar linters para validar estilo de código
  • Correr tests unitarios y de integración
  • Compilar la aplicación para verificar que no hay errores
  • Análisis de código estático (SonarQube, ESLint)
  • Escaneo de dependencias vulnerables

¿Qué es CD?

Continuous Delivery vs Continuous Deployment

Continuous Delivery

Entrega Continua: El código está siempre en estado desplegable. Los despliegues a producción requieren aprobación manual.

  • El pipeline prepara artefactos listos para producción
  • Se despliega automáticamente a entornos de staging/QA
  • El paso a producción es decisión del negocio

Continuous Deployment

Despliegue Continuo: Todo cambio que pasa las pruebas automáticas se despliega automáticamente a producción sin intervención humana.

  • Mayor nivel de automatización
  • Requiere alta confianza en las pruebas
  • Feedback inmediato del usuario final

Ejemplos de uso de CD

  • Despliegue automático a ambientes de staging
  • Generación de artefactos (builds finales, imágenes Docker)
  • Despliegue a producción con aprobación o automático

¿Qué problemas resuelven CI/CD?

  • Integración manual propensa a errores → Automatización
  • Detección tardía de bugs → Feedback rápido
  • Despliegues lentos y riesgosos → Releases frecuentes y seguras
  • Inconsistencias entre entornos → Pipelines reproducibles
  • Falta de visibilidad → Trazabilidad completa

2. Pipeline CI Básico en GitLab

Estructura de Pipeline CI

Un pipeline CI típico incluye:

  1. Build automático - Compilación del código
  2. Lint - Validación de estilo y calidad de código
  3. Tests - Pruebas unitarias e integración
  4. Validación de MRs - Revisión de Merge Requests

Ejemplo: Pipeline CI en GitLab

stages:
  - lint
  - test
  - build

lint-code:
  stage: lint
  image: node:18
  script:
    - npm ci
    - npm run lint
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

unit-tests:
  stage: test
  image: node:18
  script:
    - npm ci
    - npm run test:unit
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

Build Automático

build-app:
  stage: build
  image: node:18
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Nota: Los artefactos se pasan entre stages [1]

Validación de PRs/MRs

validate-merge-request:
  stage: test
  script:
    - echo "Validando Merge Request..."
    - npm run test
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  only:
    - merge_requests

Se ejecuta solo cuando se crea o actualiza un MR

3. Pipeline CD Básico

Estructura de Pipeline CD

  1. Generar artefactos (build final)
  2. Deploy automático a entornos de prueba (staging, S3)
  3. Deploy manual o automático a Producción

Generar Artefactos

build-production:
  stage: build
  image: node:18
  script:
    - npm ci
    - NODE_ENV=production npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 7 days
  only:
    - main

Los artefactos son el resultado compilado listo para desplegar

Deploy Automático a S3

deploy-s3-staging:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  dependencies:
    - build-production
  script:
    - aws s3 sync ./dist s3://$S3_BUCKET_STAGING/ --delete
    - echo "Deploy to staging successful"
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - main

Variables de entorno: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET_STAGING [1][2]

Deploy Manual a Producción

deploy-production:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  dependencies:
    - build-production
  script:
    - aws s3 sync ./dist s3://$S3_BUCKET_PROD/ --delete
    - echo "Deploy to production successful"
  environment:
    name: production
    url: https://www.example.com
  when: manual
  only:
    - main

when: manual requiere aprobación humana

4. Fundamentos de Seguridad

Gestión de Secrets

Nunca guardes secretos en código

Mejores Prácticas

  • Usar CI/CD Variables de GitLab (Settings → CI/CD → Variables)
  • Marcar variables como Protected y Masked
  • Utilizar Secret Managers externos (Vault, AWS Secrets Manager)
  • Usar OIDC para autenticación sin credenciales de larga duración

Ejemplo: Variables en GitLab

deploy-s3:
  script:
    - aws s3 cp ./dist s3://$S3_BUCKET/ --recursive
  variables:
    AWS_DEFAULT_REGION: us-east-1

Secretos configurados en GitLab UI:

  • AWS_ACCESS_KEY_ID (masked, protected)
  • AWS_SECRET_ACCESS_KEY (masked, protected)
  • S3_BUCKET (puede ser pública)

5. Estrategias de Despliegue

Deploy Estándar (In-Place)

Reemplazar la versión actual directamente

  • Se detiene la aplicación
  • Se instala la nueva versión
  • Se reinicia la aplicación

Ventajas: Simple, bajo costo
Desventajas: Downtime, rollback más lento

Blue-Green Deployment (Conceptual)

Dos entornos idénticos: Blue (actual) y Green (nuevo)

  1. El tráfico va a Blue (versión actual)
  2. Se despliega nueva versión en Green
  3. Se prueba Green sin afectar usuarios
  4. Se cambia el tráfico de Blue a Green
  5. Blue queda como respaldo para rollback instantáneo

Blue-Green: Ventajas

  • Zero downtime
  • Rollback instantáneo (volver a Blue)
  • Testing completo antes de switch
  • Aislamiento entre versiones

Blue-Green: Desventajas

  • Doble infraestructura (costo)
  • Complejidad en bases de datos sincronizadas
  • 100% de usuarios afectados si hay problema

Canary Deployment (Conceptual)

Despliegue gradual a un subconjunto de usuarios

  1. Desplegar nueva versión a 5% de usuarios
  2. Monitorear métricas y errores
  3. Si todo bien, aumentar a 25%, luego 50%, etc.
  4. Finalmente 100% de tráfico a nueva versión

Canary: Ventajas

  • Menor riesgo (exposición limitada)
  • Feedback temprano de usuarios reales
  • Control granular del rollout
  • Rollback parcial si hay problemas

Canary: Desventajas

  • Mayor complejidad técnica
  • Requiere routing inteligente de tráfico
  • Monitoreo sofisticado necesario
  • Más tiempo para despliegue completo

6. Observabilidad Mínima

Los Tres Pilares de Observabilidad

  1. Logs - Registro de eventos y errores
  2. Metrics - Métricas de rendimiento (CPU, RAM, latencia)
  3. Traces - Seguimiento de requests a través del sistema

Logs

Registrar eventos clave del despliegue

deploy:
  script:
    - echo "Starting deployment at $(date)"
    - aws s3 sync ./dist s3://$S3_BUCKET/
    - echo "Deployment completed successfully"
  after_script:
    - echo "Cleaning up resources..."

Logs centralizados en GitLab CI/CD Jobs

Health Checks Post-Deploy

Verificar que la aplicación funciona después del despliegue

deploy-production:
  stage: deploy
  script:
    - aws s3 sync ./dist s3://$S3_BUCKET_PROD/
    - echo "Waiting for deployment to propagate..."
    - sleep 30
    - |
      STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://www.example.com/health)
      if [ $STATUS -ne 200 ]; then
        echo "Health check failed with status $STATUS"
        exit 1
      fi
    - echo "Health check passed!"

Health Check API

Tu aplicación debe exponer un endpoint:

GET /health

Respuesta:

{
  "status": "healthy",
  "timestamp": "2025-11-13T21:00:00Z",
  "version": "1.2.3"
}

Observabilidad: Lo Mínimo

  1. Logs estructurados en cada stage
  2. Health checks después de deploy
  3. Alertas básicas por email/Slack si falla
  4. Backup automático antes de deploy
  5. Procedimiento documentado de rollback manual

🚀 PRÁCTICO

Pipeline para Microfrontend Single-SPA

Objetivo

Crear un pipeline en GitLab que:

  1. Ejecute lint y test
  2. Compile el microfrontend single-spa
  3. Suba los archivos a S3
  4. Solo se ejecute en branch main

Estructura del Proyecto

my-single-spa-app/
├── src/
├── dist/            # Build output
├── package.json
├── webpack.config.js
└── .gitlab-ci.yml

package.json Scripts

{
  "scripts": {
    "lint": "eslint src --ext .js,.jsx",
    "test": "jest",
    "build": "webpack --mode production",
    "build:local": "webpack serve --mode development"
  },
  "devDependencies": {
    "webpack": "^5.88.0",
    "webpack-cli": "^5.1.0",
    "single-spa": "^5.9.5",
    "eslint": "^8.45.0",
    "jest": "^29.6.0"
  }
}

.gitlab-ci.yml (Parte 1)

stages:
  - lint
  - test
  - build
  - deploy

variables:
  AWS_DEFAULT_REGION: us-east-1
  NODE_VERSION: "18"

# Template para jobs de Node.js
.node-template: &node-template
  image: node:${NODE_VERSION}
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
  before_script:
    - npm ci

.gitlab-ci.yml (Parte 2)

lint:
  <<: *node-template
  stage: lint
  script:
    - npm run lint
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

test:
  <<: *node-template
  stage: test
  script:
    - npm run test -- --coverage
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

.gitlab-ci.yml (Parte 3)

build:
  <<: *node-template
  stage: build
  script:
    - NODE_ENV=production npm run build
    - ls -lah dist/
  artifacts:
    paths:
      - dist/
    expire_in: 1 day
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

.gitlab-ci.yml (Parte 4)

deploy-s3:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  dependencies:
    - build
  script:
    - echo "Deploying to S3 bucket $S3_BUCKET..."
    - aws s3 sync ./dist s3://$S3_BUCKET/ --delete --acl public-read
    - echo "Deployment completed successfully!"
    - echo "URL: https://$S3_BUCKET.s3.amazonaws.com/index.html"
  environment:
    name: production
    url: https://$S3_BUCKET.s3.amazonaws.com
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Variables de Entorno en GitLab

Settings → CI/CD → Variables

Key Value Protected Masked
AWS_ACCESS_KEY_ID AKIA...
AWS_SECRET_ACCESS_KEY wJalr...
S3_BUCKET my-single-spa-app

Configuración S3 Bucket

  1. Crear bucket en AWS S3
  2. Bucket Policy:
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PublicReadGetObject",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-single-spa-app/*"
  }]
}

Webpack Config para Single-SPA

const { merge } = require('webpack-merge');
const singleSpaDefaults = require('webpack-config-single-spa-react');

module.exports = (webpackConfigEnv, argv) => {
  const defaultConfig = singleSpaDefaults({
    orgName: 'myorg',
    projectName: 'my-app',
    webpackConfigEnv,
    argv,
  });

  return merge(defaultConfig, {
    output: {
      publicPath: process.env.NODE_ENV === 'production'
        ? 'https://my-single-spa-app.s3.amazonaws.com/'
        : 'http://localhost:8080/',
    },
  });
};

Flujo Completo

  1. Desarrollador hace commit a branch feature/new-component
  2. Crea Merge Request a main
  3. Pipeline NO corre (solo en main)
  4. MR aprobado y merged a main
  5. Pipeline se ejecuta:
    • ✅ Lint
    • ✅ Test
    • ✅ Build
    • ✅ Deploy a S3
  6. Aplicación actualizada en producción

Mejoras Opcionales

  • Agregar health check después de deploy
  • Notificaciones a Slack cuando deploy exitoso
  • Dependency scanning para vulnerabilidades
  • Deploy a staging antes de producción
  • Rollback automático si health check falla

Health Check Post-Deploy (Opcional)

deploy-s3:
  # ... código anterior ...
  after_script:
    - |
      echo "Running health check..."
      sleep 15
      STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$S3_BUCKET.s3.amazonaws.com/index.html)
      if [ $STATUS -ne 200 ]; then
        echo "❌ Health check failed with status $STATUS"
        exit 1
      fi
      echo "✅ Health check passed!"

Notificaciones a Slack (Opcional)

notify-slack:
  stage: .post
  image: curlimages/curl:latest
  script:
    - |
      curl -X POST $SLACK_WEBHOOK_URL \
        -H 'Content-Type: application/json' \
        -d "{\"text\":\"✅ Deploy exitoso: $CI_COMMIT_MESSAGE\"}"
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: on_success

Variable: SLACK_WEBHOOK_URL (masked)

Resumen

Lo que Aprendimos

CI/CD: Conceptos de integración y entrega continua
Pipeline CI: Lint, test, build
Pipeline CD: Artefactos, deploy a S3, producción
Seguridad: Secrets, dependency scanning
Estrategias: In-place, Blue-Green, Canary
Observabilidad: Logs, health checks, rollback
Práctica: Pipeline completo para single-spa

Recursos Adicionales

¡Gracias!

¿Preguntas?


CI/CD

By anlijudavid

CI/CD

  • 36