KVM (bajo) VDI con máquinas virtuales únicas que usan bash

¿Para quién es este artículo?


Este artículo puede ser de interés para los administradores de sistemas que se enfrentaron con la tarea de crear un servicio de trabajos "únicos".

Prologo


Se le pidió al departamento de soporte de TI de una empresa joven en desarrollo dinámico con una pequeña red regional que organizara "estaciones de autoservicio" para el uso de sus clientes externos. Se suponía que los datos de la estación se utilizarían para registrarse en los portales externos de la compañía, descargar datos de dispositivos externos y trabajar con portales gubernamentales.

Un aspecto importante fue el hecho de que la mayoría del software está "mejorado" en MS Windows (por ejemplo, "Declaración") y, a pesar del movimiento hacia formatos abiertos, MS Office sigue siendo el estándar dominante en el intercambio de documentos electrónicos. Por lo tanto, no podríamos rechazar MS Windows al resolver este problema.

El principal problema era la posibilidad de acumular diversos datos de las sesiones de los usuarios, lo que podría llevar a su filtración a terceros. Esta situación ya ha decepcionado al MFC . Pero a diferencia del MFC cuasijudicial (institución autónoma estatal), las organizaciones no estatales serán castigadas mucho más por tales deficiencias. El siguiente problema crítico fue el requisito de trabajar con medios de almacenamiento externos, en los cuales, por supuesto, habrá un montón de malware malicioso. La probabilidad de entrada de malware desde Internet se consideró menos probable debido a la restricción del acceso a Internet a través de una lista blanca de direcciones.Los empleados de otros departamentos se unieron para resolver los requisitos, hacer sus requisitos y deseos, los requisitos finales fueron los siguientes:

Requisitos de IS

  • Después del uso, se deben eliminar todos los datos del usuario (incluidos los archivos temporales y las claves de registro).
  • Todos los procesos iniciados por el usuario deben completarse al final del trabajo.
  • Acceso a Internet a través de una lista blanca de direcciones.
  • Restricciones sobre la capacidad de ejecutar código de terceros.
  • Si la sesión está inactiva durante más de 5 minutos, la sesión debe finalizar automáticamente, la estación debe realizar una limpieza.

Requisitos del cliente

  • El número de estaciones cliente por sucursal no es más de 4.
  • El tiempo mínimo de espera para la preparación del sistema, desde el momento en que "me senté en una silla" hasta el comienzo de trabajar con el software del cliente.
  • La capacidad de conectar dispositivos periféricos (escáneres, unidades flash) directamente desde el sitio de instalación de la "estación de autoservicio".
  • Deseos del cliente
  • Demostración de material publicitario (fotos) en el momento del cierre del complejo.

Harina de creatividad


Después de haber jugado lo suficiente con Windows LiveCD, llegamos a la conclusión unánime de que la solución resultante no satisface al menos 3 puntos críticos. Están cargados durante mucho tiempo, o no están realmente vivos, o su personalización se asoció con un dolor salvaje. Tal vez buscamos mal, y puede aconsejar un conjunto de algunas herramientas, se lo agradeceré.

Además, comenzamos a mirar hacia VDI, pero para esta tarea, la mayoría de las soluciones son demasiado caras o requieren mucha atención. Y quería una herramienta simple con una cantidad mínima de magia, la mayoría de los problemas podrían resolverse simplemente reiniciando / reiniciando el servicio. Afortunadamente, teníamos equipos de servidor, de clase baja en las sucursales, del servicio fuera de servicio, que podríamos utilizar para la base tecnológica.

Cual es el resultado? Pero no podré decirte lo que sucedió al final, porque el NDA, pero en el proceso de búsqueda, desarrollamos un esquema interesante que se mostró bien en las pruebas de laboratorio, aunque no entró en serie.

Algunos descargos de responsabilidad: el autor no afirma que la solución propuesta resuelva por completo todas las tareas y lo hace de forma voluntaria y con la canción. El autor está de acuerdo de antemano con la afirmación de que Sein Englishe sprache es zehr schlecht. Como la solución ya no se desarrolla, no puede contar con una corrección de errores o un cambio en la funcionalidad, todo está en sus manos. El autor asume que está al menos un poco familiarizado con KVM y lee un artículo de revisión sobre el protocolo Spice, y trabajó un poco con Centos u otra distribución GNU Linux.

En este artículo, me gustaría analizar la columna vertebral de la solución resultante, es decir, la interacción del cliente y el servidor y la esencia de los procesos en el ciclo de vida de las máquinas virtuales en el marco de la solución en cuestión. Si el artículo será interesante para el público, describiré los detalles de la implementación de imágenes en vivo para crear clientes ligeros basados ​​en Fedora y hablaré sobre los detalles de ajuste de máquinas virtuales y servidores KVM para optimizar el rendimiento y la seguridad.

Si tomas papel de color,
Pinturas, pinceles y pegamentos.
Y un poco más de destreza ...
¡Puedes hacer cien rublos!

Esquema y descripción del banco de pruebas.




