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.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Visió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 Usar
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
Preguntas Frecuentes
P: ¿Debería deployar en cada commit a main? R: Sí para staging. Para producción, usa un gate manual o deploy en releases taggeados.
P: ¿Cómo manejo migraciones de base de datos en CI/CD? R: Ejecuta migraciones en un job separado antes del deploy. Usa migraciones backward-compatible para evitar downtime.
P: ¿Puedo usar el mismo pipeline para microservicios? R: Sí, pero usa triggers basados en paths para que solo los servicios afectados se construyan y desplieguen. Herramientas de monorepo (Nx, Turborepo) ayudan.
¿Esta solución está lista para producción?
Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.
¿Cuáles son las características de rendimiento?
El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.
¿Cómo depuro problemas con este enfoque?
Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.
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
Mejores Prácticas Adicionales
- Promueve artefactos, no código. Construye una vez, despliega el mismo artefacto a cada entorno:
# Construir una vez
build:
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- run: docker build -t app:${{ github.sha }} .
- run: docker push app:${{ github.sha }}
# Desplegar la misma imagen a cada entorno
deploy-staging:
needs: build
steps:
- run: kubectl set image deployment/app app=app:${{ github.sha }}
deploy-prod:
needs: [build, deploy-staging]
steps:
- run: kubectl set image deployment/app app=app:${{ github.sha }}
- Usa reglas de protección de entorno. Requiere aprobación manual para producción:
deploy-production:
environment:
name: production # Requiere aprobación en settings del repo
- Ejecuta migraciones de base de datos separadamente. Migra antes de desplegar código nuevo:
migrate:
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
flyway migrate -url=$DATABASE_URL \
-user=$DB_USER -password=$DB_PASSWORD
Errores Comunes Adicionales
- No testear deploys. Añade smoke tests después del deploy:
#!/bin/bash
# smoke-test.sh
URL=$1
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "$URL/health")
if [ "$RESPONSE" != "200" ]; then
echo "Smoke test falló: $URL retornó $RESPONSE"
exit 1
fi
echo "Smoke test passed"
- No hay pipeline de rollback. Crea un workflow de rollback:
# .github/workflows/rollback.yml
name: Rollback
on:
workflow_dispatch:
inputs:
version:
description: 'Versión a la que rollbackear'
required: true
jobs:
rollback:
runs-on: ubuntu-latest
environment: production
steps:
- run: kubectl set image deployment/app app=app:${{ inputs.version }}
- run: kubectl rollout status deployment/app
FAQ Adicional
¿Cómo manejo deploys de feature branches?
Usa entornos preview que despliegan en cada PR:
deploy-preview:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- run: |
PREVIEW_URL="pr-${{ github.event.number }}.preview.example.com"
./scripts/deploy-preview.sh $PREVIEW_URL
¿Cómo paralelizo suites de tests?
Divide tests entre múltiples runners:
test:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npm test -- --shard=${{ matrix.shard }}/4
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 Recursos Relacionados
CI/CD Pipeline Guide
A practical guide to building CI/CD pipelines with GitHub Actions, testing, deployment strategies, and rollback procedures.
RecipeGitHub Actions CI/CD
How to build and deploy with GitHub Actions using workflows, matrices, caching, and secrets.
RecipeBash Scripting for DevOps Automation and System Tasks
How to write reliable Bash scripts for automating deployments, system monitoring, log rotation, and routine maintenance tasks
RecipeCron Jobs
How to schedule and manage recurring tasks using cron syntax across Linux, Python, and Node.js.
DocAPI Status Page Template
A template for a public API status page that communicates uptime, incidents, and maintenance windows to consumers.
RecipeBackup Rotation Script in Bash
Automated backup with retention policies using bash and find.