¡Desplega tu aplicación con estilo usando Bash Scripting!

¡Desplega tu aplicación con estilo usando Bash Scripting!

Mastering Automation, One Script at a Time: The Series

Hoy te traigo una guía sobre un poderoso script en Bash que te permitirá automatizar la construcción y despliegue de una aplicación Node.js, ya sea ejecutándola localmente o dentro de un contenedor Docker. Además, te enseñaré algunos tips and tricks de buenas prácticas para mejorar tu desarrollo en Bash.

Este script forma parte del proyecto alojado en mi repositorio de GitHub. ¡Vamos a explorarlo y aprender algunos trucos para hacer que tus scripts Bash sean más robustos y eficientes!

¿Qué hace este script?

Este script es un ejemplo perfecto de cómo escribir código en Bash de manera organizada y robusta. Incluye funciones útiles para:

  1. Verificar herramientas instaladas: Asegura que todas las dependencias necesarias (como node, npm, jq, git, e incluso docker en modo contenedor) estén presentes.

  2. Obtener información del repositorio Git: Como el nombre del repositorio, la rama activa, y el nombre de la imagen Docker.

  3. Leer la versión de la aplicación desde package.json: Esto ayuda a versionar las imágenes Docker correctamente.

  4. Construir y ejecutar un contenedor Docker: Crea un Dockerfile de forma dinámica y luego ejecuta el contenedor de la aplicación.

  5. Ejecutar localmente: Si prefieres probar tu aplicación en tu máquina sin usar Docker, ¡también lo cubre!

  6. Detener la aplicación: Tanto en local como en Docker.

Código:

#!/bin/bash

# Habilitar modo estricto y debugging
set -euo pipefail
#set -x
IFS=$'\n\t'

TEMP_DIR="tempdir"
PORT=3000
DOCKERFILE_PATH="Dockerfile"
PID_FILE="/tmp/node-server-app.pid"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m'

# Banner llamativo
print_banner() {
    echo -e "${BLUE}"
    echo "##############################################"
    echo "      🚀 Bienvenido al RoxsApp Deployer! 🚀   "
    echo "        Automatizando todo con estilo...      "
    echo "              MODO:$EXECUTION_MODE            "
    echo "###############################################"
    echo -e "${NC}"
}

# Función para imprimir mensajes coloreados
print_message() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

# .......

Puedes encontrar el código completo en mi repositorio en GitHub.

¿Cómo Funciona el Script?

Este script en Bash está diseñado para automatizar el proceso de despliegue de una aplicación Node.js, permitiendo ejecutarla de manera local o dentro de un contenedor Docker. A continuación, te explico cada paso clave del funcionamiento:

  1. Configuración Inicial:

    • El script activa el modo estricto (set -euo pipefail) para asegurar una mejor gestión de errores.

    • Se definen variables de entorno importantes, como el directorio temporal (TEMP_DIR), el puerto de la aplicación (PORT), el archivo Dockerfile y otros parámetros necesarios para el despliegue.

  2. Modos de Ejecución:

    • LOCAL: Ejecuta la aplicación Node.js en tu máquina local.

    • DOCKER: Construye y ejecuta la aplicación dentro de un contenedor Docker.

    • STOP: Detiene la aplicación, ya sea que esté corriendo localmente o en Docker.

  3. Funciones Clave:

    • check_all_tools: Verifica que todas las herramientas necesarias (Node.js, npm, jq, git) estén instaladas antes de proceder.

    • get_git_info: Obtiene información del repositorio y rama actual, generando nombres para la imagen Docker y el contenedor basados en estos datos.

    • read_app_version: Lee la versión de la aplicación desde el archivo package.json para etiquetar la imagen Docker con la versión correcta.

    • create_dockerfile: Genera dinámicamente un archivo Dockerfile que especifica cómo construir la imagen de la aplicación Node.js.

    • docker_build_and_run: Construye la imagen Docker y la despliega en un contenedor.

    • run_locally: Ejecuta la aplicación localmente en el puerto especificado y administra los procesos activos.

  4. Flujo de Trabajo:

    • Dependiendo del modo seleccionado (LOCAL o DOCKER), el script realiza los siguientes pasos:

      • Verifica las herramientas necesarias.

      • Obtiene la información del repositorio y la versión de la aplicación.

      • En modo DOCKER, genera un Dockerfile, construye la imagen y despliega un contenedor.

      • En modo LOCAL, instala las dependencias de npm y ejecuta la aplicación directamente en el puerto especificado.

    • Si la aplicación ya está corriendo en el puerto designado, el script puede detener el proceso anterior antes de iniciar uno nuevo.

  5. Limpieza y Finalización:

    • Al finalizar, se realiza una limpieza de recursos temporales para garantizar que no queden archivos ni contenedores innecesarios ocupando espacio.