Todo el equipo se encuentra dentro de la red de sucursales, solo se apaga el canal de Internet. Históricamente, ya ha habido un servidor proxy, no es nada extraordinario. Pero es en él, entre otras cosas, que se filtrará el tráfico de las máquinas virtuales (abreviatura VM más adelante en el texto). Nada impide colocar este servicio en el servidor KVM, lo único que debe observar es cómo cambia la carga del mismo en el subsistema de disco.

Estación del cliente: de hecho, "estaciones de autoservicio", "front-end" de nuestro servicio. Son nettops de Lenovo IdeaCentre. ¿Para qué sirve esta unidad? Sí, casi todos, especialmente satisfechos con la gran cantidad de conectores USB y lectores de tarjetas en el panel frontal. En nuestro esquema, se inserta una tarjeta SD con protección de escritura de hardware en el lector de tarjetas, en la cual se graba la imagen en vivo modificada de Fedora 28. Por supuesto, un monitor, teclado y mouse están conectados a la red.

Conmutador: un conmutador de hardware poco notable del segundo nivel, está en la sala de servidores y parpadea con luces. No está conectado a ninguna red, excepto a la red de "estaciones de autoservicio".

KVM_Server es el núcleo del circuito; en las pruebas de banco del Core 2 Quad Q9650 con 8 GB de RAM, con confianza sacó 3 máquinas virtuales de Windows 10 sobre sí mismo. Subsistema de disco: adaptatec 3405 2 unidades Raid 1 + SSD. En las pruebas de campo del Xeon 1220, el SSD LSI 9260 + más serio extrajo fácilmente 5-6 máquinas virtuales. Obtendríamos el servidor del servicio retirado, no habría muchos costos de capital. El sistema de virtualización KVM con pool_Vm virtual machine pool se implementa en este servidor (es).

Vm es una máquina virtual, el backend de nuestro servicio. Es el trabajo del usuario.

Enp5s0 es una interfaz de red que mira hacia la red de "estaciones de autoservicio", dhcpd, ntpd, httpd live on it, y xinetd escucha el puerto de "señal".

Lo0 es la pseudointerfaz de bucle invertido. Estándar.

Spice_console: una cosa muy interesante, el hecho es que, a diferencia del RDP clásico, cuando activa el paquete de protocolo KVM + Spice, aparece una entidad adicional: el puerto de la consola de la máquina virtual. De hecho, al conectarnos a este puerto TCP, obtenemos la consola Vm, sin la necesidad de conectarse a Vm a través de su interfaz de red. Toda la interacción con Vm para la transmisión de señal, el servidor se hace cargo. El análogo más cercano en función es IPKVM. Es decir La imagen del monitor VM se transfiere a este puerto, se le transmiten datos sobre el movimiento del mouse y (lo más importante) la interacción a través del protocolo Spice le permite redirigir sin problemas los dispositivos USB a la máquina virtual, como si este dispositivo estuviera conectado al Vm. Probado para unidades flash, escáneres, cámaras web.

Vnet0, virbr0 y tarjetas de red virtuales Vm forman una red de máquinas virtuales.

Como funciona


Desde la estación del cliente

La estación cliente arranca en modo gráfico desde la imagen en vivo modificada de Fedora 28, recibe la dirección IP por dhcp desde el espacio de direcciones de red 169.254.24.0/24. Durante el proceso de arranque, se crean reglas de firewall que permiten conexiones a los puertos del servidor "señal" y "spice". Una vez completada la descarga, la estación espera la autorización del usuario del Cliente. Después de la autorización del usuario, se inicia el administrador de escritorio "openbox" y el script de inicio automático se ejecuta en nombre del usuario autorizado. Entre otras cosas, el script de ejecución automática ejecuta el script remote.sh.

$ HOME / .config / openbox / scripts / remote.sh
#!/bin/sh server_ip=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "server_ip" \ |/usr/bin/cut -d "=" -f2) vdi_signal_port=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "vdi_signal_port" \ |/usr/bin/cut -d "=" -f2) vdi_spice_port=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "vdi_spice_port" \ |/usr/bin/cut -d "=" -f2) animation_folder=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "animation_folder" \ |/usr/bin/cut -d "=" -f2) process=/usr/bin/remote-viewer while true do if [ -z `/usr/bin/pidof feh` ] then /usr/bin/echo $animation_folder /usr/bin/feh -N -x -D1 $animation_folder & else /usr/bin/echo fi /usr/bin/nc -i 1 $server_ip $vdi_signal_port |while read line do if /usr/bin/echo "$line" |/usr/bin/grep "RULE ADDED, CONNECT NOW!" then /usr/bin/killall feh pid_process=$($process "spice://$server_ip:$vdi_spice_port" \ "--spice-disable-audio" "--spice-disable-effects=animation" \ "--spice-preferred-compression=auto-glz" "-k" \ "--kiosk-quit=on-disconnect" | /bin/echo $!) /usr/bin/wait $pid_process /usr/bin/killall -u $USER exit else /usr/bin/echo $line >> /var/log/remote.log fi done done 


/etc/client.conf
 server_ip=169.254.24.1 vdi_signal_port=5905 vdi_spice_port=5906 animation_folder=/usr/share/backgrounds/animation background_folder=/usr/share/backgrounds2/fedora-workstation 


