ossh: ejecución paralela de comandos en muchos servidores

A veces es necesario ejecutar el parche de Barmin algún comando en muchos servidores y es recomendable no esperar demasiado por los resultados de la ejecución. Para esto, escribí ossh (One SSH para gobernarlos a todos). A continuación, se muestra un ejemplo de cómo funciona:



$ wc -l /tmp/ossh.ips
21418 /tmp/ossh.ips
$ time ossh -n -h /tmp/ossh.ips -c uptime -p 1000 >/tmp/ossh.out

real    3m10.310s
user    0m30.970s
sys     0m19.282s
$ grep 'load average' /tmp/ossh.out | sort -n -k5 | tail -n1
10.23.91.97   [1]  13:37:55 up 828 days,  2:34,  0 users,  load average: 8.29, 4.45, 3.90
$

      
      





En este ejemplo, el archivo /tmp/ossh.ips contiene 21418 direcciones IP de máquinas. -n significa que no es necesario realizar consultas inversas para determinar el nombre por dirección. -c uptime establece el comando que quiero ejecutar. -p 1000 permite hasta 1000 conexiones al mismo tiempo. Como puede ver en el resultado, el comando funcionó con bastante rapidez.



¿Qué más puede hacer ossh?



$ ossh -?
Usage: ossh [-?AinPv] [-c COMMAND] [-C COMMAND_FILE] [-H HOST_STRING] [-h HOST_FILE] [-I FILTER] [-k PRIVATE_KEY] [-l USER] [-o PORT] [-p PARALLELISM] [-T TIMEOUT] [-t TIMEOUT] [parameters ...]
 -?, --help        Show help
 -A, --askpass     Prompt for a password for ssh connects
 -c, --command=COMMAND
                   Command to run
 -C, --command-file=COMMAND_FILE
                   file with commands to run
 -H, --host=HOST_STRING
                   Add the given HOST_STRING to the list of hosts
 -h, --hosts=HOST_FILE
                   Read hosts from file
 -i, --ignore-failures
                   Ignore connection failures in the preconnect mode
 -I, --inventory=FILTER
                   Use FILTER expression to select hosts from inventory
 -k, --key=PRIVATE_KEY
                   Use this private key
 -l, --user=USER   Username for connections [$LOGNAME]
 -n, --showip      In the output show ips instead of names
 -o, --port=PORT   Port to connect to [22]
 -p, --par=PARALLELISM
                   How many hosts to run simultaneously [512]
 -P, --preconnect  Connect to all hosts before running command
 -T, --connect-timeout=TIMEOUT
                   Connect timeout in seconds [60]
 -t, --timeout=TIMEOUT
                   Run timeout in seconds
 -v, --verbose     Verbose output
$

      
      





La lista de hosts se puede especificar directamente en la línea de comando usando la opción -H (en el caso de varios hosts, deben estar separados por un espacio y la lista completa debe estar entre comillas como en los ejemplos siguientes) o cargado desde un archivo usando la opción -h. Las líneas del archivo que comienzan con # se ignoran. La dirección puede contener el puerto: my.host:2222. Puede utilizar la expansión de llaves: "host {1,3..5} .com" se convertirá en "host1.com host3.com host4.com host5.com". Tanto -H como -h se pueden usar varias veces.



Para la autorización se utilizará

  • la contraseña que ossh pedirá al usar la opción -A
  • conmutador ssh especificado por la opción -k
  • ssh-agent (en este caso, debe tener definida la variable de entorno SSH_AUTH_SOCK)


En ese orden.



A veces, debe asegurarse de poder iniciar sesión en todas las máquinas antes de ejecutar un comando. Hay una opción -P para esto. De forma predeterminada, si al menos una máquina no está disponible, ossh fallará. Si desea ignorar las conexiones fallidas, use la opción -i.



Ossh puede utilizar su sistema de inventario. Para ello, las rutas deben contener el comando ossh-Inventory, al que se le pasarán los parámetros de la opción -I. Esta opción se puede utilizar varias veces. El comando ossh-Inventory debería imprimir líneas en la salida estándar en el formato:

_ _
      
      





Donde machine_address puede ser el nombre dns o la dirección IP.



Los comandos que se ejecutarán se especifican mediante las opciones -C (leer del archivo) o -c (tomar de la línea de comandos). Estas opciones se pueden utilizar varias veces. Si tanto -C como -c están presentes, los comandos de los archivos se ejecutarán primero y luego desde la línea de comandos.



