Ajouter automatiquement de l'espace sur le serveur virtuel

Bonjour à tous!


Dans cet article, nous expliquerons comment nous avons automatisé la tâche d'extension de l'espace disque sur l'un de nos serveurs. Et qu'est-ce qui est difficile dans une tâche si simple que j'ai dû l'automatiser - demandez-vous? Rien si vous n'utilisez pas de montures fusionnées en cascade. Je sens qu'il y a plus de questions!? Eh bien, passons sous le chat.

Tout d'abord, je vais vous expliquer pourquoi nous utilisons un montage intégré en cascade.

Nous avons un système qui a besoin de stockage pour les petits fichiers (numérisation de documents, etc.). La taille moyenne d'un fichier est de 200 Ko à 1 mégaoctet, les données sont statiques, ne changent pas. Il contient des milliards de fichiers et leur nombre augmente chaque jour. Une fois, lorsque le volume était déjà supérieur à 6 To, nous avons réalisé que les problèmes allaient bientôt commencer, dont le moment de la sauvegarde et de la restauration. Ensuite, nous avons décidé de diviser les données en disques, et UnionFS a été appelé pour nous aider.

L'algorithme a été déterminé comme suit: les données sont écrites sur le disque pas plus de 2 To, à la fin, nous ajoutons un nouveau disque à la machine virtuelle, le marquons, l'ajoutons à UnionFS, transférons l'ancien à ReadOnly, supprimons-en une copie, écrivez sur bande, supprimez-le de la sauvegarde en ligne.

Comme vous l'avez déjà compris, cet algorithme est assez exigeant pour l'attention de l'administrateur - aucun mouvement maladroit et stockage n'est disponible. Par conséquent, nous avons décidé d'exclure complètement le facteur humain et nous sommes souvenus que nous avons ZABBIX, qui peut très bien gérer cela nous-mêmes si nous ajoutons un peu de PowerShell et de magie Bash à l'algorithme.

Maintenant, comment cela se fait.

Dans Zabbix, un déclencheur pour l'espace libre est configuré et un bouton pour le mode manuel est créé:

image

Lorsque le déclencheur est déclenché, une tâche se forme dans le sheduler serveur-robot sur lequel se trouvent tous nos scripts d'automatisation:

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

A l'heure fixée, un script est lancé sur le serveur qui:

Ajoute le disque à la VM souhaitée:
(en même temps il choisit le volume le plus libre)

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

Recherche les détails d'accès au serveur:
OFFTop
Nous utilisons un référentiel personnalisé de détails d'accès basé sur TeamPass, de sorte que le script trouve le serveur souhaité dans le système et reçoit ses détails automatiquement. Cela se fait parce que chaque mois, nous changeons automatiquement tous les mots de passe, mais c'est le sujet d'un article séparé

 #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)) 

Livré sur 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 

Le marque et l'ajoute au montage 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 


Malheureusement, au moment de la rédaction de ce document, nous n'avions pas résolu plusieurs problèmes liés à la création automatique de tâches dans VEEAM pour l'archivage de l'ancien disque et l'écriture sur bande, donc cela se fait toujours manuellement. Mais nous mettrons définitivement à jour le script dès que nous aurons résolu quelques problèmes.

Publié par Vitaliy Rosman ( PBCVIT ).

Un morceau de code pour coller des tableaux a été honnêtement emprunté, des liens vers l'auteur ont été enregistrés dans le code.

Script complet
 #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 


Il n'y a aucune plainte contre UnionFS qui fonctionne de manière stable depuis plus de deux ans.

La question de savoir pourquoi le stockage est organisé en général est généralement rhétorique, il suffit de l'accepter tel quel.

Veuillez noter que différents types de mappage de lecteur doivent être utilisés pour différents systèmes. par conséquent, soyez prudent et la force vous accompagnera.

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


All Articles