Descripción de las variables del archivo client.conf
server_ip - dirección KVM_Server
vdi_signal_port - puerto KVM_Server en el que xinetd "se sienta"
vdi_spice_port: puerto de red KVM_Server, desde el cual la solicitud de conexión se redirigirá desde el cliente de visor remoto al puerto de especias del Vm seleccionado (detalles a continuación)
animation_folder - carpeta de donde se toman las imágenes para la animación de demostración de mierda
background_folder: la carpeta de la que se toman las imágenes para presentaciones en espera. Más sobre animación en la siguiente parte del artículo.

El script remote.sh toma la configuración del archivo de configuración /etc/client.conf y usa nc para conectarse al puerto "vdi_signal_port" del servidor KVM y recibe una secuencia de datos del servidor, entre los cuales espera la cadena "REGLA AGREGADA, CONECTE AHORA". Cuando se recibe la línea deseada, el proceso del visor remoto comienza en modo quiosco, estableciendo una conexión con el puerto del servidor "vdi_spice_port". La ejecución del script se suspende hasta el final de la ejecución del visor remoto.

El visor remoto que se conecta al puerto "vdi_spice_port", debido a una redirección en el lado del servidor, llega al puerto "spice_console" de la interfaz lo0, es decir a la consola de la máquina virtual y el trabajo del usuario se realiza directamente. Mientras espera la conexión, al usuario se le muestra animación de mierda, en forma de presentación de diapositivas de archivos jpeg, la ruta al directorio con imágenes está determinada por el valor de la variable animation_folder del archivo de configuración.

Si se pierde la conexión al puerto "spice_console" de la máquina virtual, lo que indica el apagado / reinicio de la máquina virtual (es decir, el final real de la sesión del usuario), todos los procesos que se ejecutan en nombre del usuario autorizado finalizan, lo que conduce al reinicio de lightdm y vuelve a la pantalla de autorización .

Del lado del servidor KVM


En el puerto de "señal" de la tarjeta de red, enp5s0 está esperando la conexión xinetd. Después de conectarse al puerto de "señal", xinetd ejecuta el script vm_manager.sh sin pasarle ningún parámetro de entrada y redirige el resultado del script a la sesión nc de Client Station.

/etc/xinetd.d/test-server
 service vdi_signal { port = 5905 socket_type = stream protocol = tcp wait = no user = root server = /home/admin/scripts_vdi_new/vm_manager.sh } 