Además de simplemente ejecutar comandos usando ossh, puede transmitir registros en tiempo real:



$ ossh -H "web05 web06" -c "tail -f -c 0 /var/log/nginx/access.log|grep --line-buffered Wget"
web05 192.168.1.23 - - [22/Jun/2016:12:24:02 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"
web05 192.168.1.49 - - [22/Jun/2016:12:24:07 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"
web06 192.168.1.117 - - [22/Jun/2016:12:24:23 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"
web05 192.168.1.29 - - [22/Jun/2016:12:24:30 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"
...
      
      







Aquí hay una simulación de implementación continua:



$ ossh -p 1 -H "test0{1..3}" -c "sleep 10 && date"
test01 Wed Jun 22 12:38:24 PDT 2016
test02 Wed Jun 22 12:38:34 PDT 2016
test03 Wed Jun 22 12:38:44 PDT 2016
$
      
      







Se puede ver que los comandos se ejecutan secuencialmente en las máquinas. Solo una máquina está involucrada a la vez. Para una implementación real, "sleep 10 && date" debe reemplazarse por, por ejemplo, "apt-get install -y your_package".



Fue para la implementación que se escribió la primera versión de ossh. Alguien me preguntará por qué no usé algún sistema de administración de configuración generalmente aceptado. El caso es que fue en 2013 y usamos chef. Estaba claro que chef no nos convenía en particular con la incertidumbre de cuándo se aplicarían exactamente los cambios (el chef-cliente se ejecutaba cada 30 minutos). Para implementar cambios consistentemente en muchas máquinas, algunos desarrolladores utilizaron un truco sucio: el chef-cliente no funcionaba todo el tiempo, sino que se lanzaba una vez (a través de ssh) solo en el momento en que era necesario realizar la implementación. Ya en ese momento, se estaba trabajando para reemplazar el chef por sal, pero la transición no fue fácil y su finalización requirió tiempo adicional. Desarrollábamos un nuevo servicio,que requería implementaciones frecuentes y fue implementado por el único paquete Debian. Primero, usamos la utilidad de cuchillo del chef. Esta utilidad le permitió conectarse a través de ssh a los servidores deseados y ejecutar comandos en ellos. En algún momento, me di cuenta de que chef en este caso es un enlace adicional y escribí ossh.



Es importante tener en cuenta que ossh es una herramienta para resolver problemas a gran escala y no estándar. Si surge la necesidad de usar ossh con frecuencia, esta es una razón para pensar si le está yendo bien con la infraestructura y la administración del servidor. Aquí hay algunas situaciones en las que ossh me ayudó personalmente:

  • Una vez ordené /root/.ssh/authorized_keys en una gran cantidad de servidores (había alrededor de 7000 en ese momento). Los desarrolladores han registrado allí sus claves, en particular para los procesos de actualización de sus servicios. Era necesario obtener una lista de todas las claves utilizadas en todas las máquinas y asegurarse de que eliminar esas claves no sería catastrófico.
  • Por un segundo intercalar indoloro
  • Cuando estábamos luchando con TCP SACK PANIC , el sistema de administración de configuración implementó las reglas de iptables. Para asegurarme de que todo está bien, verifiqué las reglas correctas con ossh. Y no fue en vano en absoluto, había coches en los que no se aplicaban las reglas.
  • A veces tengo que crear entornos de prueba con cientos (y a veces miles) de máquinas. A menudo, estas máquinas están aisladas de la red de producción y no están disponibles para el sistema de gestión de configuración estándar. En situaciones como esta, la configuración de la máquina se puede realizar mediante ossh.


Preveo la pregunta de por qué no utilicé una solución lista para usar. Como mencioné anteriormente, la necesidad de ejecutar comandos en miles de máquinas se me ocurrió por primera vez en 2013. En ese momento, pude encontrar solo ssh paralelo, que no me convenía con lo siguiente:

  • No pude elevar el paralelismo por encima de 150, comenzaron a ocurrir errores al conectarme a servidores remotos
  • paralela ssh acumuló toda la salida y la volcó cuando terminó el comando. La transmisión de registros, por ejemplo, era imposible con su ayuda.
  • La salida ssh paralela fue (para mí personalmente) incómoda de analizar


Originalmente, ossh se escribió en ruby, para aumentar el rendimiento, usé la máquina de eventos y luego la fibra. Recientemente reescribí ossh para ir. Agradecería que los expertos (no estoy en este momento) lean mi código y señalen posibles formas de mejorarlo.



All Articles