إضافة تلقائيا مساحة الخادم الظاهري

مرحبا بالجميع!


في هذه المقالة سوف نتحدث عن كيفية أتمتة مهمة توسيع مساحة القرص على أحد خوادمنا. وما هو الصعوبة في هذه المهمة البسيطة التي اضطررت إلى تنفيذها تلقائيًا - تسأل؟ لا شيء إذا كنت لا تستخدم تصاعد المدمجة المتتالية. أشعر أن هناك المزيد من الأسئلة! حسناً ، دعنا نذهب تحت القط.

أولاً ، سوف أخبرك عن سبب استخدامنا للتركيب المتكامل المتتالي.

لدينا نظام واحد يحتاج إلى تخزين للملفات الصغيرة (فحص المستندات ، إلخ). متوسط ​​حجم الملف هو من 200 كيلو بايت إلى 1 ميجابايت ، والبيانات ثابتة ، لا يتغير. هناك مليارات الملفات الموجودة فيه ويتزايد العدد كل يوم. مرة واحدة ، عندما كان حجم وحدة التخزين يتجاوز بالفعل 6 تيرابايت ، أدركنا أن المشاكل ستبدأ قريبًا ، أحدها كان وقت النسخ الاحتياطي والاسترداد. ثم قررنا تقسيم البيانات إلى أقراص ، وتم استدعاء UnionFS لمساعدتنا في ذلك.

تم تحديد الخوارزمية على النحو التالي: تتم كتابة البيانات على القرص لا يزيد عن 2 تيرابايت ، عندما ينتهي نضيف قرصًا جديدًا إلى الجهاز الظاهري ، وضع علامة عليه ، إضافته إلى UnionFS ، نقل القرص القديم إلى ReadOnly ، إزالة نسخة منه ، الكتابة إلى الشريط ، إزالته من النسخة الاحتياطية عبر الإنترنت.

كما فهمت بالفعل ، فإن هذه الخوارزمية تتطلب اهتمامًا كبيرًا من المسؤول - لا تتوفر أي حركة حرجة وتخزين. لذلك ، قررنا استبعاد العامل البشري تمامًا وتذكرنا أن لدينا ZABBIX ، والذي يمكنه التعامل جيدًا مع هذا الأمر بأنفسنا إذا أضفنا القليل من سحر PowerShell و Bash إلى الخوارزمية.

الآن حول كيفية القيام بذلك.

في Zabbix ، يتم تكوين مشغل للمساحة الحرة ويتم إنشاء زر للوضع اليدوي:

الصورة

عندما يتم تشغيل المشغل ، يتم تشكيل مهمة في sheduler لخادم الروبوت والتي توجد عليها جميع برامج التشغيل الآلي:

Powershell.exe "Enable-ScheduledTask \PROD_TASKS\Add_HDD_OS0226” 

في الوقت المحدد ، يتم تشغيل البرنامج النصي على الخادم والذي:

يضيف القرص إلى الجهاز المرغوب فيه:
(في نفس الوقت يختار الحجم الأكثر حرية)

 $vm = Get-VM -Name $vmName New-HardDisk -VM $vm -CapacityGB $newHDDCapacity -Datastore $datastoreName –ThinProvisioned 

يبحث عن تفاصيل وصول الخادم:
OFFTop
نستخدم مستودعًا مخصصًا لتفاصيل الوصول استنادًا إلى TeamPass ، لذلك يعثر البرنامج النصي على الخادم المطلوب في النظام ويتلقى تفاصيله تلقائيًا. يتم ذلك لأننا نقوم كل شهر بتغيير كل كلمات المرور تلقائيًا ، ولكن هذا موضوع مقالة منفصلة

 #Generate TeamPass API request string $vmTPReq = "   TeamPass" #Send request to TeamPass $vmCreds = Invoke-WebRequest($vmTPReq) -UseBasicParsing | ConvertFrom-Json #Convert credentials $credential = New-Object System.Management.Automation.PSCredential($vmCreds.login,(ConvertTo-SecureString $vmCreds.pw -asPlainText -Force)) 