/home/admin/scripts_vdi_new/vm_manager.sh
 #!/usr/bin/sh #<SET LOCAL VARIABLES FOR SCRIPT># SRV_SCRIPTS_DIR=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_scripts_dir" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR" export SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR SRV_POOL_SIZE=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_pool_size" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "SRV_POOL_SIZE=$SRV_POOL_SIZE" export "SRV_POOL_SIZE=$SRV_POOL_SIZE" SRV_START_PORT_POOL=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_start_port_pool" |/usr/bin/cut -d "=" -f2) /usr/bin/echo SRV_START_PORT_POOL=$SRV_START_PORT_POOL export SRV_START_PORT_POOL=$SRV_START_PORT_POOL SRV_TMP_DIR=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_tmp_dir" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "SRV_TMP_DIR=$SRV_TMP_DIR" export SRV_TMP_DIR=$SRV_TMP_DIR date=$(/usr/bin/date) #</SET LOCAL VARIABLES FOR SCRIPT># /usr/bin/echo "# $date START EXECUTE VM_MANAGER.SH #" make_connect_to_vm() { #<READING CLEAR.LIST AND CHECK PORT FOR NETWORK STATE># /usr/bin/echo "READING CLEAN.LIST AND CHECK PORT STATE" #<CHECK FOR NO ONE PORT IN CLEAR.LIST># if [ -z `/usr/bin/cat $SRV_TMP_DIR/clear.list` ] then /usr/bin/echo "NO AVALIBLE PORTS IN CLEAN.LIST FOUND" /usr/bin/echo "Will try to make housekeeper, and create new vm" make_housekeeper else #<MINIMUN ONE PORT IN CLEAR.LIST FOUND># /usr/bin/cat $SRV_TMP_DIR/clear.list |while read line do clear_vm_port=$(($line)) /bin/echo "FOUND PORT $clear_vm_port IN CLEAN.LIST. TRY NETSTAT" \ "CHECK FOR PORT=$clear_vm_port" #<NETSTAT LISTEN CHECK FOR PORT FROM CLEAN.LIST># if /usr/bin/netstat -lnt |/usr/bin/grep ":$clear_vm_port" > /dev/null then /bin/echo "$clear_vm_port IS LISTEN" #<PORT IS LISTEN. CHECK FOR IS CONNECTED NOW># if /usr/bin/netstat -nt |/usr/bin/grep ":$clear_vm_port" \ |/usr/bin/grep "ESTABLISHED" > /dev/null then #<PORT LISTEN AND ALREADY CONNECTED! MOVE PORT FROM CLEAR.LIST # TO WASTE.LIST># /bin/echo "$clear_vm_port IS ALREADY CONNECTED, MOVE PORT TO WASTE.LIST" /usr/bin/sed -i "/$clear_vm_port/d" $SRV_TMP_DIR/clear.list /usr/bin/echo $clear_vm_port >> $SRV_TMP_DIR/waste.list else #<PORT LISTEN AND NO ONE CONNECT NOW. MOVE PORT FROM CLEAR.LIST TO # CONN_WAIT.LIST AND CREATE IPTABLES RULES>## /usr/bin/echo "OK, $clear_vm_port IS NOT ALREADY CONNECTED" /usr/bin/sed -i "/$clear_vm_port/d" $SRV_TMP_DIR/clear.list /usr/bin/echo $clear_vm_port >> $SRV_TMP_DIR/conn_wait.list $SRV_SCRIPTS_DIR/vm_connect.sh $clear_vm_port #<TRY TO CLEAN VM IN WASTE.LIST AND CREATE NEW WM># /bin/echo "TRY TO CLEAN VM IN WASTE.LIST AND CREATE NEW VM" make_housekeeper /usr/bin/echo "# $date STOP EXECUTE VM_MANAGER.SH#" exit fi else #<PORT IS NOT A LISTEN. MOVE PORT FROM CLEAR.LIST TO WASTE.LIST># /bin/echo " "$clear_vm_port" is NOT LISTEN. REMOVE PORT FROM CLEAR.LIST" /usr/bin/sed -i "/$clear_vm_port/d" $SRV_TMP_DIR/clear.list /usr/bin/echo $clear_vm_port >> $SRV_TMP_DIR/waste.list make_housekeeper fi done fi } make_housekeeper() { /usr/bin/echo "=Execute housekeeper=" /usr/bin/cat $SRV_TMP_DIR/waste.list |while read line do /usr/bin/echo "$line" if /usr/bin/netstat -lnt |/usr/bin/grep ":$line" > /dev/null then /bin/echo "port_alive, vm is running" if /usr/bin/netstat -nt |/usr/bin/grep ":$line" \ |/usr/bin/grep "ESTABLISHED" > /dev/null then /bin/echo "port_in_use can't delete vm!!!" else /bin/echo "port_not in use. Deleting vm" /usr/bin/sed -i "/$line/d" $SRV_TMP_DIR/waste.list /usr/bin/echo $line >> $SRV_TMP_DIR/recycle.list $SRV_SCRIPTS_DIR/vm_delete.sh $line fi else /usr/bin/echo "posible vm is already off. Deleting vm" /usr/bin/echo "MOVE VM IN OFF STATE $line FROM WASTE.LIST TO" \ "RECYCLE.LIST AND DELETE VM" /usr/bin/sed -i "/$line/d" $SRV_TMP_DIR/waste.list /usr/bin/echo $line >> $SRV_TMP_DIR/recycle.list $SRV_SCRIPTS_DIR/vm_delete.sh "$line" fi done create_clear_vm } create_clear_vm() { /usr/bin/echo "=Create new VM=" while [ $SRV_POOL_SIZE -gt 0 ] do new_vm_port=$(($SRV_START_PORT_POOL+$SRV_POOL_SIZE)) /usr/bin/echo "new_vm_port=$new_vm_port" if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/clear.list > /dev/null then /usr/bin/echo "$new_vm_port port is already defined in clear.list" else if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/waste.list > /dev/null then /usr/bin/echo "$new_vm_port port is already defined in waste.list" else if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/recycle.list > /dev/null then /usr/bin/echo "$new_vm_port PORT IS ALREADY DEFINED IN RECYCLE LIST" else if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/conn_wait.list > /dev/null then /usr/bin/echo "$new_vm_port PORT IS ALREADY DEFINED IN CONN_WAIT LIST" else /usr/bin/echo "PORT IN NOT DEFINED IN NO ONE LIST WILL CREATE" \ "VM ON PORT $new_vm_port" /usr/bin/echo $new_vm_port >> $SRV_TMP_DIR/recycle.list $SRV_SCRIPTS_DIR/vm_create.sh $new_vm_port fi fi fi fi SRV_POOL_SIZE=$(($SRV_POOL_SIZE-1)) done /usr/bin/echo "# $date STOP EXECUTE VM_MANAGER.SH #" } make_connect_to_vm |/usr/bin/tee -a /var/log/vm_manager.log 


/etc/vm_manager.conf
srv_scripts_dir = / home / admin / scripts_vdi_new
srv_pool_size = 4
srv_start_port_pool = 5920
srv_tmp_dir = / tmp / vm_state
base_host = win10_2
input_iface = enp5s0
vdi_spice_port = 5906
count_conn_tryes = 10


