Saltar a contenido

📚 Módulo 7: Construyendo un asistente experto

Duración estimada: 4-5 horas según tu ritmo y experiencia.

Objetivo: Desarrollar un proyecto final completo que integre backups automatizados, monitoreo del sistema, manejo robusto de errores y técnicas de optimización de rendimiento. 🎯

Prerrequisitos: Haber completado los Módulos 1-6. Conocimientos de funciones, bucles, manejo de errores, cron y herramientas de red.

Nota: Este módulo es el más exigente del curso. Construirás algo funcional y real. Tómate el tiempo necesario para entender cada parte antes de seguir.


📖 Contenidos del Módulo

7.1. Diseño del proyecto: Sistema de backup y monitoreo 🌱

Teoría:

Un buen proyecto Bash combina todo lo aprendido: funciones para organizar el código, manejo de errores para que sea robusto, logs para auditabilidad y cron para automatización. Antes de escribir una sola línea, es fundamental diseñar la estructura: qué hará el script, qué entradas necesita y qué salidas producirá.

El proyecto de este módulo tiene dos componentes:

  1. Sistema de backup: copia de seguridad de un directorio con rotación de backups antiguos.
  2. Monitor del sistema: comprueba CPU, memoria y disco, y envía alertas si superan umbrales.

Práctica:

  1. Crea la estructura de directorios del proyecto:
mkdir -p ~/proyecto_bash/{scripts,logs,backups,config}
ls ~/proyecto_bash/
  1. Crea el archivo de configuración:
nano ~/proyecto_bash/config/config.sh
#!/bin/bash
# Configuración del proyecto
BACKUP_ORIGEN="$HOME/documentos_prueba"
BACKUP_DESTINO="$HOME/proyecto_bash/backups"
LOG_DIR="$HOME/proyecto_bash/logs"
MAX_BACKUPS=5
UMBRAL_CPU=80
UMBRAL_DISCO=90
UMBRAL_MEMORIA=85
  1. Crea el directorio de prueba con algunos archivos:
mkdir -p ~/documentos_prueba
printf "Documento 1\nContenido de prueba\n" > ~/documentos_prueba/doc1.txt
printf "Documento 2\nMás contenido\n" > ~/documentos_prueba/doc2.txt
printf "Datos importantes\n" > ~/documentos_prueba/datos.txt
  1. Diseña el flujo del script de backup en pseudocódigo:
nano ~/proyecto_bash/scripts/diseño.txt
1. Cargar configuración
2. Verificar que el directorio origen existe
3. Crear directorio de backup con marca de tiempo
4. Copiar archivos
5. Registrar el resultado en el log
6. Eliminar backups antiguos si hay más de MAX_BACKUPS
  1. Verifica la estructura completa:
find ~/proyecto_bash -type f

Consejo: Escribir el diseño antes de codificar ahorra tiempo y evita reescribir el script a mitad. 💡


7.2. Script de monitoreo del sistema 🖌️

Teoría:

Para monitorear el sistema de forma fiable desde un script necesitamos extraer valores numéricos que podamos comparar con umbrales. La clave está en usar herramientas que produzcan salidas predecibles:

  • Disco: df con la opción -P (formato POSIX) garantiza una salida consistente entre distribuciones.
  • Memoria: free permite obtener el porcentaje calculando (usado/total)*100.
  • CPU: en lugar de top, que varía mucho entre distribuciones, vmstat o leer /proc/stat son más fiables en scripts.

Práctica:

  1. Crea el script de monitoreo:
nano ~/proyecto_bash/scripts/monitor.sh
  1. Añade la estructura básica con carga de configuración:
#!/bin/bash
source ~/proyecto_bash/config/config.sh
LOG_MONITOR="$LOG_DIR/monitor_$(date +%Y%m%d).log"

registrar() {
    local nivel="$1"
    local mensaje="$2"
    echo "$(date '+%Y-%m-%d %H:%M:%S') [$nivel] $mensaje" | tee -a "$LOG_MONITOR"
}
  1. Añade la función de monitoreo de disco:
verificar_disco() {
    local uso_disco
    uso_disco=$(df -P / | awk 'NR==2 {gsub(/%/, "", $5); print $5}')
    if [ "$uso_disco" -ge "$UMBRAL_DISCO" ]; then
        registrar "ALERTA" "Disco al ${uso_disco}% (umbral: ${UMBRAL_DISCO}%)"
    else
        registrar "OK" "Disco al ${uso_disco}%"
    fi
}

df -P usa formato POSIX, garantizando que la quinta columna siempre es el porcentaje. gsub(/%/,"", $5) elimina el símbolo % antes de comparar.

  1. Añade la función de monitoreo de memoria:
verificar_memoria() {
    local total usado porcentaje
    total=$(free | awk '/^Mem:/ {print $2}')
    usado=$(free | awk '/^Mem:/ {print $3}')
    porcentaje=$(( usado * 100 / total ))
    if [ "$porcentaje" -ge "$UMBRAL_MEMORIA" ]; then
        registrar "ALERTA" "Memoria al ${porcentaje}% (umbral: ${UMBRAL_MEMORIA}%)"
    else
        registrar "OK" "Memoria al ${porcentaje}%"
    fi
}
  1. Añade la función principal y ejecútalo:
main() {
    registrar "INFO" "=== Inicio del monitoreo ==="
    verificar_disco
    verificar_memoria
    registrar "INFO" "=== Fin del monitoreo ==="
}

main

Guarda, dale permisos (chmod +x ~/proyecto_bash/scripts/monitor.sh) y ejecútalo.

Consejo: Prueba el script cambiando temporalmente los umbrales a valores bajos (ej. UMBRAL_DISCO=1) para verificar que las alertas funcionan correctamente. 💡


7.3. Optimización de rendimiento 🚀

Teoría:

Un script puede ser correcto pero ineficiente. Las dos fuentes de lentitud más comunes en Bash son:

  1. Redireccionar dentro del bucle: cada echo "..." >> archivo dentro de un bucle abre y cierra el archivo en cada iteración. Es mucho más eficiente redirigir la salida de todo el bucle de una vez.
  2. Subshells innecesarios: cada $(comando) crea un proceso hijo. Minimizarlos cuando no son necesarios reduce la carga.

Práctica:

  1. Crea el script lento (redirige dentro del bucle):
nano ~/proyecto_bash/scripts/lento.sh
#!/bin/bash
rm -f /tmp/salida_lenta.txt
for i in $(seq 1 1000); do
    echo "Línea $i" >> /tmp/salida_lenta.txt
done
echo "Versión lenta completada"
  1. Crea el script rápido (redirige el bucle entero de una vez):
nano ~/proyecto_bash/scripts/rapido.sh
#!/bin/bash
rm -f /tmp/salida_rapida.txt
for i in $(seq 1 1000); do
    echo "Línea $i"
done > /tmp/salida_rapida.txt
echo "Versión rápida completada"

La diferencia clave: en lento.sh el >> se ejecuta 1000 veces (1000 aperturas de archivo). En rapido.sh, el > al final del done abre el archivo una sola vez para toda la salida del bucle.

  1. Mide el tiempo de cada script con time:
chmod +x ~/proyecto_bash/scripts/lento.sh ~/proyecto_bash/scripts/rapido.sh
time ~/proyecto_bash/scripts/lento.sh
time ~/proyecto_bash/scripts/rapido.sh

Observa la diferencia en "real" (tiempo total transcurrido).

  1. Aplica la redirección eficiente al script de backup que crearás:
# Patrón recomendado para generar reportes dentro de funciones:
generar_reporte() {
    echo "=== Reporte $(date) ==="
    ls -lh "$BACKUP_DESTINO"
    echo "Total de backups: $(ls "$BACKUP_DESTINO" | wc -l)"
} > "$LOG_DIR/reporte.txt"
  1. Compara los tiempos con wc -l para verificar que ambos generan el mismo número de líneas:
wc -l /tmp/salida_lenta.txt /tmp/salida_rapida.txt

Consejo: La regla general es: si puedes poner la redirección fuera del bucle, ponla fuera. Es uno de los cambios más sencillos con mayor impacto en rendimiento. 💡


7.4. Script de backup completo 📦

Teoría:

Un sistema de backup robusto necesita: verificar prerrequisitos, crear el backup con una marca de tiempo única, registrar todo en un log, y rotar los backups antiguos para no agotar el espacio en disco.

Práctica:

  1. Crea el script de backup:
nano ~/proyecto_bash/scripts/backup.sh
  1. Añade la estructura completa:
#!/bin/bash
set -e
source ~/proyecto_bash/config/config.sh

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NOMBRE="backup_$TIMESTAMP"
LOG_BACKUP="$LOG_DIR/backup.log"

registrar() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_BACKUP"
}

verificar_prerequisitos() {
    if [ ! -d "$BACKUP_ORIGEN" ]; then
        registrar "ERROR: El directorio origen '$BACKUP_ORIGEN' no existe."
        exit 1
    fi
    mkdir -p "$BACKUP_DESTINO" "$LOG_DIR"
}

realizar_backup() {
    registrar "Iniciando backup de '$BACKUP_ORIGEN'..."
    cp -r "$BACKUP_ORIGEN" "$BACKUP_DESTINO/$BACKUP_NOMBRE"
    registrar "Backup completado: $BACKUP_NOMBRE"
}

rotar_backups() {
    local total
    total=$(ls -d "$BACKUP_DESTINO"/backup_* 2>/dev/null | wc -l)
    while [ "$total" -gt "$MAX_BACKUPS" ]; do
        local mas_antiguo
        mas_antiguo=$(ls -dt "$BACKUP_DESTINO"/backup_* | tail -1)
        rm -rf "$mas_antiguo"
        registrar "Backup antiguo eliminado: $(basename "$mas_antiguo")"
        total=$(( total - 1 ))
    done
}

