使用bash的一次性虚拟机的KVM(低于)VDI

本文适用于谁?


面对创建“一次性”工作服务这一任务的系统管理员,本文可能会感兴趣。

序言


一个年轻的,动态发展的公司(具有小型区域网络)的IT支持部门被要求组织“自助服务站”,以供其外部客户使用。 该站数据应该用于在公司的外部门户网站上注册,从外部设备下载数据以及与政府门户网站一起使用。

一个重要的方面是,大多数软件在MS Windows下(例如,“声明”)已“锐化”,尽管朝着开放格式迈进,MS Office仍然是交换电子文档的主要标准。 因此,解决此问题时我们不能拒绝MS Windows。

主要问题是有可能从用户会话中收集各种数据,这可能导致它们泄露给第三方。 这种情况已经让MFC崩溃了 。 但是与准司法(国家自治机构)MFC不同,非国家组织将因此类缺陷而受到更多的惩罚。 下一个关键问题是需要使用外部存储介质,在这些存储介质上肯定会存在大量恶意软件。 由于通过白名单访问互联网受到限制,因此认为从Internet进入恶意软件的可能性较小,其他部门的员工共同制定了要求,提出要求和希望,最终要求如下:

IS要求

  • 使用后,应删除所有用户数据(包括临时文件和注册表项)。
  • 用户启动的所有过程都应在工作结束时完成。
  • 通过白名单访问Internet。
  • 对运行第三方代码的能力的限制。
  • 如果会话闲置超过5分钟,则会话应自动结束,工作站应执行清理操作。

客户要求

  • 每个分支的客户站数量不超过4。
  • 从我“坐在椅子上”到开始使用客户端软件的那一刻,系统就绪所需的最短等待时间。
  • 可以直接从“自助服务站”的安装站点连接外围设备(扫描仪,闪存驱动器)。
  • 客户的愿望
  • 大楼关闭时的广告资料(图片)演示。

创意面粉


在Windows livecd上玩了足够的游戏后,我们得出了一个一致的结论,即最终的解决方案至少不能满足3个关键点。 它们要么加载时间很长,要么加载时间不长,或者它们的自定义伴随着狂野的痛苦。 也许我们搜索不力,您可以建议一些工具,我将不胜感激。

此外,我们开始着眼于VDI,但是对于此任务,大多数解决方案要么太昂贵,要么需要密切关注。 我想要一个简单的工具,只需很少的魔力,只需重新启动/重新启动服务即可解决大多数问题。 幸运的是,我们从退役服务中获得了分支机构中低端级别的服务器设备,可以将其用作技术基础。

结果如何? 但是我无法告诉您最终发生了什么事情,因为NDA,但是在搜索过程中,我们开发了一个有趣的方案,尽管没有进行批量试验,但它在实验室测试中表现得很好。

几项免责声明:作者并没有声称所提出的解决方案完全解决了所有任务,并且自愿并随歌曲一起执行。 作者事先同意“英语(Sein English)sprache是​​zehr schlecht”的说法。 由于该解决方案不再开发,您无法指望错误修复或功能更改,一切都在您的手中。 作者假设您至少对KVM有点熟悉,并阅读了有关Spice协议的评论文章,并且您对Centos或其他GNU Linux发行版进行了一些工作。

在本文中,我想分析最终解决方案的主干,即客户端和服务器之间的交互以及所讨论解决方案框架内虚拟机生命周期中流程的本质。 如果这篇文章对公众感兴趣,我将描述为基于Fedora创建瘦客户机而实现实时映像的细节,并介绍调整虚拟机和KVM服务器以优化性能和安全性的细节。

如果您带彩色纸,
油漆,刷子和胶水,
还有更多的灵活性...
你可以赚一百卢布!

测试台的方案和说明




所有设备都位于分支网络内部,只有Internet通道断开。 从历史上看,已经有一个代理服务器,这不是什么特别的事情。 但是,除其他外,来自虚拟机的流量将被过滤掉(本文后面简称为VM)。 没有什么可以阻止将此服务放置在KVM服务器上的,您唯一需要注意的是磁盘子系统上来自该服务的负载如何变化。

客户站-实际上是我们服务的“自助服务站”,“前端”。 是Lenovo IdeaCentre的上网本。 这个单元有什么用? 是的,几乎每个人都对前面板上的大量USB连接器和读卡器感到特别满意。 在我们的方案中,将具有硬件写保护的SD卡插入读卡器中,上面记录了Fedora 28的修改后的实时图像,当然,监视器,键盘和鼠标也连接到了上网本上。

开关-第二层的不起眼的硬件开关,它在服务器机房中,并且闪烁并带有指示灯。 除“自助服务站”的网络外,它未连接到任何网络。