Descripción de las variables del archivo de configuración vm_manager.conf
srv_scripts_dir: carpeta de ubicación de script vm_manager.sh, vm_connect.sh, vm_delete.sh, vm_create.sh, vm_clear.sh
srv_pool_size - Tamaño del grupo de Vm
srv_start_port_pool: el puerto inicial, después del cual comenzarán los puertos de especias de las consolas de máquinas virtuales
srv_tmp_dir - carpeta para archivos temporales
base_host - base Vm (imagen dorada) desde la cual los clones Vm se convertirán en el grupo
input_iface - la interfaz de red del servidor, mirando hacia las estaciones de clientes
vdi_spice_port: el puerto de red del servidor desde el cual se redirigirá la solicitud de conexión desde el cliente de visor remoto al puerto de especias del Vm seleccionado
count_conn_tryes: un temporizador de espera, después del cual se considera que no se ha producido una conexión a Vm (para obtener más información, consulte vm_connect.sh)

El script vm_manager.sh lee el archivo de configuración del archivo vm_manager.conf, evalúa el estado de las máquinas virtuales en el grupo de acuerdo con varios parámetros, a saber: cuántas máquinas virtuales se implementan, si hay máquinas virtuales libres y limpias. Para hacer esto, lee el archivo clear.list que contiene los números de puertos "spice_console" de las máquinas virtuales "recién creadas" (vea el ciclo de creación de VM a continuación) y comprueba una conexión establecida con ellas. Si se detecta un puerto con una conexión de red establecida (que no debería serlo), se muestra una advertencia y el puerto se transfiere a waste.list Cuando se encuentra el primer puerto del archivo clear.list con el que actualmente no hay conexión, vm_manager.sh llama al script vm_connect.sh y pasa él como parámetro el número de este puerto.

/home/admin/scripts_vdi_new/vm_connect.sh
 #!/bin/sh date=$(/usr/bin/date) /usr/bin/echo "#" "$date" "START EXECUTE VM_CONNECT.SH#" #<SET LOCAL VARIABLES FOR SCRIPT># free_port="$1" input_iface=$(/usr/bin/cat /etc/vm_manager.conf |/usr/bin/grep "input_iface" \ |/usr/bin/cut -d "=" -f2) /usr/bin/echo "input_iface=$input_iface" vdi_spice_port=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "vdi_spice_port" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "vdi_spice_port=$vdi_spice_port" count_conn_tryes=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "count_conn_tryes" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "count_conn_tryes=$count_conn_tryes" #</SET LOCAL VARIABLES FOR SCRIPT># #<CREATE IPTABLES RULES AND SEND SIGNAL TO CONNECT># /usr/bin/echo "create rule for port" $free_port /usr/sbin/iptables -I INPUT -i $input_iface -p tcp -m tcp --dport \ $free_port -j ACCEPT /usr/sbin/iptables -I OUTPUT -o $input_iface -p tcp -m tcp --sport \ $free_port -j ACCEPT /usr/sbin/iptables -t nat -I PREROUTING -p tcp -i $input_iface --dport \ $vdi_spice_port -j DNAT --to-destination 127.0.0.1:$free_port /usr/bin/echo "RULE ADDED, CONNECT NOW!" #</CREATE IPTABLES RULES AND SEND SIGNAL TO CONNECT># #<WAIT CONNECT ESTABLISHED AND ACTIVATE CONNECT TIMER># while [ $count_conn_tryes -gt 0 ] do if /usr/bin/netstat -nt |/usr/bin/grep ":$free_port" \ |/usr/bin/grep "ESTABLISHED" > /dev/null then /bin/echo "$free_port NOW in use!!!" /usr/bin/sleep 1s /usr/sbin/iptables -t nat -D PREROUTING -p tcp -i $input_iface --dport \ $vdi_spice_port -j DNAT --to-destination 127.0.0.1:$free_port /usr/sbin/iptables -D INPUT -i $input_iface -p tcp -m tcp --dport \ $free_port -j ACCEPT /usr/sbin/iptables -D OUTPUT -o $input_iface -p tcp -m tcp --sport \ $free_port -j ACCEPT /usr/bin/sed -i "/$free_port/d" $SRV_TMP_DIR/conn_wait.list /usr/bin/echo $free_port >> $SRV_TMP_DIR/waste.list return else /usr/bin/echo "$free_port NOT IN USE" /usr/bin/echo "RULE ADDED, CONNECT NOW!" /usr/bin/sleep 1s fi count_conn_tryes=$((count_conn_tryes-1)) done #</WAIT CONNECT ESTABLISED AND ACTIVATE CONNECT TIMER># #<IF COUNT HAS EXPIRED. REMOVE IPTABLES RULE AND REVERT \ # VM TO CLEAR.LIST># /usr/bin/echo "REVERT IPTABLES RULE AND REVERT VM TO CLEAN \ LIST $free_port" /usr/sbin/iptables -t nat -D PREROUTING -p tcp -i $input_iface --dport \ $vdi_spice_port -j DNAT --to-destination 127.0.0.1:$free_port /usr/sbin/iptables -D INPUT -i $input_iface -p tcp -m tcp --dport $free_port \ -j ACCEPT /usr/sbin/iptables -D OUTPUT -o $input_iface -p tcp -m tcp --sport \ $free_port -j ACCEPT /usr/bin/sed -i "/$free_port/d" $SRV_TMP_DIR/conn_wait.list /usr/bin/echo $free_port >> $SRV_TMP_DIR/clear.list #</COUNT HAS EXPIRED. REMOVE IPTABLES RULE AND REVERT VM \ #TO CLEAR.LIST># /usr/bin/echo "#" "$date" "END EXECUTE VM_CONNECT.SH#" # Attention! Must Be! sysctl net.ipv4.conf.all.route_localnet=1 


