¿Qué es un pipeline de CI/CD?

Un pipeline de CI/CD (Integración Continua / Despliegue Continuo) automatiza las etapas necesarias para llevar código desde el repositorio hasta producción. En GitLab, todo gira en torno al archivo .gitlab-ci.yml en la raíz del proyecto. Este archivo YAML define los stages (etapas), jobs (tareas) y scripts que se ejecutan automáticamente con cada push o merge request.

Un pipeline típico tiene tres stages: build (compilar y generar artefactos), test (ejecutar pruebas unitarias, linting, análisis de seguridad) y deploy (desplegar en desarrollo, staging o producción). GitLab ejecuta los stages en orden secuencial, pero los jobs dentro de un mismo stage pueden correr en paralelo si no hay dependencias.

Estructura básica de .gitlab-ci.yml

Veamos un ejemplo completo para un proyecto Node.js que construye, prueba y despliega:

stages:
  - build
  - test
  - deploy

variables:
  NODE_VERSION: "20-alpine"
  APP_NAME: "mi-app"

cache:
  paths:
    - node_modules/

build-app:
  stage: build
  image: node:${NODE_VERSION}
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  only:
    - main
    - develop

test-lint:
  stage: test
  image: node:${NODE_VERSION}
  script:
    - npm install
    - npm run lint
  only:
    - main
    - develop
    - merge_requests

test-unit:
  stage: test
  image: node:${NODE_VERSION}
  script:
    - npm install
    - npm run test:ci
  artifacts:
    reports:
      junit: junit.xml
  only:
    - main
    - develop
    - merge_requests

deploy-staging:
  stage: deploy
  image: alpine:3.19
  script:
    - apk add --no-cache openssh rsync
    - rsync -avz --delete dist/ deploy@staging-server:/var/www/${APP_NAME}/
  environment:
    name: staging
    url: https://staging.midominio.com
  only:
    - develop
  when: manual

deploy-production:
  stage: deploy
  image: alpine:3.19
  script:
    - apk add --no-cache openssh rsync
    - rsync -avz --delete dist/ deploy@prod-server:/var/www/${APP_NAME}/
  environment:
    name: production
    url: https://midominio.com
  only:
    - main
  when: manual

Variables de CI/CD: protegidas, enmascaradas y por entorno

Nunca pongas credenciales en duro en el archivo YAML. GitLab permite definir variables desde la interfaz web en Settings > CI/CD > Variables:

# Ejemplo de variables definidas en Settings > CI/CD
SSH_PRIVATE_KEY:          # Tipo: File (protegida, enmascarada)
DEPLOY_USER:              # Tipo: Variable
DEPLOY_SERVER:            # Tipo: Variable
SLACK_WEBHOOK_URL:        # Tipo: Variable (enmascarada)
DATABASE_URL:             # Tipo: Variable (enmascarada, solo entorno staging)

# Puedes usarlas directamente en scripts del .gitlab-ci.yml
before_script:
  - echo "Desplegando en $DEPLOY_SERVER con usuario $DEPLOY_USER"
  - which ssh-agent || (apk add --no-cache openssh)
  - eval $(ssh-agent -s)
  - echo "$SSH_PRIVATE_KEY" | ssh-add -

Las variables protegidas solo están disponibles en ramas o tags protegidos. Las enmascaradas ocultan su valor en los logs del pipeline. Puedes también asignar diferentes valores por entorno (staging, producción) para evitar mezclar configuraciones.

Deploy con rsync y SSH

Para despliegues simples, rsync sobre SSH sigue siendo el método más fiable. El setup requiere configurar las claves SSH como variables protegidas:

.deploy_template: &deploy_template
  image: alpine:3.19
  before_script:
    - apk add --no-cache openssh rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - mkdir -p ~/.ssh
    - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  script:
    - rsync -avz --delete --exclude='.env' dist/ ${DEPLOY_USER}@${DEPLOY_SERVER}:/var/www/${APP_NAME}/
    - echo "✅ Despliegue completado en $(date)"

deploy-prod:
  <<: *deploy_template
  stage: deploy
  environment:
    name: production
  only:
    - main
  when: manual
  variables:
    APP_NAME: "mi-app-prod"

El bloque when: manual convierte el job en un botón que debe pulsarse manualmente desde la interfaz de GitLab. Esto es ideal para evitar despliegues accidentales a producción.

Pipelines programados

Además de ejecutar pipelines con cada push, GitLab permite programarlos con cron. Esto es útil para tareas como limpiar logs, regenerar sitemaps o ejecutar pruebas nocturnas:

# Desde la interfaz: CI/CD > Schedules
# Crear nuevo schedule:
#   Description: "Daily cleanup"
#   Interval pattern: 0 3 * * * (3:00 AM todos los días)
#   Target branch: main
#   Variables (opcional):
#     CRON_JOB: cleanup

# En el .gitlab-ci.yml:
cleanup-old-logs:
  stage: test
  script:
    - echo "Limpiando logs anteriores a 30 días..."
    - find /var/log -name "*.log" -mtime +30 -delete
  only:
    variables:
      - $CRON_JOB == "cleanup"

Combinando pipelines manuales, programados y automáticos, cubres todos los escenarios posibles de un flujo DevOps moderno. GitLab lo hace todo desde una única plataforma, eliminando la necesidad de integrar Jenkins, Ansible Tower y otras herramientas por separado.