Agregar automáticamente espacio de servidor virtual

Hola a todos!


En este artículo hablaremos sobre cómo automatizamos la tarea de expandir el espacio en disco en uno de nuestros servidores. ¿Y qué es difícil en una tarea tan simple que tuve que automatizarla? Nada si no utiliza monturas combinadas en cascada. Siento que hay más preguntas! Pues bien, vamos debajo del gato.

Primero, le diré por qué usamos el montaje integrado en cascada.

Tenemos un sistema que necesita almacenamiento para archivos pequeños (escaneos de documentos, etc.). El tamaño promedio del archivo es de 200 kb a 1 megabyte, los datos son estáticos, no cambian. Hay miles de millones de archivos y el número crece cada día. Una vez, cuando el volumen ya era más de 6 TB, nos dimos cuenta de que los problemas comenzarían pronto, uno de los cuales era el momento de la copia de seguridad y la recuperación. Luego decidimos dividir los datos en discos, y se llamó a UnionFS para ayudarnos con esto.

El algoritmo se determinó de la siguiente manera: los datos se escriben en el disco no más de 2 TB, cuando termina, agregamos un nuevo disco a la máquina virtual, lo marcamos, lo agregamos a UnionFS, transferimos el viejo a ReadOnly, eliminamos una copia, escribimos en cinta, lo eliminamos de la copia de seguridad en línea.

Como ya entendió, este algoritmo exige bastante la atención del administrador: cualquier movimiento incómodo y almacenamiento no están disponibles. Por lo tanto, decidimos excluir completamente el factor humano y recordamos que tenemos ZABBIX, que muy bien podemos manejar esto nosotros mismos si agregamos un poco de magia PowerShell y Bash al algoritmo.

Ahora sobre cómo se hace esto.

En Zabbix, se configura un disparador para espacio libre y se hace un botón para el modo manual:

imagen

Cuando se activa el activador, se forma una tarea en el programador servidor-robot en el que se encuentran todos nuestros scripts de automatización:

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

A la hora señalada, se inicia un script en el servidor que:

Agrega el disco a la VM deseada:
(al mismo tiempo, elige el volumen más libre)

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

Busca detalles de acceso al servidor:
APAGADO
Utilizamos un repositorio personalizado de detalles de acceso basado en TeamPass, por lo que el script encuentra el servidor deseado en el sistema y recibe sus detalles automáticamente. Esto se hace porque cada mes cambiamos automáticamente todas las contraseñas, pero este es el tema de un artículo separado

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

Viene en 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 

Lo marca y lo agrega al montaje 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 


Desafortunadamente, al momento de escribir esto, no resolvimos varios problemas relacionados con la creación automática de tareas en VEEAM para archivar el disco viejo y escribirlo en cinta, por lo que esto todavía se hace manualmente. Pero definitivamente actualizaremos el script tan pronto como resolvamos un par de problemas.

Publicado por Vitaliy Rosman ( PBCVIT ).

Honestamente, se prestó un fragmento de código para pegar matrices, los enlaces al autor se guardaron en el código.

Guión completo
 #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 


No hay quejas contra UnionFS; ha estado funcionando de manera estable durante más de dos años.

La pregunta de por qué el almacenamiento está organizado en general es retórica, simplemente acéptelo tal como es.

Tenga en cuenta que se deben utilizar diferentes tipos de asignación de unidades para diferentes sistemas. por lo tanto, tenga cuidado y la fuerza vendrá con usted.

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


All Articles