El script vm_connect.sh introduce reglas de firewall que crean un "vdi_spice_port" de redireccionamiento del puerto del servidor de la interfaz enp5s0 al "puerto de consola de especias" de la VM ubicada en la interfaz del servidor lo0, que se pasa como parámetro de inicio. El puerto se transfiere a conn_wait.list, se considera que la VM está pendiente de conexión. La línea REGLA AGREGADA, CONECTAR AHORA se envía a la sesión de Client Station en el puerto de "señal" del servidor, que es lo que espera el script remote.sh que se ejecuta en él. Un ciclo de espera de conexión comienza con el número de intentos determinados por el valor de la variable "count_conn_tryes" del archivo de configuración. Cada segundo en la sesión nc, se le dará la cadena "REGLA AGREGADA, CONECTAR AHORA" y se comprobará la conexión establecida al puerto "spice_console".

Si la conexión falló durante el número establecido de intentos, el puerto spice_console se transfiere de nuevo a clear.list La ejecución de vm_connect.sh se completa, se reanuda la ejecución de vm_manager.sh, lo que inicia el ciclo de limpieza.

Si Client Station se conecta al puerto spice_console en la interfaz lo0, las reglas del firewall que crean una redirección entre el puerto del servidor spice y el puerto spice_console se eliminan y la conexión se mantiene mediante un mecanismo para determinar el estado del firewall. En el caso de una conexión desconectada, la reconexión al puerto spice_console fallará. El puerto spice_console se transfiere a waste.list, la VM se considera sucia y no puede volver al grupo de máquinas virtuales limpias sin pasar por la limpieza. Se completa la ejecución de vm_connect.sh, se reanuda la ejecución de vm_manager.sh, lo que inicia el ciclo de limpieza.

El ciclo de limpieza comienza observando el archivo waste.list, al que se transfieren los números de spice_console de los puertos de la máquina virtual a los que se establece la conexión. La presencia de una conexión activa se determina en cada puerto spice_console de la lista. Si no hay conexión, se considera que la máquina virtual ya no está en uso y el puerto se transfiere a recycle.list y se inicia el proceso de eliminación de la máquina virtual (ver más abajo) a la que pertenecía este puerto. Si se detecta una conexión de red activa en el puerto, se supone que se está utilizando la máquina virtual, no se toman medidas al respecto. Si no se toca el puerto, se supone que la VM está apagada y ya no es necesaria. El puerto se transfiere a recycle.list y se inicia el proceso de eliminación de la máquina virtual. Para hacer esto, se llama al script vm_delete.sh, al que se transfiere el número "spice_console" al puerto VM como parámetro, que debe eliminarse.

/home/admin/scripts_vdi_new/vm_delete.sh
 #!/bin/sh #<Set local VARIABLES># port_to_delete="$1" date=$(/usr/bin/date) #</Set local VARIABLES># /usr/bin/echo "# $date START EXECUTE VM_DELETE.SH#" /usr/bin/echo "TRY DELETE VM ON PORT: $vm_port" #<VM NAME SETUP># vm_name_part1=$(/usr/bin/cat /etc/vm_manager.conf |/usr/bin/grep 'base_host' \ |/usr/bin/cut -d'=' -f2) vm_name=$(/usr/bin/echo "$vm_name_part1""-""$port_to_delete") #</VM NAME SETUP># #<SHUTDOWN AND DELETE VM># /usr/bin/virsh destroy $vm_name /usr/bin/virsh undefine $vm_name /usr/bin/rm -f /var/lib/libvirt/images_write/$vm_name.qcow2 /usr/bin/sed -i "/$port_to_delete/d" $SRV_TMP_DIR/recycle.list #</SHUTDOWN AND DELETE VM># /usr/bin/echo "VM ON PORT $vm_port HAS BEEN DELETE AND REMOVE" \ "FROM RECYCLE.LIST. EXIT FROM VM_DELETE.SH" /usr/bin/echo "# $date STOP EXECUTE VM_DELETE.SH#" exit 


Eliminar una máquina virtual es una operación bastante trivial, el script vm_delete.sh determina el nombre de la máquina virtual que posee el puerto pasado como parámetro de inicio. La VM se ve obligada a detenerse, la VM se elimina del hipervisor y el disco duro virtual de esta VM se elimina. El puerto spice_console se elimina de recycle.list. Finaliza la ejecución de vm_delete.sh, se reanuda la ejecución de vm_manager.sh

El script vm_manager.sh, al final de las operaciones para limpiar máquinas virtuales innecesarias de la lista waste.list, inicia el ciclo de creación de máquinas virtuales en el grupo.

