KVM (sous) VDI avec des machines virtuelles uniques utilisant bash

À qui s'adresse cet article?


Cet article peut intéresser les administrateurs système qui ont été confrontés à la tâche de créer un service de tâches «ponctuel».

Prologue


Le département de support informatique d'une jeune entreprise en développement dynamique avec un petit réseau régional a été chargé d'organiser des «stations libre-service» à l'usage de leurs clients externes. Les données de la station étaient censées être utilisées pour l'enregistrement sur les portails externes de l'entreprise, le téléchargement de données à partir d'appareils externes et la collaboration avec les portails gouvernementaux.

Un aspect important a été le fait que la plupart des logiciels sont «affinés» pour MS Windows (par exemple, «Déclaration»), et malgré l'évolution vers des formats ouverts, MS Office reste la norme dominante dans l'échange de documents électroniques. Ainsi, nous ne pouvions pas refuser MS Windows lors de la résolution de ce problème.

Le principal problème était la possibilité d'accumuler diverses données des sessions des utilisateurs, ce qui pouvait entraîner leur fuite à des tiers. Cette situation a déjà laissé tomber le MFC . Mais contrairement au MFC quasi judiciaire (institution autonome de l'État), les organisations non étatiques seront beaucoup plus punies pour de telles lacunes. Le prochain problème critique était la nécessité de travailler avec des supports de stockage externes, sur lesquels, par tous les moyens, il y aura un tas de logiciels malveillants. La probabilité d'entrée de logiciels malveillants à partir d'Internet a été jugée moins probable en raison de la restriction de l'accès à Internet via une liste blanche d'adresses. Les employés d'autres départements se sont associés pour définir les exigences, formuler leurs exigences et leurs souhaits, les exigences finales étaient les suivantes:

Exigences SI

  • Après utilisation, toutes les données utilisateur (y compris les fichiers temporaires et les clés de registre) doivent être supprimées.
  • Tous les processus lancés par l'utilisateur doivent être terminés à la fin du travail.
  • Accès à Internet via une liste blanche d'adresses.
  • Restrictions sur la possibilité d'exécuter du code tiers.
  • Si la session est inactive pendant plus de 5 minutes, la session doit se terminer automatiquement, la station doit effectuer un nettoyage.

Besoins du client

  • Le nombre de postes clients par succursale ne dépasse pas 4.
  • Le temps d'attente minimum pour la préparation du système, à partir du moment où je me suis «assis sur une chaise» jusqu'au début du travail avec le logiciel client.
  • La possibilité de connecter des périphériques (scanners, lecteurs flash) directement depuis le site d'installation de la «station libre-service».
  • Souhaits du client
  • Démonstration de matériel publicitaire (photos) lors de la fermeture du complexe.

Farine de créativité


Ayant suffisamment joué avec Windows Live, nous sommes arrivés à la conclusion unanime que la solution résultante ne satisfait pas au moins 3 points critiques. Ils sont soit chargés pendant longtemps, soit pas tout à fait vivants, ou leur personnalisation a été associée à une douleur sauvage. Peut-être que nous avons mal recherché, et vous pouvez conseiller un ensemble d'outils, je vous en serai reconnaissant.

De plus, nous avons commencé à nous tourner vers le VDI, mais pour cette tâche, la plupart des solutions sont trop coûteuses ou nécessitent une attention particulière. Et je voulais un outil simple avec un minimum de magie, dont la plupart des problèmes pourraient être résolus en redémarrant / redémarrant simplement le service. Heureusement, nous avions du matériel serveur, bas de gamme dans les succursales, du service déclassé, que nous pouvions utiliser pour la base technologique.

Quel est le résultat? Mais je ne serai pas en mesure de vous dire ce qui s'est passé à la fin, parce que le NDA, mais en cours de recherche, nous avons développé un schéma intéressant qui s'est bien montré dans les tests de laboratoire, bien qu'il ne soit pas entré en série.

Quelques avertissements: l'auteur ne prétend pas que la solution proposée résout complètement toutes les tâches et le fait volontairement et avec la chanson. L'auteur souscrit à l'avance à l'affirmation selon laquelle Sein Englishe sprache est zehr schlecht. Comme la solution ne se développe plus, vous ne pouvez pas compter sur une correction de bogue ou un changement de fonctionnalité, tout est entre vos mains. L'auteur suppose que vous êtes au moins un peu familier avec KVM et que vous avez lu un article de synthèse sur le protocole Spice, et que vous avez un peu travaillé avec Centos ou une autre distribution GNU Linux.