KVM_Server是电路的核心,在台式测试中,具有8 GB RAM的Core 2 Quad Q9650可以自信地将3个Windows 10虚拟机拉上。 磁盘子系统-Adaptec 3405 2驱动器RAID 1 + SSD。 在至强1220的现场试验中,更严重的LSI 9260 + SSD轻松拉出5-6个VM。 我们将从退役的服务中获取服务器,因此不会有很多资本成本。 具有pool_Vm虚拟机池的KVM虚拟化系统已部署在该服务器上。

Vm是虚拟机,是我们服务的后端。 这是用户的工作。

Enp5s0是一个网络接口,它面向“自助服务站”,dhcpd,ntpd,httpd生活在其上的网络,并且xinetd侦听“信号”端口。

Lo0是回送伪接口。 标准。

Spice_console-一个非常有趣的事情,事实是,与传统的RDP不同,当您打开KVM + Spice协议捆绑包时,会出现一个附加实体-虚拟机控制台端口。 实际上,连接到此TCP端口后,我们得到了Vm控制台,而无需通过其网络接口连接到Vm。 与Vm的所有交互用于信号传输,服务器接管。 功能上最接近的模拟是IPKVM。 即 VM监视器的映像已传输到此端口,鼠标移动的数据也已传输到该端口,并且(最重要的是)通过Spice协议进行的交互使您可以将USB设备无缝重定向到虚拟机,就好像该设备已连接到Vm本身一样。 已测试闪存驱动器,扫描仪,网络摄像头。

Vnet0,virbr0和虚拟网卡Vm形成虚拟机网络。

如何运作


从客户站

从Fedora 28的修改后的实时映像以图形方式加载客户站,并通过dhcp从网络地址空间169.254.24.0/24接收IP地址。 在引导过程中,将创建防火墙规则,以允许连接“信号”和“香料”服务器端口。 下载完成后,工作站将等待客户端用户的授权。 用户授权后,将启动“ openbox”桌面管理器,并代表授权用户执行自动启动自动启动脚本。 除其他外,自动运行脚本运行remote.sh脚本。

$ HOME / .config / openbox /脚本/ 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 


client.conf文件的变量说明
server_ip-地址KVM_Server
vdi_signal_port-xinetd“坐在”的端口KVM_Server
vdi_spice_port-网络端口KVM_Server,连接请求将从网络端口KVM_Server重定向到远程查看器客户端到所选Vm的spice端口(详细信息如下)
animation_folder-用于演示废话动画的图像的文件夹
background_folder-备用演示文稿从中获取图像的文件夹。 本文下半部分将详细介绍动画。

remote.sh脚本从配置文件/etc/client.conf中获取设置,并使用nc连接到KVM服务器的“ vdi_signal_port”端口,并从服务器接收数据流,其中需要字符串“ RULE ADDED,CONNECT NOW”。 接收到所需的线路后,远程查看器进程将以信息亭模式启动,并建立与“ vdi_spice_port”服务器端口的连接。 脚本的执行被挂起,直到远程查看器执行结束。

由于服务器端的重定向,连接到“ vdi_spice_port”端口的远程查看器到达lo0接口的“ spice_console”端口,即 到虚拟机的控制台,用户的工作就直接进行了。 在等待连接时,以jpeg文件的幻灯片形式向用户显示废话动画,带有图片的目录的路径由配置文件中的animation_folder变量的值确定。

如果与虚拟机的“ spice_console”端口的连接丢失,这表明虚拟机已关闭/重新启动(即,用户会话的实际结束),则代表授权用户运行的所有进程都将终止,这将导致lightdm重新启动并返回到授权屏幕。

从KVM服务器的侧面


在网卡的“信号”端口上,enp5s0正在等待xinetd连接。 连接到“信号”端口后,xinetd会在不传递任何输入参数的情况下运行vm_manager.sh脚本,并将脚本结果重定向到Client Station的nc会话。

/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 = /主页/管理员/ 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


配置文件vm_manager.conf的变量说明
srv_scripts_dir-脚本位置文件夹vm_manager.sh,vm_connect.sh,vm_delete.sh,vm_create.sh,vm_clear.sh
srv_pool_size-Vm池大小
srv_start_port_pool-初始端口,之后将启动虚拟机控制台的香料端口
srv_tmp_dir-临时文件的文件夹
base_host-基本Vm(黄金映像),将从该基本Vm克隆到池中
input_iface-服务器的网络接口,面向客户端工作站
vdi_spice_port-服务器的网络端口,连接请求将从该服务器的网络端口从远程查看器客户端重定向到所选Vm的spice端口
count_conn_tryes-等待计时器,在此之后认为尚未与Vm建立连接(有关详细信息,请参阅vm_connect.sh)

