KVM (تحت) VDI با ماشین های مجازی یکبار مصرف با استفاده از bash

این مقاله برای چه کسانی در نظر گرفته شده است؟

این مقاله ممکن است برای مدیران سیستم که با وظیفه ایجاد سرویس ایستگاه های کاری "یک بار مصرف" روبرو هستند، جالب باشد.

پیش نویس

از بخش پشتیبانی فناوری اطلاعات یک شرکت جوان در حال توسعه پویا با یک شبکه منطقه ای کوچک خواسته شد تا "ایستگاه های خود سرویس" را برای استفاده مشتریان خارجی خود سازماندهی کند. این ایستگاه ها قرار بود برای ثبت نام در پورتال های خارجی شرکت ها، دانلود داده ها از دستگاه های خارجی و کار با پورتال های دولتی استفاده شوند.

یکی از جنبه‌های مهم این واقعیت بود که بیشتر نرم‌افزارها برای MS Windows (مثلاً «اعلامیه») «تناسب شده‌اند» و با وجود حرکت به سمت فرمت‌های باز، MS Office استاندارد غالب برای تبادل اسناد الکترونیکی باقی مانده است. بنابراین، ما نمی‌توانستیم هنگام حل این مشکل از MS Windows خودداری کنیم.

مشکل اصلی امکان جمع آوری داده های مختلف از جلسات کاربر بود که می تواند منجر به نشت آنها به اشخاص ثالث شود. این وضعیت قبلاً MFC را شکست داده است. اما بر خلاف شبه دولتی (موسسه خودمختار دولتی) MFC، سازمان های غیردولتی برای چنین کاستی هایی مجازات بسیار شدیدتری خواهند داشت. مهم ترین مشکل بعدی، نیاز به کار با رسانه های ذخیره سازی خارجی بود که قطعاً حاوی یک دسته بدافزار مخرب است. احتمال واردات بدافزار از اینترنت به دلیل محدودیت‌های دسترسی به اینترنت با استفاده از لیست سفید آدرس‌ها کمتر امکان‌پذیر تلقی می‌شد. مثل این:

الزامات امنیت اطلاعات

  • پس از استفاده، تمام داده های کاربر (از جمله فایل های موقت و کلیدهای رجیستری) باید حذف شوند.
  • تمام فرآیندهای راه اندازی شده توسط کاربر باید پس از اتمام کار خاتمه یابد.
  • دسترسی به اینترنت با استفاده از لیست سفید آدرس ها.
  • محدودیت در توانایی اجرای کدهای شخص ثالث.
  • اگر جلسه بیش از 5 دقیقه بیکار باشد، جلسه باید به طور خودکار پایان یابد و ایستگاه باید خود را تمیز کند.

الزامات مشتری

  • تعداد ایستگاه های مشتری در هر شعبه بیش از 4 عدد نیست.
  • حداقل زمان انتظار برای آماده شدن سیستم، از لحظه "نشستن" تا شروع کار با نرم افزار مشتری.
  • امکان اتصال دستگاه های جانبی (اسکنر، فلش مموری) به طور مستقیم از محل نصب «ایستگاه سلف سرویس».
  • خواسته های مشتری
  • نمایش مطالب تبلیغاتی (تصاویر) در زمان بیکار بودن مجتمع.

دردهای خلاقیت

پس از بازی زیاد با livecds ویندوز، به اتفاق آرا به این نتیجه رسیدیم که راه حل حاصل حداقل 3 نقطه بحرانی را برآورده نمی کند. آنها یا زمان زیادی برای بارگیری نیاز دارند، یا کاملاً زنده نیستند، یا سفارشی سازی آنها با دردهای وحشی همراه بود. شاید ما خوب جستجو نکرده‌ایم، و شما می‌توانید مجموعه‌ای از ابزارها را توصیه کنید، سپاسگزار خواهم بود.