Dans cet article, je voudrais analyser l'épine dorsale de la solution résultante, à savoir l'interaction du client et du serveur et l'essence des processus sur le cycle de vie des machines virtuelles dans le cadre de la solution en question. Si l'article sera intéressant pour le public, je décrirai les détails de l'implémentation d'images en direct pour créer des clients légers basés sur Fedora et raconterai les détails de l'optimisation des machines virtuelles et des serveurs KVM pour optimiser les performances et la sécurité.

Si vous prenez du papier de couleur,
Peintures, pinceaux et colle,
Et un peu plus de dextérité ...
Vous pouvez faire cent roubles!

Schéma et description du banc d'essai




Tous les équipements sont situés à l'intérieur du réseau de succursales, seul le canal Internet s'éteint. Historiquement, il y a déjà eu un serveur proxy, ce n'est rien d'extraordinaire. Mais c'est sur celui-ci, entre autres, que le trafic des machines virtuelles sera filtré (abréviation VM plus loin dans le texte). Rien n'empêche de placer ce service sur le serveur KVM, la seule chose à surveiller est de savoir comment sa charge sur le sous-système de disque change.

Station client - en fait, «stations libre-service», «front-end» de notre service. Sont des nettops de Lenovo IdeaCentre. À quoi sert cette unité? Oui, presque tout le monde, particulièrement satisfait du grand nombre de connecteurs USB et de lecteurs de cartes sur le panneau avant. Dans notre schéma, une carte SD avec protection matérielle en écriture est insérée dans le lecteur de carte, sur laquelle est enregistrée l'image en direct modifiée de Fedora 28. Bien sûr, un moniteur, un clavier et une souris sont connectés au nettop.

Switch - un commutateur matériel banal du deuxième niveau, il est dans la salle des serveurs et clignote avec des lumières. Il n'est connecté à aucun réseau à l'exception du réseau des «stations libre-service».

KVM_Server est le cœur du circuit; dans les tests de banc du Core 2 Quad Q9650 avec 8 Go de RAM, il a tiré en toute confiance 3 machines virtuelles Windows 10 sur lui-même. Sous-système de disque - adaptec 3405 2 disques Raid 1 + SSD. Lors des essais sur le terrain du Xeon 1220, le SSD LSI 9260 +, plus sérieux, a facilement tiré 5-6 VM. Nous obtiendrions le serveur du service retiré, il n'y aurait pas beaucoup de coûts en capital. Le système de virtualisation KVM avec pool de machines virtuelles pool_Vm est déployé sur ce ou ces serveurs.

VM est une machine virtuelle, le backend de notre service. C'est le travail de l'utilisateur.

Enp5s0 est une interface réseau qui regarde vers le réseau des "stations libre-service", dhcpd, ntpd, httpd en direct, et xinetd écoute le port "signal".

Lo0 est la pseudo-interface de bouclage. Standard.

Spice_console - Une chose très intéressante, le fait est que, contrairement au RDP classique, lorsque vous tournez le bundle de protocole KVM + Spice, une entité supplémentaire apparaît - le port de console de machine virtuelle. En fait, en se connectant à ce port TCP, on obtient la console Vm, sans avoir besoin de se connecter à Vm via son interface réseau. Toute interaction avec Vm pour la transmission du signal, le serveur prend le relais. La fonction analogique la plus proche est IPKVM. C'est-à-dire Une image d'un moniteur VM est transférée sur ce port, des données sur le mouvement de la souris lui sont transmises et (surtout) l'interaction via le protocole Spice vous permet de rediriger de manière transparente des périphériques USB vers une machine virtuelle, comme si ce périphérique était connecté à Vm lui-même. Testé pour les lecteurs flash, scanners, webcams.

Les cartes Vnet0, virbr0 et réseau virtuel Vm forment un réseau de machines virtuelles.

Comment ça marche


Depuis la station client

La station client est chargée en mode graphique à partir d'une image en direct modifiée de Fedora 28, reçoit l'adresse IP par DHCP à partir de l'espace d'adressage réseau 169.254.24.0/24. Pendant le processus de démarrage, des règles de pare-feu sont créées qui permettent des connexions aux ports de serveur «signal» et «pimenter». Une fois le téléchargement terminé, la station attend l'autorisation de l'utilisateur client. Après l'autorisation de l'utilisateur, le gestionnaire de bureau «openbox» est lancé et le script de démarrage automatique démarre automatiquement au nom de l'utilisateur autorisé. Entre autres choses, le script d'exécution automatique exécute le 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 


Description des variables du fichier client.conf
server_ip - adresse KVM_Server
vdi_signal_port - port KVM_Server sur lequel xinetd "se trouve"
vdi_spice_port - port réseau KVM_Server, à partir duquel la demande de connexion sera redirigée du client de visualisation à distance vers le port spice du Vm sélectionné (détails ci-dessous)
animation_folder - dossier dans lequel les images sont prises pour une animation de démonstration de conneries
background_folder - le dossier d'où les images sont prises pour les présentations en attente. Plus d'informations sur l'animation dans la prochaine partie de l'article.