main() {
    registrar "=== Inicio del sistema de backup ==="
    verificar_prerequisitos
    realizar_backup
    rotar_backups
    registrar "=== Backup finalizado correctamente ==="
}

main
  1. Dale permisos y ejecútalo:
chmod +x ~/proyecto_bash/scripts/backup.sh
~/proyecto_bash/scripts/backup.sh
  1. Verifica que el backup se creó correctamente:
ls ~/proyecto_bash/backups/
cat ~/proyecto_bash/logs/backup.log
  1. Ejecuta el script varias veces y verifica que la rotación funciona cuando hay más de MAX_BACKUPS copias:
# Ejecuta 6 veces para probar la rotación (MAX_BACKUPS=5)
for i in {1..6}; do ~/proyecto_bash/scripts/backup.sh; sleep 1; done
ls ~/proyecto_bash/backups/ | wc -l

Consejo: En producción usa rsync en lugar de cp -r para backups más eficientes: solo copia los archivos que han cambiado. 💡


7.5. Integración final y despliegue con cron ⚙️

Teoría:

El último paso es automatizar la ejecución de los scripts con cron. Para que funcionen sin problemas en el entorno de cron (que no carga .bashrc ni las variables de tu sesión), los scripts deben usar rutas absolutas para todos los archivos y comandos.

Práctica:

  1. Verifica que los scripts funcionan con rutas absolutas:
/bin/bash ~/proyecto_bash/scripts/backup.sh
/bin/bash ~/proyecto_bash/scripts/monitor.sh
  1. Crea un script maestro que ejecute ambos:
nano ~/proyecto_bash/scripts/maestro.sh
#!/bin/bash
/bin/bash "$HOME/proyecto_bash/scripts/backup.sh"
/bin/bash "$HOME/proyecto_bash/scripts/monitor.sh"

Guarda y dale permisos.

  1. Abre el crontab para automatizarlo:
crontab -e
  1. Añade las entradas con rutas absolutas:
# Backup diario a las 2:00 AM
0 2 * * * /bin/bash /home/TU_USUARIO/proyecto_bash/scripts/backup.sh

# Monitoreo cada 15 minutos
*/15 * * * * /bin/bash /home/TU_USUARIO/proyecto_bash/scripts/monitor.sh

Sustituye TU_USUARIO por tu nombre de usuario real (whoami te lo muestra).

  1. Revisa el log después de unos minutos para confirmar la ejecución automática:
tail -f ~/proyecto_bash/logs/backup.log

Advertencia: Usa siempre $HOME o rutas absolutas completas en scripts de cron. Las rutas relativas fallan porque cron no sabe cuál es tu directorio de trabajo. 🛑


🛠️ Ejercicios prácticos

  1. Monitoreo: Añade al script de monitoreo una comprobación del número de procesos zombi (ps aux | grep -c 'Z').
  2. Backup: Modifica el script de backup para comprimir el directorio con tar -czf en lugar de copiarlo con cp -r.
  3. Optimización: Mide con time la diferencia entre tu script de backup con y sin compresión.
  4. Cron: Configura el monitor para que también guarde su salida en un archivo de log separado por día (monitor_YYYYMMDD.log).
  5. Desafío: Añade al script maestro el envío de un resumen por correo usando mail o sendmail si están disponibles en tu sistema.

📝 Evaluación

Cuestionario (5 preguntas):

  1. ¿Por qué es más eficiente redirigir la salida del bucle entero que redirigir dentro de cada iteración?
  2. ¿Qué hace set -e al principio de un script?
  3. ¿Por qué hay que usar rutas absolutas en scripts de cron?
  4. ¿Qué hace trap 'función' ERR?
  5. ¿Cómo verificarías que un script de cron se ejecutó correctamente?

Tarea práctica:

Extiende el proyecto con: - Una función enviar_alerta en el script de monitoreo que escriba en un archivo alertas.log solo cuando se supere un umbral. - Modifica el backup para que use tar -czf y el archivo resultante tenga extensión .tar.gz. - Configura el crontab para ejecutar el maestro cada hora.

Entrega: Los scripts actualizados, el backup.log y el alertas.log.

🎬 Vídeo con la solución de la evaluación: Próximamente.


🎉 Recursos adicionales

  • Documentación: man rsync, man tar, man crontab.
  • Herramienta de cron: Crontab.guru para construir expresiones cron fácilmente.
  • Análisis de scripts: ShellCheck para detectar errores antes de ejecutar.
  • Referencia de rsync: rsync.samba.org — la herramienta definitiva para backups eficientes.

Tip: Usa ShellCheck en todos tus scripts antes de pasarlos a producción. Detecta docenas de errores sutiles automáticamente. 💡


🚀 Siguientes pasos

¡Felicidades por completar el Módulo 7! En el Módulo 8 explorarás temas avanzados: integración con Python, patrones de DevOps, y shells alternativas como Zsh y Fish. ¡El último capítulo te espera! ✍️