سپس ما شروع به بررسی VDI کردیم، اما برای این کار اکثر راه حل ها یا خیلی گران هستند یا نیاز به توجه دقیق دارند. اما من یک ابزار ساده می خواستم، با حداقل مقدار جادو، که اکثر مشکلات آن با یک راه اندازی مجدد / راه اندازی مجدد سرویس ساده حل می شد. خوشبختانه ما تجهیزات سرور کلاس پایین را در شعب از سرویس در حال از کار انداختن داشتیم که می‌توانستیم برای پایه فناوری استفاده کنیم.

آنچه در پایان اتفاق افتاد؟ اما نمی‌توانم به شما بگویم در نهایت چه اتفاقی افتاد، زیرا NDA است، اما در روند جستجو، طرح جالبی را ایجاد کردیم که در آزمایش‌های آزمایشگاهی خود را به خوبی نشان داد، اگرچه به تولید نرسید.

چند سلب مسئولیت: نویسنده ادعا نمی کند که راه حل پیشنهادی تمام مشکلات تعیین شده را به طور کامل حل می کند و این کار را داوطلبانه و با آهنگ انجام می دهد. نویسنده پیشاپیش با این جمله موافق است که Sein Englishe sprache zehr schlecht است. از آنجایی که راه حل دیگر توسعه نمی یابد، نمی توانید روی رفع اشکال یا تغییر در عملکرد حساب کنید، همه چیز در دستان شماست. نویسنده فرض می‌کند که شما حداقل تا حدودی با KVM آشنا هستید و یک مقاله مروری در مورد پروتکل Spice خوانده‌اید و کمی با Centos یا یکی دیگر از توزیع‌های لینوکس گنو کار کرده‌اید.

در این مقاله، می‌خواهم ستون فقرات راه‌حل به‌دست‌آمده، یعنی تعامل بین مشتری و سرور و ماهیت فرآیندها در چرخه زندگی ماشین‌های مجازی را در چارچوب راه‌حل مورد بررسی، تحلیل کنم. اگر مقاله مورد توجه عموم باشد، جزئیات پیاده سازی تصاویر زنده برای ایجاد تین کلاینت بر اساس فدورا را شرح خواهم داد و در مورد جزئیات راه اندازی ماشین های مجازی و سرور KVM برای بهینه سازی عملکرد و امنیت صحبت خواهم کرد.

اگر کاغذ رنگی بگیرید،
رنگ، قلم مو و چسب،
و کمی مهارت بیشتر...
می توانید صد روبل بسازید!

نمودار و توضیحات نیمکت آزمون

KVM (تحت) VDI با ماشین های مجازی یکبار مصرف با استفاده از bash

تمام تجهیزات در داخل شبکه شعبه قرار دارند، فقط کانال اینترنت به بیرون می رود. قبلاً یک سرور پراکسی از نظر تاریخی وجود داشته است؛ هیچ چیز خارق‌العاده‌ای نیست. اما در میان موارد دیگر، فیلتر کردن ترافیک از ماشین‌های مجازی (مخفف VMهای زیر در متن) روی آن است. هیچ چیزی مانع از قرار دادن این سرویس بر روی یک سرور KVM نیست؛ تنها چیزی که باید تماشا کنید این است که چگونه بارگذاری روی زیرسیستم دیسک از آن تغییر می کند.

Client Station در واقع یک "ایستگاه سلف سرویس" است که "انتخاب" خدمات ما است. آنها نت تاپ های Lenovo IdeaCentre هستند. چه چیزی در مورد این واحد خوب است؟ بله، تقریباً همه، مخصوصاً از تعداد زیاد کانکتورهای USB و کارت خوان در پانل جلویی راضی هستند. در طرح ما، یک کارت SD با فعال بودن حفاظت از نوشتن سخت افزاری در کارت خوان قرار می گیرد که روی آن یک تصویر زنده تغییر یافته از فدورا 28 ثبت می شود.البته یک مانیتور، صفحه کلید و ماوس به نت تاپ متصل هستند.

سوئیچ یک سوئیچ سخت افزاری سطح دوم غیرقابل توجه است که در اتاق سرور ایستاده و چراغ های چشمک زن است. به هیچ شبکه ای غیر از شبکه "ایستگاه های سلف سرویس" متصل نیست.