El proceso comienza con la determinación de los puertos spice_console disponibles para el alojamiento. Para hacer esto, según el parámetro del archivo de configuración "srv_start_port_pool" que establece el puerto de inicio para el grupo "spice_console" de máquinas virtuales y el parámetro "srv_pool_size", que determina el límite en el número de máquinas virtuales, todas las variantes de puerto posibles se ordenan secuencialmente. Para cada puerto específico, se busca en clear.list, waste.list, conn_wait.list, recycle.list. Si se encuentra un puerto en cualquiera de estos archivos, el puerto se considera ocupado y se omite. Si el puerto no se encuentra en los archivos especificados, se ingresa en el archivo recycle.list y comienza el proceso de creación de una nueva máquina virtual. Para hacer esto, se llama al script vm_create.sh al que se pasa como parámetro el número spice_console del puerto para el que desea crear una VM.

/home/admin/scripts_vdi_new/vm_create.sh
 #!/bin/sh /usr/bin/echo "#" "$date" "START RUNNING VM_CREATE.SH#" new_vm_port=$1 date=$(/usr/bin/date) a=0 /usr/bin/echo SRV_TMP_DIR=$SRV_TMP_DIR #<SET LOCAL VARIABLES FOR SCRIPT># base_host=$(/usr/bin/cat /etc/vm_manager.conf |/usr/bin/grep "base_host" \ |/usr/bin/cut -d "=" -f2) /usr/bin/echo "base_host=$base_host" #</SET LOCAL VARIABLES FOR SCRIPT># hdd_image_locate() { /bin/echo "Run STEP 1 - hdd_image_locate" hdd_base_image=$(/usr/bin/virsh dumpxml $base_host \ |/usr/bin/grep "source file" |/usr/bin/grep "qcow2" |/usr/bin/head -n 1 \ |/usr/bin/cut -d "'" -f2) if [ -z "$hdd_base_image" ] then /bin/echo "base hdd image not found!" else /usr/bin/echo "hdd_base_image found is a $hdd_base_image. Run next step 2" #< CHECK FOR SNAPSHOT ON BASE HDD ># if [ 0 -eq `/usr/bin/qemu-img info "$hdd_base_image" | /usr/bin/grep -c "Snapshot"` ] then /usr/bin/echo "base image haven't snapshot, run NEXT STEP 3" else /usr/bin/echo "base hdd image have a snapshot, can't use this image" exit fi #</ CHECK FOR SNAPSHOT ON BASE HDD ># #< CHECK FOR HDD IMAGE IS LINK CLONE ># if [ 0 -eq `/usr/bin/qemu-img info "$hdd_base_image" |/usr/bin/grep -c "backing file" then /usr/bin/echo "base image is not a linked clone, NEXT STEP 4" /usr/bin/echo "Base image check complete!" else /usr/bin/echo "base hdd image is a linked clone, can't use this image" exit fi fi #</ CHECK FOR HDD IMAGE IS LINK CLONE ># cloning } cloning() { # <Step_1 turn the base VM off ># /usr/bin/virsh shutdown $base_host > /dev/null 2>&1 # </Step_1 turn the base VM off ># #<Create_vm_config># /usr/bin/echo "Free port for Spice VM is $new_vm_port" #<Setup_name_for_new_VM># new_vm_name=$(/bin/echo $base_host"-"$new_vm_port) #</Setup_name_for_new_VM># #<Make_base_config_as_clone_base_VM># /usr/bin/virsh dumpxml $base_host > $SRV_TMP_DIR/$new_vm_name.xml #<Make_base_config_as_clone_base_VM># ##<Setup_New_VM_Name_in_config>## /usr/bin/sed -i "s%<name>$base_host</name>%<name>$new_vm_name</name>%g" $SRV_TMP_DIR/$new_vm_name.xml #</Setup_New_VM_Name_in_config># #<UUID Changing># old_uuid=$(/usr/bin/cat $SRV_TMP_DIR/$new_vm_name.xml |/usr/bin/grep "<uuid>") /usr/bin/echo old UUID $old_uuid new_uuid_part1=$(/usr/bin/echo "$old_uuid" |/usr/bin/cut -d "-" -f 1,2) new_uuid_part2=$(/usr/bin/echo "$old_uuid" |/usr/bin/cut -d "-" -f 4,5) new_uuid=$(/bin/echo $new_uuid_part1"-"$new_vm_port"-"$new_uuid_part2) /usr/bin/echo $new_uuid /usr/bin/sed -i "s%$old_uuid%$new_uuid%g" $SRV_TMP_DIR/$new_vm_name.xml #</UUID Changing># #<Spice port replace># old_spice_port=$(/usr/bin/cat $SRV_TMP_DIR/$new_vm_name.xml \ |/usr/bin/grep "graphics type='spice' port=") /bin/echo old spice port $old_spice_port new_spice_port=$(/usr/bin/echo "<graphics type='spice' port='$new_vm_port' autoport='no' listen='127.0.0.1'>") /bin/echo $new_spice_port /usr/bin/sed -i "s%$old_spice_port%$new_spice_port%g" $SRV_TMP_DIR/$new_vm_name.xml #</Spice port replace># #<MAC_ADDR_GENERATE># mac_new=$(/usr/bin/hexdump -n6 -e '/1 ":%02X"' /dev/random|/usr/bin/sed s/^://g) /usr/bin/echo New Mac is $mac_new #</MAC_ADDR_GENERATE># #<GET OLD MAC AND REPLACE># mac_old=$(/usr/bin/cat $SRV_TMP_DIR/$new_vm_name.xml |/usr/bin/grep "mac address=") /usr/bin/echo old mac is $mac_old /usr/bin/sed -i "s%$mac_old%$mac_new%g" $SRV_TMP_DIR/$new_vm_name.xml #<GET OLD MAC AND REPLACE># #<new_disk_create># /usr/bin/qemu-img create -f qcow2 -b $hdd_base_image /var/lib/libvirt/images_write/$new_vm_name.qcow2 #</new_disk_create># #<attach_new_disk_in_confiig># /usr/bin/echo hdd base image is $hdd_base_image /usr/bin/sed -i "s%<source file='$hdd_base_image'/>%<source file='/var/lib/libvirt/images_write/$new_vm_name.qcow2'/>%g" $SRV_TMP_DIR/$new_vm_name.xml #</attach_new_disk_in_confiig># starting_vm #</Create_vm config># } starting_vm() { /usr/bin/virsh define $SRV_TMP_DIR/$new_vm_name.xml /usr/bin/virsh start $new_vm_name while [ $a -ne 1 ] do if /usr/bin/virsh list --all |/usr/bin/grep "$new_vm_name" |/usr/bin/grep "running" > /dev/null 2>&1 then a=1 /usr/bin/sed -i "/$new_vm_port/d" $SRV_TMP_DIR/recycle.list /usr/bin/echo $new_vm_port >> $SRV_TMP_DIR/clear.list /usr/bin/echo "#" "$date" "VM $new_vm_name IS STARTED #" else /usr/bin/echo "#VM $new_vm_name is not ready#" a=0 /usr/bin/sleep 2s fi done /usr/bin/echo "#$date EXIT FROM VM_CREATE.SH#" exit } hdd_image_locate 