vm_manager.sh脚本从vm_manager.conf文件中读取配置文件,并根据多个参数评估池中虚拟机的状态,即:已部署了多少个VM,是否有免费的干净VM。 为此,他读取了clear.list文件,其中包含“新创建”(请参见下面的VM创建周期)虚拟机的“ spice_console”端口号,并检查与它们的已建立连接。 如果检测到具有已建立网络连接的端口(绝对不应),则将显示警告,并将该端口转移到waste.list。如果从clear.list文件中找到当前没有连接的第一个端口,则vm_manager.sh调用vm_connect.sh脚本并通过他作为该端口号的参数。

/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 


vm_connect.sh脚本引入了防火墙规则,这些规则创建了enp5s0接口的服务器端口到位于lo0服务器接口上的VM的“ spice控制台端口”的重定向“ vdi_spice_port”,并作为启动参数传递。 该端口已转移到conn_wait.list,该VM被视为挂起连接。 已将规则添加,立即连接行发送到服务器“信号”端口上的Client Station会话,这是运行在其上的remote.sh脚本所期望的。 连接等待周期以尝试次数开始,尝试次数由配置文件中的变量“ count_conn_tryes”的值确定。 在nc会话中,每秒钟都会给出字符串“ RULE ADDED,CONNECT NOW”,并检查与“ spice_console”端口建立的连接。

如果由于设置的尝试次数而连接失败,则将spice_console端口传送回clear.list。vm_connect.sh的执行已完成,vm_manager.sh的执行已恢复,从而开始了清洁周期。

如果客户端工作站连接到lo0接口上的spice_console端口,则将删除在spice服务器端口和spice_console端口之间创建重定向的防火墙规则,并通过确定防火墙状态的机制进一步维护该连接。 如果连接断开,则重新连接到spice_console端口将失败。 spice_console端口已转移到waste.list,该VM被认为是脏的,并且如果不进行清理,就无法返回到干净的虚拟机池。 完成vm_connect.sh的执行,恢复执行vm_manager.sh,这将启动清理周期。

清理周期通过查看waste.list文件开始,将建立连接的虚拟机端口的spice_console编号传输到该文件中。 从列表中的每个spice_console端口确定活动连接的存在。 如果没有连接,则认为该虚拟机已不再使用,并且该端口已转移到recycle.list,并且开始删除该端口所属的虚拟机(请参见下文)的过程。 如果在端口上检测到活动的网络连接,则假定正在使用虚拟机,则不会对其执行任何操作。 如果未敲击该端口,则认为该VM已关闭且不再需要。 端口已转移到recycle.list,并且删除虚拟机的过程开始。 为此,将调用vm_delete.sh脚本,并将“ spice_console”号作为参数传输到VM端口,该脚本必须删除。

/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 


删除虚拟机是一项非常简单的操作,vm_delete.sh脚本确定拥有作为启动参数传递的端口的虚拟机的名称。 强制虚拟机停止,将虚拟机从虚拟机管理程序中删除,并删除该虚拟机的虚拟硬盘。 从recycle.list中删除了spice_console端口。 结束vm_delete.sh的执行,恢复执行vm_manager.sh

在从清单waste.list清除不必要的虚拟机的操作结束时,脚本vm_manager.sh开始在池中创建虚拟机的周期。

该过程从确定可用于托管的spice_console端口开始。 为此,根据配置文件“ srv_start_port_pool”的参数(该参数设置虚拟机池“ spice_console”的起始端口)和参数“ srv_pool_size”(确定虚拟机数量的限制),依次枚举所有可能的端口变量。 对于每个特定端口,将在clear.list,waste.list,conn_wait.list,recycle.list中进行搜索。 如果在这些文件中的任何一个文件中都找到了端口,则该端口被认为是繁忙的并且被跳过。 如果在指定文件中找不到该端口,则将其输入到recycle.list文件中,然后开始创建新虚拟机的过程。 为此,调用vm_create.sh脚本,您要为其创建VM的端口的spice_console号作为参数传递给该脚本。

/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 


创建新虚拟机的过程

vm_create.sh脚本从配置文件中读取变量“ base_host”的值,该变量确定将基于其进行克隆的示例虚拟机。 它从虚拟机监控程序数据库中卸载VM的xml配置,对VM磁盘映像执行一系列检查qcow,并在成功完成后为新VM和新VM的“链接克隆”磁盘映像创建xml配置文件。 然后,将新VM的xml配置加载到管理程序数据库中,然后VM启动。 spice_console端口从recycle.list转移到clear.list。 vm_create.sh的执行结束,并且vm_manager.sh的执行结束。
下次连接时,将从头开始。

对于紧急情况,该工具包包含脚本vm_clear.sh,该脚本会强制运行池中的所有VM,并通过将列表的值清零来删除它们。 在加载阶段调用它可以使您从头开始(在VDI下)。

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


在此,我想结束我的故事的第一部分。 以上内容足以让系统管理员尝试在业务中使用underVDI。 如果社区觉得这个话题很有趣,那么在第二部分中,我将讨论livecd Fedora的修改及其向自助服务亭的转变。

Source: https://habr.com/ru/post/zh-CN462203/


All Articles