Le script remote.sh prend les paramètres du fichier de configuration /etc/client.conf et utilise nc pour se connecter au port "vdi_signal_port" du serveur KVM et reçoit un flux de données du serveur, parmi lequel il attend la chaîne "RULE ADDED, CONNECT NOW". Lorsque la ligne souhaitée est reçue, le processus de visionneuse à distance démarre en mode kiosque, établissant une connexion au port du serveur «vdi_spice_port». L'exécution du script est suspendue jusqu'à la fin de l'exécution de la visionneuse à distance.

La visionneuse à distance se connectant au port "vdi_spice_port", en raison d'une redirection côté serveur, arrive au port "spice_console" de l'interface lo0 ie à la console de la machine virtuelle et le travail de l'utilisateur s'effectue directement. En attendant la connexion, l'utilisateur voit une animation de conneries, sous la forme d'un diaporama de fichiers jpeg, le chemin vers le répertoire avec des images est déterminé par la valeur de la variable animation_folder du fichier de configuration.

Si la connexion au port «spice_console» de la machine virtuelle est perdue, ce qui signale l'arrêt / redémarrage de la machine virtuelle (c'est-à-dire la fin réelle de la session utilisateur), tous les processus en cours d'exécution pour le compte de l'utilisateur autorisé sont interrompus, ce qui conduit au redémarrage de lightdm et revient à l'écran d'autorisation .

Du côté du serveur KVM


Sur le port «signal» de la carte réseau, enp5s0 attend la connexion xinetd. Après la connexion au port «signal», xinetd exécute le script vm_manager.sh sans lui passer de paramètres d'entrée et redirige le résultat du script vers la session nc de la station client.

/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


Description des variables du fichier de configuration vm_manager.conf
srv_scripts_dir - dossier d'emplacement de script vm_manager.sh, vm_connect.sh, vm_delete.sh, vm_create.sh, vm_clear.sh
srv_pool_size - Taille du pool Vm
srv_start_port_pool - le port initial, après quoi les ports spice des consoles de machines virtuelles commenceront
srv_tmp_dir - dossier pour les fichiers temporaires
base_host - base Vm (image dorée) à partir de laquelle les clones Vm seront transformés en pool
input_iface - l'interface réseau du serveur, en regardant vers les postes clients
vdi_spice_port - le port réseau du serveur à partir duquel la demande de connexion sera redirigée du client visualiseur distant vers le port spice du Vm sélectionné
count_conn_tryes - un temporisateur d'attente, après quoi il est considéré qu'une connexion à Vm ne s'est pas produite (pour plus de détails, voir vm_connect.sh)