KVM_Server هسته اصلی این طرح است؛ در تست‌های بنچ، Core 2 Quad Q9650 با 8 گیگابایت رم با اطمینان از 3 ماشین مجازی با ویندوز 10 پشتیبانی می‌کند. زیرسیستم دیسک – adaptec 3405 2 disk Raid 1 + SSD. در تست های میدانی Xeon 1220، LSI 9260 + SSD جدی تر به راحتی از 5-6 VM پشتیبانی می کند. سرورها را از سرویس بازنشسته می‌گرفتیم؛ هزینه‌های سرمایه‌ای زیادی وجود نداشت. این سرور(ها) دارای یک سیستم مجازی سازی KVM است که با یک ماشین مجازی pool pool_Vm مستقر شده است.

Vm یک ماشین مجازی است، پشتیبان خدمات ما. جایی است که کار کاربر انجام می شود.

Enp5s0 یک رابط شبکه است که رو به شبکه "ایستگاه های خود سرویس"، dhcpd، ntpd، httpd زنده روی آن قرار دارد و xinetd به پورت "سیگنال" گوش می دهد.

Lo0 یک رابط شبه حلقه بک است. استاندارد.

Spice_console - یک چیز بسیار جالب، واقعیت این است که برخلاف RDP کلاسیک، وقتی بسته پروتکل KVM+Spice را گسترش می دهید، یک موجودیت اضافی ظاهر می شود - پورت کنسول ماشین مجازی. در واقع با اتصال به این پورت TCP، بدون نیاز به اتصال به Vm از طریق رابط شبکه، کنسول Vm را دریافت می کنیم. سرور تمام تعاملات با Vm را برای انتقال سیگنال به عهده می گیرد. نزدیکترین آنالوگ در عملکرد IPKVM است. آن ها تصویر مانیتور VM به این پورت منتقل می‌شود، اطلاعات مربوط به حرکت ماوس نیز به آن منتقل می‌شود و (مهمتر از همه) تعامل از طریق پروتکل Spice به شما این امکان را می‌دهد که دستگاه‌های USB را به طور یکپارچه به ماشین مجازی هدایت کنید، گویی این دستگاه متصل است. به خود Vm. تست شده برای درایوهای فلش، اسکنرها، دوربین های وب.

کارت های شبکه مجازی Vnet0، virbr0 و Vm شبکه ای از ماشین های مجازی را تشکیل می دهند.

چگونه کار می کند

از ایستگاه مشتری

ایستگاه مشتری در حالت گرافیکی از یک تصویر زنده تغییر یافته از فدورا 28 بوت می شود، یک آدرس IP از طریق dhcp از فضای آدرس شبکه 169.254.24.0/24 دریافت می کند. در طول فرآیند دانلود، قوانین فایروال ایجاد می‌شوند که به پورت‌های سیگنال و اسپیس سرور اجازه اتصال می‌دهند. پس از تکمیل دانلود، ایستگاه منتظر می ماند تا کاربر «کلینت» مجوز دهد. پس از مجوز کاربر، مدیر دسکتاپ "openbox" راه اندازی می شود و اسکریپت شروع خودکار از طرف کاربر مجاز اجرا می شود. از جمله موارد دیگر، اسکریپت autorun اسکریپت 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

شرح متغیرهای فایل client.conf
server_ip - آدرس سرور KVM
vdi_signal_port - پورت KVM_Server که xinetd روی آن قرار دارد
vdi_spice_port — پورت شبکه KVM_Server که از آن درخواست اتصال از کلاینت نمایشگر راه دور به پورت ادویه Vm اختصاصی هدایت می شود (جزئیات زیر)
animation_folder - پوشه ای که تصاویر نمایش انیمیشن مزخرف از آن گرفته شده است.
background_folder - پوشه ای که تصاویر از آن برای نمایش ارائه ها در حالت آماده به کار گرفته می شود. توضیحات بیشتر در مورد انیمیشن در قسمت بعدی مقاله.

