Script para envío de comandos en paralelo a Hosts remotos – Linux

Imagen 1. En paralelo

He estado un poco alejado del blog por que he estado trabajando en un pequeño proyecto que me ha quitado mas del tiempo esperado.

Con lo que respecta a este script, comentar que nace de la necesidad de ejecutar varios comandos a la vez en varias maquinas remotas por SSH. Antes de tener este script, lo que estaba haciendo era conectarme a las maquinas remotas por SSH y ejecutar los comandos en serie, es decir, tenía un script con un bucle para ejecutar ‘X‘ y ‘Z‘ comando en todas la maquinas remotas, el problema de esto era que los comandos debían ser ejecutados sobre 3000 maquinas, con previsión de tener que hacerlo hasta en un total de 6000

Con este script lo que he conseguido es enviar uno o varios comandos hasta un total de 8000 dispositivos a la vez. Para ver mejor la problemática: tenia que conectarme por SSH al dispositivo, realizar unas modificaciones en ciertos ficheros del sistema y a continuación reiniciar el dispositivo, esto con 3000 dispositivos tomaba hasta 1 h 30 min el proceso era el siguiente:

for device in deviceslist; do
ssh $device 
execute command1 and execute command2
done

Con el nuevo script el proceso es diferente, y para ejecutar los comandos del segundo equipo en la lista, no espera a que los comandos del dispositivo primero hayan sido ejecutados, es decir se realizan todas las conexiones en paralelo y en este caso toma 2 segundos para ejecutar los comandos, como veis la diferencia es ABISMAL. La complejidad del script no es muy grande.

A continuación dejo el código del script, pero como sabéis, si realizo alguna modificación en el script lo haré en mi GitHub, así que si quieres la última versión pásate por aquí

#!/bin/bash
# dependencias: sshpass

# Comando a enviar a los dispositivos
commandToSend="touch commandSender.txt"
#read -p "Inserta el comando a enviar: " commandToSend

# ficheros de logs
logOK="/root/commsenderlogOK.txt"
logKO="/root/commsenderlogKO.txt"

# password
pass_dev='password'

commandSender() {
 # Funcion principal encargada de enviar los comandos a los dispositivos
 # las ordenes pasadas con la variable $commandToSend

 if [[ ! $(ping -w1 $1 | grep "100% packet loss") ]]; then
 echo "DEV: $ip UP" 
 echo "$ip" >> $logOK;
 sshpass -p $pass_dev ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $1 "$commandToSend" 2> /dev/null 1>&2
 else
 echo "DEV: $ip DOWN" 
 echo "$ip" >> $logKO;
 fi
}

help() {
 # Funcion encargada de mostrar ayuda al usuario

 echo "-j [0-8000]"
 echo " Numero de conexiones que se generan en paralelo. Cualquier numero entre 0 y 8000"
 echo " Por ejemplo:"
 echo " commandsender.sh -j 300"
 echo " Establecera 300 conexiones en paralelo a 300 dispositivos, y entonces"
 echo " esperara a que todos los comandos hayan sido ejecutados para continuar" 
 echo " con las siguientes 300 conexiones."
 echo ""
 echo " Valor por defecto: -j 100"
 echo ""
 echo "-f nombre_archivo"
 echo " Si desea usar otro nombre escribalo usando esta opción."
 echo " Por ejemplo:"
 echo " commandsender.sh -f /tmp/direccionesips.txt"
 echo ""
 echo " Valor por defecto: /root/ips.txt"
 echo ""
 echo "-h"
 echo " Muestra la ayuda del script."
 echo ""
}

# se obtienen las valores pasados en las opciones

while getopts ":j:f:h" opt; do
 case "$opt" in
 j) maxJobs=$OPTARG ;;
 f) ipsFile=$OPTARG  ;;
 h) help; exit ;;
 *) echo "Opción invalida. -$OPTARG"; exit ;;
 esac
done

# si la variable $ipsFile es nula por que el usuario
# no la ha declarado, por defecto usamos /root/ips.txt

if [ -z $ipsFile ]; then 
 ipsFile="/root/ips.txt"; 
else 
 if [ ! -f $ipsFile ]; then 
 echo "El archivo $ipsFile no existe."; 
 exit 1; 
 fi
fi

# Si la variable $maxJobs es nula por que el usuario
# no ha pasado dicha opcion, por defecto usamos 100
if [ -z $maxJobs ]; then maxJobs=100; fi 

# Obtenemos todas las IPs del fichero
listado_devices=$(cat $ipsFile)

for ip in $listado_devices; do
 runningJobs=$(($runningJobs + 1))
 commandSender $ip &

 if [ $runningJobs -eq $maxJobs ]; then 
 wait; 
 runningJobs=0; 
 fi
done 

Hay una limitación que he comentado en el script y es el numero de conexiones, en este caso 8000 esto es debido a que el numero máximo de PID (jobs) que puede tener un PPID esta limitado a ese numero. He estado leyendo y no existen otra razón que pueda limitar las conexiones multiples. En Linux también se definen limites por conexiones TCP pero en este caso el limite suele ser muy alto y no debería ser un problema.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s