Instalación de Rundeck: Automatización de Jobs y Respuesta a Incidentes

Rundeck es una plataforma de automatización operacional que permite definir, programar y ejecutar runbooks de forma centralizada, facilitando tanto la automatización rutinaria como la respuesta a incidentes. Con soporte para control de acceso granular, gestión de nodos remotos y triggers via webhook, Rundeck es la solución ideal para equipos de operaciones que gestionan servidores Linux en producción.

Requisitos Previos

  • Ubuntu 20.04/22.04 o CentOS/Rocky Linux 8+
  • Java 11+ (OpenJDK recomendado)
  • Al menos 2 GB de RAM (4 GB recomendado para producción)
  • MySQL 8.0+ o PostgreSQL 14+ (para producción, SQLite por defecto)
  • Acceso SSH a los nodos remotos que se van a gestionar
  • Acceso root o usuario con privilegios sudo

Instalación de Rundeck

Instalación en Ubuntu/Debian

# Instalar dependencias
sudo apt-get update
sudo apt-get install -y openjdk-11-jdk curl gnupg

# Añadir repositorio de Rundeck
curl -s https://packagecloud.io/pagerduty/rundeck/gpgkey | sudo apt-key add -

cat > /etc/apt/sources.list.d/rundeck.list << 'EOF'
deb https://packagecloud.io/pagerduty/rundeck/ubuntu/ focal main
EOF

sudo apt-get update
sudo apt-get install -y rundeck

# Iniciar y habilitar el servicio
sudo systemctl enable rundeck
sudo systemctl start rundeck

# Verificar el estado
sudo systemctl status rundeck

Instalación en CentOS/Rocky Linux

# Añadir repositorio RPM
sudo curl -o /etc/yum.repos.d/rundeck.repo \
    https://packagecloud.io/pagerduty/rundeck/config_file.repo?os=rpm_any

# Instalar
sudo yum install -y java-11-openjdk rundeck

# Iniciar servicio
sudo systemctl enable rundeck
sudo systemctl start rundeck

Instalación con Docker

# Crear directorio de datos persistente
mkdir -p /opt/rundeck/{data,logs}

# Docker Compose para Rundeck con MySQL
cat > /opt/rundeck/docker-compose.yml << 'EOF'
version: '3.8'

services:
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: root_password_seguro
      MYSQL_DATABASE: rundeck
      MYSQL_USER: rundeck
      MYSQL_PASSWORD: rundeck_pass_123
    volumes:
      - mysql_data:/var/lib/mysql

  rundeck:
    image: rundeck/rundeck:latest
    restart: unless-stopped
    ports:
      - "4440:4440"
    environment:
      RUNDECK_DATABASE_DRIVER: org.mariadb.jdbc.Driver
      RUNDECK_DATABASE_URL: jdbc:mysql://db:3306/rundeck?autoReconnect=true&useSSL=false
      RUNDECK_DATABASE_USERNAME: rundeck
      RUNDECK_DATABASE_PASSWORD: rundeck_pass_123
      RUNDECK_GRAILS_URL: http://rundeck.mi-dominio.com:4440
      RUNDECK_SERVER_ADDRESS: 0.0.0.0
    volumes:
      - rundeck_data:/home/rundeck/server/data
      - /opt/rundeck/logs:/home/rundeck/var/logs
    depends_on:
      - db

volumes:
  mysql_data:
  rundeck_data:
EOF

cd /opt/rundeck
docker-compose up -d

Configuración Inicial

# Acceder a la interfaz web: http://servidor:4440
# Credenciales por defecto: admin / admin (CAMBIAR inmediatamente)

# Configuración del archivo principal de Rundeck
sudo cat > /etc/rundeck/rundeck-config.properties << 'EOF'
# URL base de Rundeck
grails.serverURL=https://rundeck.mi-dominio.com

# Base de datos (producción con MySQL)
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/rundeck?autoReconnect=true
dataSource.username=rundeck
dataSource.password=rundeck_pass_123

# Configuración de seguridad
rundeck.security.requestHeaderAuthentication.enabled=false
EOF

# Cambiar contraseña del administrador
# Editar /etc/rundeck/realm.properties
sudo sed -i 's/admin:admin,user,admin,architect,deploy,build/admin:NUEVA_PASSWORD,user,admin,architect,deploy,build/' \
    /etc/rundeck/realm.properties

sudo systemctl restart rundeck

Definición de Jobs

Los jobs son la unidad central de automatización en Rundeck:

# Crear un job via API (primero obtener token de API)
# Panel Admin > User > Profile > API Tokens > Generate Token

# Crear proyecto
curl -X POST http://localhost:4440/api/40/projects \
    -H "X-Rundeck-Auth-Token: TU_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
        "name": "infraestructura",
        "description": "Automatización de infraestructura",
        "config": {
            "project.description": "Gestión de servidores de producción"
        }
    }'

# Crear job en formato YAML
cat > /tmp/job-deploy.yaml << 'EOF'
- name: Despliegue de Aplicación
  description: Despliega la última versión de la aplicación en producción
  id: deploy-app-v1
  loglevel: INFO
  sequence:
    keepgoing: false
    strategy: node-first
    commands:
      # Paso 1: Verificar salud del servidor
      - exec: "systemctl is-active nginx || exit 1"
      
      # Paso 2: Hacer backup de la versión actual
      - exec: "cp -r /var/www/app /var/www/app.bak.$(date +%Y%m%d)"
      
      # Paso 3: Descargar nueva versión
      - exec: "cd /opt/releases && git pull origin main"
      
      # Paso 4: Instalar dependencias
      - exec: "cd /opt/releases && npm install --production"
      
      # Paso 5: Reiniciar la aplicación
      - exec: "systemctl restart app"
      
      # Paso 6: Verificar que la aplicación responde
      - exec: "sleep 5 && curl -f http://localhost:3000/health"
      
  nodefilters:
    filter: "tags: web-server"
  schedule:
    time:
      hour: "2"
      minute: "0"
      seconds: "0"
    month: "*"
    year: "*"
    dayofweek: "*"
  scheduleEnabled: true
EOF

# Importar el job al proyecto
curl -X POST "http://localhost:4440/api/40/project/infraestructura/jobs/import" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN" \
    -H "Content-Type: application/yaml" \
    --data-binary @/tmp/job-deploy.yaml

# Ejecutar el job manualmente
curl -X POST "http://localhost:4440/api/40/job/JOB_ID/run" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"argString": "-entorno produccion"}'

Gestión de Nodos

Definir los nodos (servidores) que Rundeck gestiona:

# Crear archivo de recursos de nodos
sudo mkdir -p /var/rundeck/projects/infraestructura/etc

cat > /var/rundeck/projects/infraestructura/etc/resources.yaml << 'EOF'
# Inventario de nodos gestionados por Rundeck
web-01:
  nodename: web-01
  hostname: 192.168.1.10
  username: deploy
  tags: web-server,produccion
  description: "Servidor web principal"
  osFamily: unix
  ssh-key-storage-path: "keys/web-servers/deploy_key"

web-02:
  nodename: web-02
  hostname: 192.168.1.11
  username: deploy
  tags: web-server,produccion
  description: "Servidor web secundario"
  osFamily: unix
  ssh-key-storage-path: "keys/web-servers/deploy_key"

db-primary:
  nodename: db-primary
  hostname: 192.168.1.20
  username: rundeck
  tags: database,mysql,produccion
  description: "Base de datos MySQL principal"
  osFamily: unix
  ssh-key-storage-path: "keys/db-servers/rundeck_key"
EOF

# Subir claves SSH al almacén de Rundeck
curl -X POST "http://localhost:4440/api/40/storage/keys/web-servers/deploy_key" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN" \
    -H "Content-Type: application/octet-stream" \
    --data-binary @~/.ssh/deploy_key

# Verificar nodos disponibles
curl "http://localhost:4440/api/40/project/infraestructura/nodes" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN"

Políticas ACL

Control de acceso granular con políticas ACLPOLICY:

# Crear política para operadores (solo pueden ejecutar jobs, no crearlos)
cat > /etc/rundeck/acl/operadores.aclpolicy << 'EOF'
# Política ACL para el rol de operadores
description: Política para operadores - solo ejecución
context:
  project: '.*'
for:
  resource:
    - equals:
        kind: job
      allow: [read, run]
    - equals:
        kind: node
      allow: [read, run]
  job:
    - allow: [read, run, kill]
  node:
    - allow: [read, run]
by:
  group: operadores
---
description: Acceso a la aplicación
context:
  application: rundeck
for:
  resource:
    - equals:
        kind: project
      allow: [read]
  project:
    - match:
        name: '.*'
      allow: [read]
by:
  group: operadores
EOF

# Verificar la sintaxis de la política ACL
sudo java -jar /var/lib/rundeck/bootstrap/rundeck-cli.jar \
    aclvalidate -f /etc/rundeck/acl/operadores.aclpolicy