اسکریپت remote.sh تنظیمات را از فایل پیکربندی /etc/client.conf می گیرد و از nc برای اتصال به پورت "vdi_signal_port" سرور KVM استفاده می کند و یک جریان داده را از سرور دریافت می کند که در میان آنها خطوط "RULE ADDED" را انتظار می رود. ، اکنون وصل شوید. هنگامی که رشته مورد نیاز دریافت می شود، فرآیند مشاهده از راه دور در حالت کیوسک راه اندازی می شود و اتصال به پورت سرور "vdi_spice_port" برقرار می شود. اجرای اسکریپت تا زمانی که نمایشگر از راه دور اجرا را به پایان برساند به حالت تعلیق در می آید.

نمایشگر راه دور که به پورت "vdi_spice_port" متصل می شود، به دلیل تغییر مسیر در سمت سرور، به پورت "spice_console" رابط lo0 می رسد، یعنی. به کنسول ماشین مجازی و کار کاربر به طور مستقیم رخ می دهد. در حین انتظار برای اتصال، انیمیشن مزخرف به کاربر نشان داده می شود، در قالب یک نمایش اسلاید از فایل های 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.confsrv_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

توضیح متغیرها در فایل پیکربندی 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 - رابط شبکه سرور، رو به Client Stations
vdi_spice_port - پورت شبکه سرور که از آن درخواست اتصال از مشتری بیننده راه دور به پورت ادویه Vm اختصاصی هدایت می شود.
count_conn_tryes - تایمر انتظار، پس از آن در نظر گرفته می شود که هیچ اتصالی به Vm رخ نداده است (برای جزئیات، به vm_connect.sh مراجعه کنید)

اسکریپت vm_manager.sh فایل پیکربندی را از فایل vm_manager.conf می‌خواند و وضعیت ماشین‌های مجازی موجود در استخر را بر اساس چندین پارامتر ارزیابی می‌کند، یعنی: چند ماشین مجازی مستقر شده‌اند، آیا ماشین‌های مجازی تمیز رایگان وجود دارد یا خیر. برای انجام این کار، فایل clear.list را می‌خواند که حاوی شماره پورت «spice_console» ماشین‌های مجازی «تازه‌ایجادشده» (در زیر چرخه ایجاد VM را ببینید) و ارتباط برقرار شده با آنها را بررسی می‌کند. هنگامی که یک پورت با اتصال شبکه ایجاد شده شناسایی می شود (که مطلقاً نباید باشد)، یک هشدار نمایش داده می شود و پورت به 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 قوانین فایروال را معرفی می کند که یک تغییر مسیر از پورت سرور "vdi_spice_port" رابط enp5s0 به "پورت کنسول ادویه" ماشین مجازی واقع در رابط سرور lo0 ایجاد می کند که به عنوان پارامتر راه اندازی ارسال می شود. پورت به conn_wait.list منتقل می شود، VM در نظر گرفته می شود که منتظر اتصال است. رشته "RULE ADDED, CONNECT NOW" به جلسه Client Station در پورت "signal" سرور ارسال می شود که توسط اسکریپت remote.sh در حال اجرا بر روی آن انتظار می رود. یک چرخه انتظار اتصال با تعداد تلاش تعیین شده توسط مقدار متغیر "count_conn_tryes" از فایل پیکربندی شروع می شود. هر ثانیه رشته "RULE ADDED, CONNECT NOW" به جلسه nc ارسال می شود و وجود یک اتصال برقرار شده به پورت "spice_console" بررسی می شود.

اگر برای تعداد تلاش های تعیین شده، هیچ اتصالی رخ ندهد، پورت "spice_console" به clear.list بازگردانده می شود. اجرای vm_connect.sh به پایان می رسد، اجرای vm_manager.sh از سر گرفته می شود که چرخه پاکسازی را شروع می کند.