El proceso de crear una nueva máquina virtual

El script vm_create.sh lee del archivo de configuración el valor de la variable "base_host" que determina la máquina virtual de muestra en función de la cual se realizará el clon. Descarga la configuración xml de la VM de la base de datos del hipervisor, realiza una serie de comprobaciones qcow de la imagen de disco de la VM y, una vez finalizado con éxito, crea el archivo de configuración xml para la nueva VM y la imagen de disco de "clon vinculado" de la nueva VM. Después de eso, la configuración xml de la nueva VM se carga en la base de datos del hipervisor y se inicia la VM. El puerto spice_console se transfiere de recycle.list a clear.list. La ejecución de vm_create.sh finaliza y la ejecución de vm_manager.sh finaliza.
La próxima vez que te conectes, comienza desde el principio.

Para casos de emergencia, el kit incluye un script vm_clear.sh que ejecuta por la fuerza todas las máquinas virtuales del grupo y las elimina al poner a cero los valores de las listas. Llamarlo en la etapa de carga le permite iniciar (bajo) VDI desde cero.

/home/admin/scripts_vdi_new/vm_clear.sh
 #!/usr/bin/sh #set VARIABLES# SRV_SCRIPTS_DIR=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_scripts_dir" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR" export SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR SRV_TMP_DIR=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_tmp_dir" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "SRV_TMP_DIR=$SRV_TMP_DIR" export SRV_TMP_DIR=$SRV_TMP_DIR SRV_POOL_SIZE=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_pool_size" |/usr/bin/cut -d "=" -f2) /usr/bin/echo "SRV_POOL_SIZE=$SRV_POOL_SIZE" SRV_START_PORT_POOL=$(/usr/bin/cat /etc/vm_manager.conf \ |/usr/bin/grep "srv_start_port_pool" |/usr/bin/cut -d "=" -f2) /usr/bin/echo SRV_START_PORT_POOL=$SRV_START_PORT_POOL #Set VARIABLES# /usr/bin/echo "= Cleanup ALL VM=" /usr/bin/mkdir $SRV_TMP_DIR /usr/sbin/service iptables restart /usr/bin/cat /dev/null > $SRV_TMP_DIR/clear.list /usr/bin/cat /dev/null > $SRV_TMP_DIR/waste.list /usr/bin/cat /dev/null > $SRV_TMP_DIR/recycle.list /usr/bin/cat /dev/null > $SRV_TMP_DIR/conn_wait.list port_to_delete=$(($SRV_START_PORT_POOL+$SRV_POOL_SIZE)) while [ "$port_to_delete" -gt "$SRV_START_PORT_POOL" ] do $SRV_SCRIPTS_DIR/vm_delete.sh $port_to_delete port_to_delete=$(($port_to_delete-1)) done /usr/bin/echo "= EXIT FROM VM_CLEAR.SH=" 


Sobre esto me gustaría terminar la primera parte de mi historia. Lo anterior debería ser suficiente para que los administradores de sistemas prueben bajo VDI en los negocios. Si la comunidad encuentra este tema interesante, en la segunda parte hablaré sobre la modificación de livecd Fedora y su transformación en un quiosco.

Source: https://habr.com/ru/post/462203/


All Articles