sudo systemctl reload rundeck

Triggers via Webhook

Ejecutar jobs automáticamente via HTTP:

# Crear webhook desde el panel: Jobs > Webhooks > Add Webhook

# O via API
curl -X POST "http://localhost:4440/api/40/project/infraestructura/webhooks" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
        "name": "deploy-trigger",
        "enabled": true,
        "eventPlugin": "webhook-run-job",
        "config": {
            "jobId": "JOB_ID",
            "argString": "-entorno produccion"
        }
    }'

# El webhook se invoca con una petición HTTP POST:
curl -X POST "http://localhost:4440/api/40/webhook/TOKEN_WEBHOOK" \
    -H "Content-Type: application/json" \
    -d '{"branch": "main", "commit": "abc123"}'

# Integración con GitHub Actions (en .github/workflows/deploy.yml):
# - name: Trigger Rundeck Deploy
#   run: |
#     curl -X POST "$RUNDECK_WEBHOOK_URL" \
#         -H "Content-Type: application/json" \
#         -d '{"commit": "${{ github.sha }}"}'

Runbooks de Respuesta a Incidentes

Definir procedimientos estandarizados para incidentes:

# Job de respuesta a servidor no disponible
cat > /tmp/runbook-servidor-caido.yaml << 'EOF'
- name: "RUNBOOK: Servidor No Disponible"
  description: "Procedimiento de respuesta cuando un servidor deja de responder"
  loglevel: INFO
  sequence:
    keepgoing: false
    commands:
      # Paso 1: Verificar conectividad de red
      - exec: "ping -c 3 ${node.hostname} && echo 'RED OK' || echo 'SIN RED'"
      
      # Paso 2: Verificar servicios críticos
      - script: |
          #!/bin/bash
          # Comprobar estado de servicios esenciales
          for servicio in nginx mysql sshd; do
              if ! systemctl is-active --quiet $servicio; then
                  echo "FALLO: $servicio no está activo"
                  systemctl start $servicio
                  sleep 3
                  systemctl is-active --quiet $servicio && echo "$servicio reiniciado OK"
              else
                  echo "OK: $servicio activo"
              fi
          done
      
      # Paso 3: Verificar espacio en disco
      - exec: "df -h / | awk 'NR==2{print \"Disco: \"$5\" usado\"}'"
      
      # Paso 4: Verificar carga del sistema
      - exec: "uptime && free -h"
      
      # Paso 5: Enviar informe de estado
      - exec: "journalctl -n 50 --no-pager > /tmp/incident_log.txt"

  options:
    - name: servidor
      description: "Nombre del servidor afectado"
      required: true
    - name: severidad
      description: "Nivel de severidad del incidente"
      values:
        - "critico"
        - "alto"
        - "medio"
      required: true
EOF

curl -X POST "http://localhost:4440/api/40/project/infraestructura/jobs/import" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN" \
    -H "Content-Type: application/yaml" \
    --data-binary @/tmp/runbook-servidor-caido.yaml

Solución de Problemas

Rundeck no arranca (error de Java):

# Verificar versión de Java
java -version

# Revisar logs de inicio
sudo journalctl -u rundeck -n 50
sudo tail -f /var/log/rundeck/service.log

No se puede conectar a los nodos via SSH:

# Probar conexión SSH manual
ssh -i /path/to/key [email protected]

# Verificar que la clave está correctamente almacenada en Rundeck
curl "http://localhost:4440/api/40/storage/keys/web-servers/deploy_key" \
    -H "X-Rundeck-Auth-Token: TU_TOKEN"

# Revisar el log de ejecución del job para ver el error SSH específico

El scheduler no ejecuta los jobs:

# Verificar que la zona horaria está configurada correctamente
sudo cat /etc/rundeck/profile | grep JAVA_OPTS
# Añadir si falta: JAVA_OPTS="... -Duser.timezone=Europe/Madrid"

sudo systemctl restart rundeck

Conclusión

Rundeck transforma la gestión operacional al centralizar la ejecución de runbooks, scripts y tareas de mantenimiento en una plataforma con control de acceso y auditoría completa. Su integración via webhooks permite responder automáticamente a alertas de herramientas de monitorización, mientras que el sistema de jobs programados elimina intervención manual en tareas rutinarias. Con políticas ACL granulares, Rundeck habilita el autoservicio seguro para equipos de desarrollo sin comprometer la seguridad de la infraestructura.