Le script vm_manager.sh lit le fichier de configuration à partir du fichier vm_manager.conf, évalue l'état des machines virtuelles dans le pool en fonction de plusieurs paramètres, à savoir: combien de VM sont déployées, s'il existe des VM propres et libres. Pour ce faire, il lit le fichier clear.list qui contient le nombre de ports «spice_console» des machines virtuelles «nouvellement créées» (voir cycle de création de VM ci-dessous) et recherche une connexion établie avec eux. Si un port avec une connexion réseau établie est détecté (ce qui ne devrait absolument pas l'être), un avertissement s'affiche et le port est transféré vers waste.list Lorsque le premier port est trouvé dans le fichier clear.list avec lequel il n'y a actuellement aucune connexion, vm_manager.sh appelle le script vm_connect.sh et passe lui comme paramètre le numéro de ce port.

/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 


Le script vm_connect.sh introduit des règles de pare-feu qui créent une redirection "vdi_spice_port" du port de serveur de l'interface enp5s0 vers le "port de console spice" de la machine virtuelle située sur l'interface de serveur lo0, transmise comme paramètre de démarrage. Le port est transféré vers conn_wait.list, la machine virtuelle est considérée comme étant en attente de connexion. La ligne RULE ADDED, CONNECT NOW est envoyée à la session Client Station sur le port «signal» du serveur, ce qui est attendu par le script remote.sh qui s'exécute dessus. Un cycle d'attente de connexion commence par le nombre de tentatives déterminé par la valeur de la variable "count_conn_tryes" du fichier de configuration. Chaque seconde dans la session nc, la chaîne "RULE ADDED, CONNECT NOW" sera donnée et la connexion établie au port "spice_console" sera vérifiée.

Si la connexion a échoué pendant le nombre de tentatives défini, le port spice_console est retransféré vers clear.list L'exécution de vm_connect.sh est terminée, l'exécution de vm_manager.sh reprend, ce qui démarre le cycle de nettoyage.

Si la station cliente se connecte au port spice_console sur l'interface lo0, les règles de pare-feu qui créent une redirection entre le port du serveur spice et le port spice_console sont supprimées et la connexion est maintenue par un mécanisme permettant de déterminer l'état du pare-feu. En cas de connexion déconnectée, la reconnexion au port spice_console échouera. Le port spice_console est transféré vers waste.list, la machine virtuelle est considérée comme sale et elle ne peut pas retourner au pool de machines virtuelles propres sans passer par le nettoyage. L'exécution de vm_connect.sh est terminée, l'exécution de vm_manager.sh reprend, ce qui démarre le cycle de nettoyage.

Le cycle de nettoyage commence par regarder le fichier waste.list, dans lequel les numéros spice_console des ports de machine virtuelle vers lesquels la connexion est établie sont transférés. La présence d'une connexion active est déterminée sur chaque port spice_console de la liste. S'il n'y a pas de connexion, il est considéré que la machine virtuelle n'est plus utilisée et le port est transféré vers recycle.list et le processus de suppression de la machine virtuelle (voir ci-dessous) à laquelle ce port appartenait est démarré. Si une connexion réseau active est détectée sur le port, il est supposé que la machine virtuelle est utilisée, aucune action n'est entreprise pour elle. Si le port n'est pas exploité, il est supposé que la machine virtuelle est désactivée et n'est plus nécessaire. Le port est transféré vers recycle.list et le processus de suppression de la machine virtuelle démarre. Pour ce faire, le script vm_delete.sh est appelé, auquel le numéro "spice_console" est transféré au port VM en tant que paramètre, qui doit être supprimé.

/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 


La suppression d'une machine virtuelle est une opération assez banale, le script vm_delete.sh détermine le nom de la machine virtuelle qui possède le port transmis comme paramètre de démarrage. La machine virtuelle est obligée de s'arrêter, la machine virtuelle est supprimée de l'hyperviseur, le disque dur virtuel de cette machine virtuelle est supprimé. Le port spice_console est supprimé de recycle.list. L'exécution de vm_delete.sh se termine, l'exécution de vm_manager.sh reprend

Le script vm_manager.sh, à la fin des opérations de nettoyage des machines virtuelles inutiles de la liste waste.list, démarre le cycle de création de machines virtuelles dans le pool.

Le processus commence par la détermination des ports spice_console disponibles pour l'hébergement. Pour ce faire, en fonction du paramètre du fichier de configuration "srv_start_port_pool" qui définit le port de départ pour le pool "spice_console" des machines virtuelles et du paramètre "srv_pool_size", qui détermine la limite du nombre de machines virtuelles, toutes les variantes de port possibles sont triées séquentiellement. Pour chaque port spécifique, il est recherché dans clear.list, waste.list, conn_wait.list, recycle.list. Si un port est trouvé dans l'un de ces fichiers, le port est considéré comme occupé et est ignoré. Si le port n'est pas trouvé dans les fichiers spécifiés, il est entré dans le fichier recycle.list et le processus de création d'une nouvelle machine virtuelle commence. Pour ce faire, le script vm_create.sh est appelé auquel le numéro spice_console du port pour lequel vous souhaitez créer une VM est passé en paramètre.

/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 


Le processus de création d'une nouvelle machine virtuelle

Le script vm_create.sh lit dans le fichier de configuration la valeur de la variable "base_host" qui détermine l'exemple de machine virtuelle sur la base de laquelle le clone sera effectué. Décharge la configuration xml de la machine virtuelle de la base de données de l'hyperviseur, effectue une série de vérifications qcow de l'image disque de la machine virtuelle et, une fois terminée, crée le fichier de configuration xml pour la nouvelle machine virtuelle et l'image disque "clone lié" de la nouvelle machine virtuelle. Après cela, la configuration xml de la nouvelle machine virtuelle est chargée dans la base de données de l'hyperviseur et la machine virtuelle démarre. Le port spice_console est transféré de recycle.list vers clear.list. L'exécution de vm_create.sh se termine et l'exécution de vm_manager.sh se termine.
La prochaine fois que vous vous connectez, cela recommence depuis le début.

Pour les cas d'urgence, le kit comprend un script vm_clear.sh qui traverse de force toutes les machines virtuelles du pool et les supprime en mettant à zéro les valeurs des listes. L'appeler à l'étape de chargement vous permet de démarrer (sous) VDI à partir de zéro.

/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=" 


Sur ce, je voudrais terminer la première partie de mon histoire. Les éléments ci-dessus devraient suffire aux administrateurs système pour essayer underVDI en entreprise. Si la communauté trouve ce sujet intéressant, dans la seconde partie je parlerai de la modification de livecd Fedora et de sa transformation en kiosque.

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


All Articles