¿Cómo Ejecutar el Script?

Sigue estos pasos para ejecutar el script y automatizar el despliegue de tu aplicación Node.js, ya sea localmente o dentro de un contenedor Docker.

1. Clona el Repositorio

Primero, clona el repositorio que contiene el script y la configuración necesaria:

branch devops-cars-management-system

git clone -b devops-cars-management-system https://github.com/roxsross/roxs-projects-blog.git
cd roxs-projects-blog

2. Haz el Script Ejecutable

Si aún no lo has hecho, asegúrate de que el archivo del script tenga permisos de ejecución:

chmod +x deploy.sh

3. Modos de Ejecución del Script

El script ofrece tres modos de ejecución: LOCAL, DOCKER, y STOP. A continuación te explico cómo usar cada uno:

  • Ejecución Local
    Para ejecutar la aplicación Node.js localmente, ejecuta el siguiente comando:

      ./deploy.sh LOCAL
    

    Esto iniciará la aplicación en tu máquina usando el puerto configurado (por defecto el puerto 3000). Si deseas cambiar el puerto, puedes hacerlo editando la variable PORT en el script.

  • 💡
    Version de Node Recomendada superior a v18
  • Te recomiendo usar el Gestor NVM para trabajar con multiples versiones de node su instalación es muy fácil https://github.com/nvm-sh/nvm
  • 💡
    Importante si ejecutas el script LOCAL ten en cuenta que si lo corres en Ubuntu debes tener instalado estos paquetes
  •   sudo apt update
      sudo apt install build-essential python3 make
    
  • Ejecución en Docker
    Si prefieres desplegar la aplicación dentro de un contenedor Docker, usa el siguiente comando:

      ./deploy.sh DOCKER
    

    El script construirá la imagen Docker utilizando el Dockerfile generado automáticamente y ejecutará la aplicación en un contenedor.

  • Detener la Aplicación
    Para detener la aplicación, ya sea que esté corriendo localmente o en Docker, utiliza:

      ./deploy.sh STOP
    

    Esto se encargará de matar el proceso local o detener el contenedor Docker que esté en ejecución.

4. Verificar el Despliegue

Dependiendo del modo de ejecución que elijas, puedes acceder a la aplicación desde el navegador usando:

  • Local:
    Si ejecutaste la aplicación localmente, ve a http://localhost:3000 (o el puerto que hayas configurado).

  • Docker:
    Si utilizaste Docker, abre el navegador y accede a http://localhost:<puerto> para verificar que el contenedor se está ejecutando correctamente.


Script en Acción


💡
Si quieren que este video lo explique paso a paso puedes contactarme en alguna de mis redes sociales roxs.295devops.com

Buenas prácticas y tips


1. Usa set -euo pipefail y IFS=$'\n\t' para evitar errores ocultos

  • set -euo pipefail: Habilitar este modo es clave para evitar que el script continúe si ocurre un error. De esta forma, el script se detiene si un comando falla (-e), si intentas usar una variable no declarada (-u), o si algún comando dentro de una pipeline falla (pipefail).

  • IFS=$'\n\t': Esto ajusta el Internal Field Separator para que los espacios en blanco no separen elementos inesperadamente. Es especialmente útil cuando trabajas con archivos o variables que contienen saltos de línea.

Tip: No olvides incluir este ajuste en tus scripts para prevenir errores difíciles de detectar.


2. Colorea tus salidas para mejorar la legibilidad

  • Este script utiliza variables de color como RED, GREEN, YELLOW, BLUE, NC para imprimir mensajes coloridos. Esto hace que sea más fácil identificar errores, advertencias o mensajes de éxito.
print_message() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

Tip: Añadir colores es una excelente manera de hacer que tu script sea más intuitivo. Úsalo especialmente cuando haya errores, advertencias o información clave.