اگر اتصال Client Station به پورت “spice_console” در رابط lo0 شناسایی شود، قوانین فایروال که تغییر مسیری بین پورت “spice” سرور و پورت “spice_console” ایجاد می‌کنند، حذف می‌شوند و اتصال بیشتر با استفاده از حالت فایروال حفظ می‌شود. مکانیسم تشخیص اگر اتصال قطع شود، امکان برقراری مجدد ارتباط با پورت "spice_console" وجود نخواهد داشت. پورت «spice_console» به waste.list منتقل می‌شود، VM «کثیف» در نظر گرفته می‌شود و نمی‌تواند بدون انجام تمیز کردن به مجموعه ماشین‌های مجازی «تمیز» بازگردد. اجرای vm_connect.sh به پایان می رسد و اجرای vm_manager.sh از سر گرفته می شود که چرخه پاکسازی را شروع می کند.

چرخه تمیز کردن با مشاهده فایل waste.list آغاز می شود، که در آن شماره پورت "spice_console" ماشین های مجازی که اتصال به آنها برقرار شده است، منتقل می شود. وجود یک اتصال فعال در هر پورت "spice_console" از لیست تعیین می شود. اگر اتصال وجود نداشته باشد، فرض بر این است که ماشین مجازی دیگر در حال استفاده نیست و پورت به recycle.list منتقل می‌شود و فرآیند حذف ماشین مجازی (به زیر مراجعه کنید) که این پورت به آن تعلق داشت آغاز می‌شود. اگر یک اتصال شبکه فعال در یک پورت شناسایی شود، ماشین مجازی در حال استفاده در نظر گرفته می شود و هیچ اقدامی روی آن انجام نمی شود. اگر پورت گوش نمی دهد، ماشین مجازی خاموش در نظر گرفته می شود و دیگر مورد نیاز نیست. پورت به recycle.list منتقل می شود و فرآیند حذف ماشین مجازی شروع می شود. برای انجام این کار، اسکریپت vm_delete.sh فراخوانی می شود که شماره “spice_console” به عنوان پارامتر به پورت ماشین مجازی که باید حذف شود، ارسال می شود.

/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 نام ماشین مجازی را که صاحب پورت ارسال شده به عنوان پارامتر راه اندازی است، تعیین می کند. VM مجبور می شود متوقف شود، VM از Hypervisor حذف می شود و هارد دیسک مجازی این VM حذف می شود. پورت "spice_console" از recycle.list حذف شده است. اجرای vm_delete.sh به پایان می رسد، اجرای vm_manager.sh از سر گرفته می شود

اسکریپت vm_manager.sh، پس از اتمام عملیات پاکسازی ماشین های مجازی غیر ضروری از لیست waste.list، چرخه ایجاد ماشین های مجازی در استخر را آغاز می کند.

این فرآیند با شناسایی پورت های "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 فراخوانی می شود که به عنوان پارامتر شماره پورت “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" را از فایل پیکربندی می‌خواند، که نمونه ماشین مجازی را تعیین می‌کند که بر اساس آن کلون ساخته می‌شود. پیکربندی xml ماشین مجازی را از پایگاه داده Hypervisor دانلود می‌کند، یک سری بررسی‌های qcow را روی تصویر دیسک VM انجام می‌دهد و پس از تکمیل موفقیت‌آمیز، یک فایل پیکربندی xml برای ماشین مجازی جدید و یک "کلون پیوندی" تصویر دیسک جدید ایجاد می‌کند. VM. پس از آن پیکربندی xml ماشین مجازی جدید در پایگاه داده Hypervisor بارگذاری می شود و VM راه اندازی می شود. پورت "spice_console" از recycle.list به clear.list منتقل می شود. اجرای vm_create.sh به پایان می رسد و اجرای vm_manager.sh به پایان می رسد.
دفعه بعد که وصل می شوید، همه چیز از ابتدا شروع می شود.

برای مواقع اضطراری، این کیت شامل اسکریپت vm_clear.sh است که به اجبار در تمام ماشین های مجازی از استخر اجرا می شود و آنها را حذف می کند و مقادیر لیست را به صفر می رساند. فراخوانی آن در مرحله راه‌اندازی به شما امکان می‌دهد (تحت) 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 فدورا و تبدیل آن به کیوسک صحبت خواهم کرد.

منبع: www.habr.com

اضافه کردن نظر