يأتي على SSH:
 #Create partition & FS, mount disk to directory, edit fstab...etc. New-SSHSession -ComputerName $vmCreds.url -Credential $credential -Verbose -AcceptKey:$true $results = Invoke-SSHCommand -SessionId 0 -Command "/mnt/autodoit.sh" Remove-SSHSession -Index 0 -Verbose 

وضع علامة عليها وإضافتها إلى حامل UnionFS:
(autodoit.sh)
 #!/bin/bash fstab="/etc/fstab" newdisk=$(( ( parted -lm >&1 ) 1>/tmp/gethddlog ) 2>&1) newdisk=$(echo $newdisk | cut -d ':' -f 2) if [[ $newdisk == "" ]] ; then printf "New disk not found! Exit\n". exit fi printf "New disk found: $newdisk\n" echo #Create new partition echo Create new partition ... parted $newdisk mklabel gpt unit TB mkpart primary 0.00TB 2.00TB print sleep 10 #Create filesystem echo Create filesystem xfs ... newdisk="$newdisk$((1))" mkfs.xfs $newdisk #Create new DATA directory lastdata=$(ls /mnt/ | grep 'data[0-9]\+$' | cut -c5- | sort -n | tail -n1) lastdatamount="/mnt/data$((lastdata))" newdata="/mnt/data$((lastdata+1))" printf "Create new direcory: $newdata\n" mkdir $newdata chown -R nobody:nobody $newdata chmod -R 755 $newdata #Mount new partition to new directory printf "Mount new partition to $newdata...\n" mount -t xfs ${newdisk} ${newdata} #Get UUID of new partition uuid=$(blkid $newdisk -o value -s UUID) printf "New disk UUID: $uuid\n" #Add mountpoint for new partition printf "Add mountpoint for new disk to fstab...\n" lastdatamount=$(cat $fstab | grep "$lastdatamount\s") newdatamount="UUID=$uuid $newdata xfs defaults,nofail 0 0" ldm=$(echo $lastdatamount | sed -r 's/[\/]/\\\//g') ndm=$(echo $newdatamount | sed -r 's/[\/]/\\\//g') sed -i "/$ldm/a $ndm" $fstab #Change UnionFS mountpoint string printf "Modify mountpoint for UnionFS in fstab...\n" prevunion=$(cat $fstab | grep fuse.unionfs) newunion=$(echo $prevunion | sed -e "s/=rw/=ro/") newunion=$(echo $newdata=rw:$newunion) sed -i "s|$prevunion|$newunion|" $fstab #Remount UnionFS printf "Remount UnionFS...\n" service smb stop sleep 0.6 umount /mnt/unionfs mount /mnt/unionfs service smb start printf "Done!\n\n" rm /tmp/gethddlog 


لسوء الحظ ، في وقت كتابة هذا التقرير ، لم نحل العديد من المشكلات المتعلقة بالإنشاء التلقائي للمهام في VEEAM لأرشفة القرص القديم وكتابته على شريط ، لذلك لا يزال يتم ذلك يدويًا. ولكن سنقوم بالتأكيد بتحديث البرنامج النصي بمجرد أن نحل بعض المشاكل.

أرسلت بواسطة فيتالي روزمان ( PBCVIT ).

تم استعارة جزء من التعليمات البرمجية لمصفوفات الإلتصاق ، وتم حفظ روابط المؤلف في الكود.