3. Verifica dependencias antes de continuar

  • check_tool es una función que comprueba si las herramientas necesarias están instaladas antes de proceder. Si alguna no está, el script muestra un mensaje de error y sale.
check_tool() {
    if ! command -v "$1" &>/dev/null; then 
        print_message "$RED" "Error: '$1' no está instalado."
        exit 1
    fi
}

Tip: Siempre asegúrate de que todas las dependencias estén disponibles antes de ejecutar el código principal de tu script. Esto evitará problemas inesperados durante la ejecución.


4. Divide tu script en funciones reutilizables

  • Agrupar tareas en funciones como create_dockerfile, docker_build_and_run, run_locally y stop_node_server ayuda a mantener el código organizado y facilita el mantenimiento.
create_dockerfile() {
    cat <<EOF > "$TEMP_DIR/$DOCKERFILE_PATH"
    FROM node:18-alpine
    LABEL org.opencontainers.image.authors="RoxsRoss"
    RUN apk add --no-cache python3 make g++
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    EXPOSE $PORT
    CMD ["npm", "start"]
EOF
}

Tip: Cada función debe cumplir un propósito claro y específico. Esto no solo mejora la legibilidad del código, sino que también facilita la depuración.


5. Control de errores y salida temprana

  • A lo largo del script, hay comprobaciones continuas de errores con el uso de if y el comando exit 1 si algo falla. Por ejemplo, si el archivo package.json no existe o no puede leerse:
if [ ! -f "package.json" ]; then
    print_message "$RED" "Error: 'package.json' no encontrado."
    exit 1
fi

Tip: Controlar errores y salir temprano es esencial en scripts Bash para evitar que el flujo continúe cuando las condiciones no son las correctas.


6. Automatiza el manejo de procesos

  • El script incluye funciones para detener procesos locales o contenedores en ejecución, lo que es clave para limpiar después de cada ejecución:
stop_node_server() {
    PIDS=$(lsof -t -i:"$PORT")
    if [ -n "$PIDS" ]; then
        echo "$PIDS" | xargs kill -9
    fi
}

Tip: Asegúrate de limpiar los procesos o contenedores para evitar que el puerto quede bloqueado o que queden recursos innecesarios ejecutándose.


7. Usa trap para manejar la limpieza al salir

  • Este script maneja la limpieza de recursos (como directorios temporales) usando trap. Esto asegura que, incluso si el script es interrumpido, la limpieza ocurra:
trap 'cleanup; show_running_processes; exit 0' EXIT

Tip: Implementar un trap para la limpieza es una buena práctica en Bash scripting, especialmente cuando se trabaja con archivos o procesos temporales.


8. Desactiva debugging para producción, pero habilítalo durante el desarrollo

  • El script incluye set -x, que está comentado, pero se puede habilitar para hacer un debugging más sencillo:
#set -x

Tip: Habilita el modo de debugging (set -x) cuando estés depurando un problema y desactívalo en producción para mantener los logs limpios.


Desafio

Crear un script de automatización para un pipeline de CI/CD que prepare, construya y despliegue una aplicación web simple.

Requisitos

  1. Script de Automatización

    • Crea un script en Bash llamado ci_cd_automation.sh.

    • El script debe realizar las siguientes tareas:

      • a. Obtener la versión actual del proyecto (de package.json, CHANGELOG.md o Git tags).

      • b. Obtener el nombre del repositorio y la rama actual.

      • c. Construir el nombre de la imagen Docker basado en el repositorio y la versión.

      • d. Obtener información del último commit (autor y correo electrónico).

      • e. Verificar si las herramientas necesarias (git, docker, jq) están instaladas.

Revisa el Repositorio Pasos a Seguir


Conclusión

Este script en Bash es un excelente ejemplo de cómo automatizar tareas comunes en el ciclo de vida de desarrollo de una aplicación, desde la verificación de dependencias hasta la ejecución de aplicaciones localmente o en contenedores. Usar funciones modulares, manejo adecuado de errores, y técnicas avanzadas como trap y set -euo pipefail garantiza que tu script sea seguro, escalable y fácil de mantener.

Implementar estos tips y seguir buenas prácticas de scripting te permitirá mejorar tus habilidades y evitar sorpresas inesperadas en producción. ¡Pruébalo y mejora la automatización de tus despliegues!