📚 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:
- Sistema de backup: copia de seguridad de un directorio con rotación de backups antiguos.
- Monitor del sistema: comprueba CPU, memoria y disco, y envía alertas si superan umbrales.
Práctica:
- Crea la estructura de directorios del proyecto:
mkdir -p ~/proyecto_bash/{scripts,logs,backups,config}
ls ~/proyecto_bash/
- 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
- 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
- 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
- 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:
dfcon la opción-P(formato POSIX) garantiza una salida consistente entre distribuciones. - Memoria:
freepermite obtener el porcentaje calculando(usado/total)*100. - CPU: en lugar de
top, que varía mucho entre distribuciones,vmstato leer/proc/statson más fiables en scripts.
Práctica:
- Crea el script de monitoreo:
nano ~/proyecto_bash/scripts/monitor.sh
- 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"
}
- 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.
- 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
}
- 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:
- Redireccionar dentro del bucle: cada
echo "..." >> archivodentro 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. - Subshells innecesarios: cada
$(comando)crea un proceso hijo. Minimizarlos cuando no son necesarios reduce la carga.
Práctica:
- 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"
- 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.
- 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).
- 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"
- Compara los tiempos con
wc -lpara 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:
- Crea el script de backup:
nano ~/proyecto_bash/scripts/backup.sh
- 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
- Dale permisos y ejecútalo:
chmod +x ~/proyecto_bash/scripts/backup.sh
~/proyecto_bash/scripts/backup.sh
- Verifica que el backup se creó correctamente:
ls ~/proyecto_bash/backups/
cat ~/proyecto_bash/logs/backup.log
- Ejecuta el script varias veces y verifica que la rotación funciona cuando hay más de
MAX_BACKUPScopias:
# 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
rsyncen lugar decp -rpara 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:
- Verifica que los scripts funcionan con rutas absolutas:
/bin/bash ~/proyecto_bash/scripts/backup.sh
/bin/bash ~/proyecto_bash/scripts/monitor.sh
- 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.
- Abre el crontab para automatizarlo:
crontab -e
- 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).
- 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
$HOMEo rutas absolutas completas en scripts decron. Las rutas relativas fallan porquecronno sabe cuál es tu directorio de trabajo. 🛑
🛠️ Ejercicios prácticos¶
- Monitoreo: Añade al script de monitoreo una comprobación del número de procesos zombi (
ps aux | grep -c 'Z'). - Backup: Modifica el script de backup para comprimir el directorio con
tar -czfen lugar de copiarlo concp -r. - Optimización: Mide con
timela diferencia entre tu script de backup con y sin compresión. - Cron: Configura el monitor para que también guarde su salida en un archivo de log separado por día (
monitor_YYYYMMDD.log). - Desafío: Añade al script maestro el envío de un resumen por correo usando
mailosendmailsi están disponibles en tu sistema.
📝 Evaluación¶
Cuestionario (5 preguntas):
- ¿Por qué es más eficiente redirigir la salida del bucle entero que redirigir dentro de cada iteración?
- ¿Qué hace
set -eal principio de un script? - ¿Por qué hay que usar rutas absolutas en scripts de
cron? - ¿Qué hace
trap 'función' ERR? - ¿Cómo verificarías que un script de
cronse 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! ✍️