النصي الكامل
 #Set VM name $vmName = "OS0226" #Set TeamPass ID of linux server $vmTPId = "1161" #Set capacity of new HDD in GB $newHDDCapacity = 2048 #Set Log file $logFile = "C:\SCRIPTS\Log\NewHardDisk-OS0226.log" #Import module for SSH connections Import-Module Posh-SSH #Add VEEAM Snap-In Add-PSSnapin VeeamPSSnapin #Initialize VMWare PowerCLI & 'C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1' #Add function for array join Function Join-Object { # https://powersnippets.com/join-object/ [CmdletBinding()]Param ( # Version 02.02.00, by iRon [Object[]]$RightTable, [Alias("Using")]$On, $Merge = @{}, [Parameter(ValueFromPipeLine = $True)][Object[]]$LeftTable, [String]$Equals ) $Type = ($MyInvocation.InvocationName -Split "-")[0] $PipeLine = $Input | ForEach {$_}; If ($PipeLine) {$LeftTable = $PipeLine} If ($LeftTable -eq $Null) {If ($RightTable[0] -is [Array]) {$LeftTable = $RightTable[0]; $RightTable = $RightTable[-1]} Else {$LeftTable = $RightTable}} $DefaultMerge = If ($Merge -is [ScriptBlock]) {$Merge; $Merge = @{}} ElseIf ($Merge."") {$Merge.""} Else {{$Left.$_, $Right.$_}} If ($Equals) {$Merge.$Equals = {If ($Left.$Equals -ne $Null) {$Left.$Equals} Else {$Right.$Equals}}} ElseIf ($On -is [String] -or $On -is [Array]) {@($On) | ForEach {If (!$Merge.$_) {$Merge.$_ = {$Left.$_}}}} $LeftKeys = @($LeftTable[0].PSObject.Properties | ForEach {$_.Name}) $RightKeys = @($RightTable[0].PSObject.Properties | ForEach {$_.Name}) $Keys = $LeftKeys + $RightKeys | Select -Unique $Keys | Where {!$Merge.$_} | ForEach {$Merge.$_ = $DefaultMerge} $Properties = @{}; $LeftOut = @($True) * @($LeftTable).Length; $RightOut = @($True) * @($RightTable).Length For ($LeftIndex = 0; $LeftIndex -lt $LeftOut.Length; $LeftIndex++) {$Left = $LeftTable[$LeftIndex] For ($RightIndex = 0; $RightIndex -lt $RightOut.Length; $RightIndex++) {$Right = $RightTable[$RightIndex] $Select = If ($On -is [String]) {If ($Equals) {$Left.$On -eq $Right.$Equals} Else {$Left.$On -eq $Right.$On}} ElseIf ($On -is [Array]) {($On | Where {!($Left.$_ -eq $Right.$_)}) -eq $Null} ElseIf ($On -is [ScriptBlock]) {&$On} Else {$True} If ($Select) {$Keys | ForEach {$Properties.$_ = If ($LeftKeys -NotContains $_) {$Right.$_} ElseIf ($RightKeys -NotContains $_) {$Left.$_} Else {&$Merge.$_} }; New-Object PSObject -Property $Properties; $LeftOut[$LeftIndex], $RightOut[$RightIndex] = $Null } } } If ("LeftJoin", "FullJoin" -Contains $Type) { For ($LeftIndex = 0; $LeftIndex -lt $LeftOut.Length; $LeftIndex++) { If ($LeftOut[$LeftIndex]) {$Keys | ForEach {$Properties.$_ = $LeftTable[$LeftIndex].$_}; New-Object PSObject -Property $Properties} } } If ("RightJoin", "FullJoin" -Contains $Type) { For ($RightIndex = 0; $RightIndex -lt $RightOut.Length; $RightIndex++) { If ($RightOut[$RightIndex]) {$Keys | ForEach {$Properties.$_ = $RightTable[$RightIndex].$_}; New-Object PSObject -Property $Properties} } } }; Set-Alias Join Join-Object Set-Alias InnerJoin Join-Object; Set-Alias InnerJoin-Object Join-Object -Description "Returns records that have matching values in both tables" Set-Alias LeftJoin Join-Object; Set-Alias LeftJoin-Object Join-Object -Description "Returns all records from the left table and the matched records from the right table" Set-Alias RightJoin Join-Object; Set-Alias RightJoin-Object Join-Object -Description "Returns all records from the right table and the matched records from the left table" Set-Alias FullJoin Join-Object; Set-Alias FullJoin-Object Join-Object -Description "Returns all records when there is a match in either left or right table" #Connect to vCenter Connect-VIServer vcenter.mmc.local #Get datastore $datastores = get-datastore | where-object Name -like "*TIERED_VM_PROD*" if ($datastores.Count -gt 0) { if (($datastores | Sort -Descending {$_.FreeSpaceGB})[0].FreeSpaceGB -gt 2048) { $datastoreName = ($datastores | Sort -Descending {$_.FreeSpaceGB})[0].Name } else { Write-Host("ERROR: No enought space on datastore for new HDD!") break } } else { Write-Host("ERROR: No Datastores found!") break } #Generate TeamPass API request string $vmTPReq = "   TeamPass" #Send request to TeamPass $vmCreds = Invoke-WebRequest($vmTPReq) -UseBasicParsing | ConvertFrom-Json #Convert credentials $credential = New-Object System.Management.Automation.PSCredential($vmCreds.login,(ConvertTo-SecureString $vmCreds.pw -asPlainText -Force)) if ((Test-Connection $vmCreds.url -Count 1 -Quiet) -eq $false) { $err = $error[0].FullyQualifiedErrorId } try { # Get disks information from Linux New-SSHSession -ComputerName $vmCreds.url -Credential $credential -Verbose -AcceptKey:$true $linuxCommand1 = 'ls -dl /sys/block/sd*/device/scsi_device/*' $linuxDisksData1 = Invoke-SSHCommand -SessionId 0 -Command $linuxCommand1 $linuxCommand2 = 'lsblk -l | grep /mnt' $linuxDisksData2 = Invoke-SSHCommand -SessionId 0 -Command $linuxCommand2 Remove-SSHSession -Index 0 -Verbose $linuxMounts = $linuxDisksData2.Output -replace '\s+', ' ' | Select @{N='Partition';E={($_.split(" ")[0])}}, @{N='linuxMount';E={($_.split(" ")[6])}} $linuxDisks = $linuxDisksData1.Output -replace '\s+', ' ' | Select @{N='Partition';E={($_.split(" ")[8]).split('/')[3]+'1'}}, @{N='SCSIAddr';E={(($_.split(" ")[8]).split('/')[6]).split(':')[1]+':'+(($_.split(" ")[8]).split('/')[6]).split(':')[2]}} $linuxDisks = $linuxDisks | sort SCSIAddr } catch { $err = $error[0].FullyQualifiedErrorId } #Get disks information from vmware $vmDisks = Get-VM -Name $vmName | Get-HardDisk | Select @{N='vmwareHardDisk';E={$_.Name}}, @{N='vSCSI';E={$_.uid.split("/")[3].split("=")[1]}}, @{N='SCSIAddr';E={[string]([math]::truncate((($_.uid.split("/")[3].split("=")[1])-2000)/16))+':'+[string]((($_.uid.split("/")[3].split("=")[1])-2000)%16)}} $vmDisks = $vmDisks | sort SCSIAddr #Get total information about VM Disks $OLAYtotalEffects = $vmDisks | InnerJoin $linuxDisks SCSIAddr -eq SCSIAddr | InnerJoin $linuxMounts Partition -eq Partition| sort vmwareHardDisk #Display total information about VM Disks $OLAYtotalEffects | ft $OLAYtotalEffects | ft 2>$logFile #Get latest mount $linuxLatestDiskMount = [string](($OLAYtotalEffects | select linuxMount | where linuxMount -like "/mnt/data*" | % {[int](($_.linuxMount.Split("/")[2]).Replace("data",""))} | Measure -Maximum).Maximum) #Get latest vSCSI number $latestDiskvSCSI = ($OLAYtotalEffects | where {$_.linuxMount -eq "/mnt/data$linuxLatestDiskMount"}).vSCSI #Add HDD to VM $vm = Get-VM -Name $vmName New-HardDisk -VM $vm -CapacityGB $newHDDCapacity -Datastore $datastoreName -ThinProvisioned #Let the disk takes root Write-Host("Let the disk takes root...") sleep 10 if ((Test-Connection $vmCreds.url -Count 1 -Quiet) -eq $false) { $err = $error[0].FullyQualifiedErrorId } try { #Create partition & FS, mount disk to directory, edit fstab...etc. New-SSHSession -ComputerName $vmCreds.url -Credential $credential -Verbose -AcceptKey:$true $results = Invoke-SSHCommand -SessionId 0 -Command "/mnt/autodoit.sh" Remove-SSHSession -Index 0 -Verbose $results.Output } catch { $err = $error[0].FullyQualifiedErrorId } #After adding a new disk, some checks are just performed if ((Test-Connection $vmCreds.url -Count 1 -Quiet) -eq $false) { $err = $error[0].FullyQualifiedErrorId } try { # Get disks information from Linux New-SSHSession -ComputerName $vmCreds.url -Credential $credential -Verbose -AcceptKey:$true $linuxCommand1 = 'ls -dl /sys/block/sd*/device/scsi_device/*' $linuxDisksData1 = Invoke-SSHCommand -SessionId 0 -Command $linuxCommand1 $linuxCommand2 = 'lsblk -l | grep /mnt' $linuxDisksData2 = Invoke-SSHCommand -SessionId 0 -Command $linuxCommand2 Remove-SSHSession -Index 0 -Verbose $linuxMounts = $linuxDisksData2.Output -replace '\s+', ' ' | Select @{N='Partition';E={($_.split(" ")[0])}}, @{N='linuxMount';E={($_.split(" ")[6])}} $linuxDisks = $linuxDisksData1.Output -replace '\s+', ' ' | Select @{N='Partition';E={($_.split(" ")[8]).split('/')[3]+'1'}}, @{N='SCSIAddr';E={(($_.split(" ")[8]).split('/')[6]).split(':')[1]+':'+(($_.split(" ")[8]).split('/')[6]).split(':')[2]}} $linuxDisks = $linuxDisks | sort SCSIAddr } catch { $err = $error[0].FullyQualifiedErrorId } #Get disks information from vmware $vmDisks = Get-VM -Name $vmName | Get-HardDisk | Select @{N='vmwareHardDisk';E={$_.Name}}, @{N='vSCSI';E={$_.uid.split("/")[3].split("=")[1]}}, @{N='SCSIAddr';E={[string]([math]::truncate((($_.uid.split("/")[3].split("=")[1])-2000)/16))+':'+[string]((($_.uid.split("/")[3].split("=")[1])-2000)%16)}} $vmDisks = $vmDisks | sort SCSIAddr #Get total information about VM Disks $OLAYtotalEffects = $vmDisks | InnerJoin $linuxDisks SCSIAddr -eq SCSIAddr | InnerJoin $linuxMounts Partition -eq Partition| sort vmwareHardDisk #Display total information about VM Disks $OLAYtotalEffects | ft $OLAYtotalEffects | ft 2>$logFile Disconnect-VIServer -Confirm:$false Disable-ScheduledTask \PROD_TASKS\Add_HDD_OS0226 


لا توجد شكاوى ضد UnionFS ؛ فهي تعمل بثبات لأكثر من عامين.

السؤال عن سبب تنظيم التخزين بشكل عام هو بلاغي عام ، فقط تقبله كما هو.

يرجى ملاحظة أنه يجب استخدام أنواع مختلفة من تعيين محرك الأقراص لأنظمة مختلفة. لذلك كن حذرًا وستأتي القوة معك.

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


All Articles