Configuración de Pipelines CI/CD
Configura pipelines CI/CD automatizados para testing, building y deployment de aplicaciones con GitHub Actions y lo que funciona.
Descripción general
Continuous Integration y Continuous Deployment (CI/CD) automatizan el viaje desde el commit de código hasta el deploy en producción. Un pipeline bien configurado ejecuta tests, construye artifacts, escanea vulnerabilidades y despliega a staging o producción sin intervención manual. Esto elimina errores humanos, acelera releases y provee feedback rápido a los desarrolladores.
Cuándo usarla
Usa este recurso cuando:
- configures un nuevo proyecto y quieras testing automatizado desde el día uno. Consulta Unit Testing para fundamentos de testing.
- Migres de deploys manuales a releases automatizados. Consulta GitHub Actions para automatización de workflows.
- Agregues scanning de seguridad, linting o gates de calidad de código a tu workflow. Consulta Container Security Scanning para gates de seguridad en CI.
- Construyas una estrategia de deploy multi-ambiente (dev → staging → prod). Consulta Blue-Green Deployment para releases sin downtime.
Solución
Workflow de GitHub Actions
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run test:ci
- run: npm run build
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm audit --audit-level=moderate
deploy:
needs: [test, security]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: |
npm run build
npm run deploy:staging
Configuración de GitLab CI
stages:
- test
- build
- deploy
variables:
NODE_VERSION: "20"
test:
stage: test
image: node:$NODE_VERSION
script:
- npm ci
- npm run test:ci
deploy_prod:
stage: deploy
script:
- npm run deploy:production
only:
- main
environment:
name: production
url: https://api.example.com
Explicación
Un pipeline de CI/CD de producción típicamente incluye:
- Trigger: Push, pull request o job programado por cron
- Build: Compila, bundlea y crea artifacts
- Test: Tests unitarios, tests de integración, linting, type checking
- Seguridad: Auditoría de dependencias, SAST, escaneo de secretos
- Deploy: Push a staging, smoke tests, promoción a producción
- Notificación: Slack, email o sistema de incident management
Estrategias de deploy:
- Básico: Deploy directo a producción
- Blue-Green: Dos ambientes idénticos; switch de tráfico instantáneo
- Canary: Rutea 1% de tráfico a nueva versión; incrementa gradualmente
- Rolling: Reemplaza instancias una a una con zero downtime
Variantes
| Plataforma | Ideal Para | Notas |
|---|---|---|
| GitHub Actions | Open source, repos GitHub | Gratis para repos públicos; marketplace de actions |
| GitLab CI | Proyectos GitLab-hosted | Built-in; excelente para monorepos |
| CircleCI | Testing paralelo rápido | Excelente soporte de Docker |
| Jenkins | On-premise, plugins custom | Self-hosted; alto mantenimiento |
| ArgoCD | Kubernetes GitOps | Declarativo; sincroniza cluster con estado Git |
Lo que funciona
- Fail fast: Ejecuta linting y tests unitarios rápidos antes de tests de integración costosos
- Paraleliza jobs: Divide tests por archivo o módulo para reducir tiempo wall-clock
- Cachea dependencias: Cachea node_modules, pip cache y capas Docker entre ejecuciones
- Usa secrets management: Nunca commitees API keys; usa secrets de GitHub/GitLab o Vault
- Requiere reviews para prod: Usa branch protection y CODEOWNERS
Errores comunes
- Sin promoción de artifacts: Reconstruir en cada stage introduce no-determinismo
- Testear solo en CI: Los desarrolladores hacen push de código roto y esperan feedback de CI
- Secrets en variables de entorno: Visibles en logs de jobs; usa secrets enmascarados
- Sin plan de rollback: Deploys fallidos necesitan revert instantáneo vía blue-green o imagen anterior
- Ignorar tests flaky: Fallos aleatorios erosionan la confianza en el pipeline
Ejemplos avanzados
Deploy Multi-Entorno con Gates
# .github/workflows/deploy.yml
name: CI/CD Pipeline
on:
push:
branches: [main, staging]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run test:ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm audit --audit-level=moderate
- name: Run SAST
uses: github/codeql-action/init@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
deploy-staging:
needs: [test, security]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/staging'
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- name: Deploy a staging
run: |
aws s3 sync dist/ s3://staging-bucket --delete
aws cloudfront create-invalidation --distribution-id ${{ secrets.STAGING_DIST_ID }} --paths "/*"
deploy-production:
needs: [test, security]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- name: Deploy a producción
run: |
aws s3 sync dist/ s3://prod-bucket --delete
aws cloudfront create-invalidation --distribution-id ${{ secrets.PROD_DIST_ID }} --paths "/*"
Docker Build y Push en CI
# .github/workflows/docker.yml
name: Docker Build and Push
on:
push:
tags: ['v*']
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
Deploy Blue-Green
# .github/workflows/blue-green.yml
deploy:
needs: [test, security]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Determinar entorno activo
id: active
run: |
ACTIVE=$(kubectl get service app -o jsonpath='{.spec.selector.color}')
echo "active=$ACTIVE" >> $GITHUB_OUTPUT
if [ "$ACTIVE" = "blue" ]; then
echo "target=green" >> $GITHUB_OUTPUT
else
echo "target=blue" >> $GITHUB_OUTPUT
fi
- name: Deploy al entorno inactivo
run: |
kubectl set image deployment/app-${{ steps.active.outputs.target }} \
app=ghcr.io/org/app:${{ github.sha }}
kubectl rollout status deployment/app-${{ steps.active.outputs.target }}
- name: Cambiar tráfico
run: |
kubectl patch service app -p \
'{"spec":{"selector":{"color":"${{ steps.active.outputs.target }}"}}}'
- name: Ejecutar smoke tests
run: |
./scripts/smoke-test.sh https://example.com/health
if [ $? -ne 0 ]; then
kubectl patch service app -p \
'{"spec":{"selector":{"color":"${{ steps.active.outputs.active }}"}}}'
exit 1
fi
Deploy Canary con Argo Rollouts
# rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api-server
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: { duration: 5m }
- setWeight: 25
- pause: { duration: 5m }
- setWeight: 50
- pause: { duration: 10m }
- setWeight: 100
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api
image: ghcr.io/org/api:v2.0.0
ports:
- containerPort: 8080
Estrategia de Caching en Pipeline
# .github/workflows/cached-build.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
pip:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- run: pip install -r requirements.txt
- run: pytest
Tips de Rendimiento
- Cachea agresivamente. Cachea dependencias, Docker layers y outputs de build:
- uses: actions/cache@v4
with:
path: |
node_modules
.next/cache
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- Usa jobs condicionales. Salta jobs innecesarios según archivos cambiados:
test:
steps:
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
src:
- 'src/**'
docs:
- 'docs/**'
- if: steps.changes.outputs.src == 'true'
run: npm test
- Usa workflows reutilizables. DRY tus definiciones de pipeline:
# .github/workflows/test.yml (reutilizable)
on:
workflow_call:
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm ci && npm test
# .github/workflows/main.yml
jobs:
test:
uses: ./.github/workflows/test.yml
- Setea timeouts de jobs. Previene jobs colgados que bloqueen el pipeline:
test:
timeout-minutes: 15
runs-on: ubuntu-latest
Lecturas recomendadas
- Documentación de GitHub Actions — workflows, environments, caché y reviewers obligatorios
- Documentación de GitLab CI/CD — stages, artifacts y entornos protegidos
- Argo Rollouts — despliegue canary y blue-green en Kubernetes
- Docker build-push-action — action oficial para builds de imagen con caché de capas
Preguntas frecuentes
¿Debería desplegar en cada commit a main?
Sí para staging — ya pasó los gates. Para producción, añade una aprobación manual (environment: con reviewers obligatorios) o despliega solo en releases etiquetadas.
¿Cómo gestiono las migraciones de base de datos en CI/CD?
Correlas en un job separado antes del despliegue, y mantenlas compatibles hacia atrás para que la versión anterior de la app siga funcionando durante el rollout. Nunca las corras en el arranque de la app — en un despliegue de 20 pods son 20 cambios de esquema concurrentes.
¿Puedo usar el mismo pipeline para microservicios?
Sí — usa triggers por ruta (paths: o dorny/paths-filter) para que solo los servicios modificados se construyan y desplieguen. Herramientas de monorepo como Nx o Turborepo calculan el grafo afectado por ti.
¿Cómo depuro un pipeline que solo falla en CI?
Reprodúcelo en local primero con los mismos comandos y versiones. Si solo rompe en CI, casi siempre es el estado de caché, secretos enmascarados, la variable CI o diferencias del SO del runner — depura con logging por job o action-tmate en el job que falla.
¿Cuál es el pipeline mínimo que vale la pena?
Lint, tests unitarios y un gate de build en cada PR. Ese trío caza la mayoría de roturas y se monta en un día — todo lo demás se apoya encima.
Recursos Relacionados
Guía de Pipelines CI/CD: Del Commit a Producción
Guía práctica para construir pipelines CI/CD con GitHub Actions, gates de testing, estrategias de despliegue y procedimientos de rollback.
RecipeGitHub Actions: Ejemplos de Workflows CI/CD y Matriz
Cómo construir y desplegar con GitHub Actions usando workflows, matrices, caché, secrets, environments y workflows reutilizables.
RecipeScripting en Bash para Automatización DevOps y Tareas de
Como escribir scripts Bash robustos para automatizar despliegues, monitoreo de sistemas, rotacion de logs y tareas de mantenimiento rutinarias
RecipeCron: programar tareas en Linux, Python y Node.js
Cómo programar y gestionar tareas recurrentes usando sintaxis cron en Linux, Python, Node.js y Kubernetes.
DocPlantilla de Página de Estado de API para Incidentes
Una plantilla para una página de estado de API pública que comunica uptime, incidentes y ventanas de mantenimiento a los consumidores.
RecipeRotación de copias en Bash: retención e integridad
Automatiza la rotación de copias en Bash con archivos diarios, semanales y mensuales, retención, cifrado y verificación de integridad.