Kürzlich habe ich mich mit dem Thema Lizenzoptimierung für Gast Betriebssysteme in VMware vSphere Umgebungen auseinandergesetzt. Die Anforderung war es, möglichst wenige ESXi Hosts mit RHEL oder SLES zu lizenzieren. Die simple Lösung ist, diese Betriebssysteme auf bestimmten, möglichst wenigen ESXi Hosts in den Clustern zusammenzuhalten. Der Cluster kann und soll dabei noch mehr Hosts beinhalten als die lizenzierten. Da für mich nur eine automatisierte Lösung in Frage kam, habe ich mich für den Ansatz DRS Affinity Rules by Tag entschieden.
Ich muss ganz ehrlich sagen ich war etwas verwundert, dass es in VMware vSphere out of the box nicht möglich ist, DRS Gruppen per Tag zu bilden, ähnlich wie bei Storage Policys. Aber mit VMware PowerCLI ist ja fast alles möglich.
Die Umsetzung der Anforderung sollte dann wie folgt aussehen:
- An den VMs mit lizenzpflichtigen Gastbetriebssystemen wird das entsprechende Tag angelegt
- Pro Cluster bekommt mindestens ein ESXi Host pro Lizenztyp ebenfalls das oder die jeweiligen Tags
- Anhand dieser Tags werden automatisiert DRS Gruppen gebildet
- Diese Gruppen werden dann wiederum automatisiert in einer VM to Host Affinity Rule verwendet
Hört sich erst einmal etwas aufwendig an, hat jedoch langfristig enorme Vorteile:
- Beim Provisionieren muss nur der Tag an die VM geheftet werden und der Automatismus kümmert sich um die Platzierung
- Der gleiche Automatismus greift auch für den Host. Es muss nur das Tag gesetzt oder entfernt werden anstatt die Gruppe zu verändern
- Compliance Audit ist anhand der Tags jederzeit möglich
- Die Tags können in Drittprodukten wie vRealize Operatrions Manager ebenfalls verwendet werden
Voraussetzungen:
- DRS ist in allen Clustern mindestens auf Teilautomatisiert gestellt, besser auf Vollautomatisiert (auch bereits auf ganz Konservativ)
- Nur ein Lizenz Tag pro VM, Hosts gerne mehrere
- Bei Stretched Clustern (über mehrere Datacenter) muss in jedem Datacenter mindestens ein Host pro Lizenztyp vorhanden sein
Umsetzung DRS Affinity Rules by Tag
Ich habe für die initiale Umsetzung erst einmal PowerCLI gewählt. Später wird das Ganze wohl auf vRealize Orchestrator portiert werden.
VMs Klassifizieren
Als erster Schritt ist es notwendig die bestehenden VMs für die Lizenzgruppen zu identifizieren.
$VMs = Get-VM $SLESVMs = @() $RHELVMs = @() $UnknownVMs = @() foreach ($VM in $VMs){ Write-Host "Processing $($VM.Name) ..." if ($VM.GuestId -match "SLES|SUSE"){ $SLESVMs += $VM Write-Host "$($VM.Name) SLES identified." -ForegroundColor Green } elseif ($VM.GuestId -match "RHEL|Red") { $RHELVMs += $VM Write-Host "$($VM.Name) RHEL identified." -ForegroundColor Green } else { Write-Host "$($VM.Name) has unknown Guest ID: $($VM.GuestID) and Detected OS: $($VM.ExtensionData.Guest.GuestId) " -ForegroundColor Yellow $UnknownVMs += $VM } }
Tags den VMs zuweisen
Das gehört ebenfalls noch zum initialen Prozess um die bestehenden VMs zu erfassen.
$mySLESTag = Get-Tag -Category "OS License" -Name SLES $myRHELTag = Get-Tag -Category "OS License" -Name RHEL $SLESVMs | New-TagAssignment $mySLESTag $RHELVMs | New-TagAssignment $myRHELTag
Kleiner Test
Um einen Eindruck zu bekommen wie praktisch die Tags sind, ist dieser Codeschnipsel sehr aussagekräftig:
Get-VM -Tag $mySLESTag
Gruppen und Regeln erzeugen
Nun kommen wir zum Hauptteil der initialen Klassifizierung. In jedem vSphere Cluster in dem sich VMs befinden müssen die jeweiligen Gruppen und Regeln erzeugt werden.
Um die Gruppen und Regeln zu erzeugen ist allerdings zusätzlich das DRSRule PowerShell module von Matt Boren und Luc Dekens erforderlich.
$myClusters = $VMs | Get-Cluster | Sort name | Select -Unique foreach ($myCluster in $myClusters) { $mySLESHosts = $myCluster | Get-VMHost | Sort Name | select -First 1 $myRHELHosts = $myCluster | Get-VMHost | Sort Name | select -First 1 $mySLESHosts | New-TagAssignment $mySLESTag $myRHELHosts | New-TagAssignment $myRHELTag $SLESHosts = $myCluster | Get-VMHost -Tag $mySLESTag $RHELHosts = $myCluster | Get-VMHost -Tag $myRHELTag New-DrsVMHostGroup -Cluster $myCluster -VMHost $SLESHosts -Name "OS-SLES-Host" New-DrsVMHostGroup -Cluster $myCluster -VMHost $RHELHosts -Name "OS-RHEL-Host" if ($SLESVMs = $myCluster | Get-VM -Tag $mySLESTag) { New-DrsVMGroup -Cluster $myCluster -VM $SLESVMs -Name "OS-SLES-VM" New-DrsVMToVMHostRule -Cluster $myCluster -VMGroupName "OS-SLES-VM" -AffineHostGroupName "OS-SLES-Host" -Mandatory:$false -Enabled:$true -Name "OS-SLES" } if ($RHELVMs = $myCluster | Get-VM -Tag $myRHELTag){ New-DrsVMGroup -Cluster $myCluster -VM $RHELVMs -Name "OS-RHEL-VM" New-DrsVMToVMHostRule -Cluster $myCluster -VMGroupName "OS-RHEL-VM" -AffineHostGroupName "OS-RHEL-Host" -Mandatory:$false -Enabled:$true -Name "OS-RHEL" } }
In meinem Beisiel wird ein Host pro Cluster mit allen Lizenz Tags versehen.
Auch zu beachten ist, dass ich die Regeln initial als Soft-Rule angelegt habe. Das ist bezüglich der Lizenz Compliance etwas fraglich aber für die Handhabe wesentlich einfacher.
Mehr Infos zu den beiden Regel Typen findet man in diesen Artikeln:
Ergebnis DRS Affinity Rules by Tag
Auf der vSphere Cluster Ebene wurden nun durch das Skript als erstes mehrere VM und Host Gruppen erzeugt.
Dann wurden die „zusammengehörigen“ Gruppen in einer VM to Host Affinity Rule verbunden.
Reporting DRS Affinity Rules by Tag
Das Reporting für diese Anforderung ist ein essentieller Bestandteil.
Folgendes muss geprüft werden können:
- Welche Hosts haben welche Lizenzen (bzw. Lizenz Tags)
- VM Tag zu Host Tag Compliance (sind die VMs auf den korrekten Hosts)
- VM Group Compliance (sind die VMs in den Gruppen auch korrekt ge-Tagged)
- Host Group Compliance (sind die Hosts in den Gruppen auch korrekt ge-Tagged)
Welche Hosts haben welche Lizenzen
## Report Hosts $myView = @() $TagedVMHosts = Get-VMHost -Tag $mySLESTag, $myRHELTag, $myTEMPTag foreach ($TagedVMHost in $TagedVMHosts) { $Report = [PSCustomObject] @{ Name = $TagedVMHost.name HostOSTag = [Array]($TagedVMHost | Get-TagAssignment -Category "OS License").Tag } $MyView += $Report } $myView | Sort Name | ft -AutoSize # Resport Statistics $myStatistic = $myView.HostOSTag | ForEach-Object -Begin { $wordCounts=@{} } -Process { $wordCounts.$_++ } -End { $wordCounts } $myStatistic
Reporting per vSphere vCheck
Diesen Report ich zusätzlich auch noch als Beispiel für die Integration in den vSphere vCheck von Alan Renouf hergenommen.
# Start of Settings # End of Settings $mySLESTag = Get-Tag -Category "OS License" -Name SLES -Server $Server $myRHELTag = Get-Tag -Category "OS License" -Name RHEL -Server $Server $myTEMPTag = Get-Tag -Category "OS License" -Name TEMP -Server $Server $TagedVMHostsView = @() $Tags = @() $TagedVMHosts = Get-VMHost -Tag $mySLESTag, $myRHELTag, $myTEMPTag foreach ($TagedVMHost in $TagedVMHosts) { $Report = [PSCustomObject] @{ Name = $TagedVMHost.name HostOSTag = [String](($Tag = [Array]($TagedVMHost | Get-TagAssignment -Category "OS License").Tag) -join ", " ) } $Tags += $Tag $TagedVMHostsView += $Report } $TagedVMHostsView | Sort Name $Title = "Host OS License tags" $Header = "Hosts with License Tags : $(($TagedVMHostsView).count)" $Comments = "The following hosts are enabled for custom OS Linceses" $Display = "Table" $Author = "Markus Kraus" $PluginVersion = 1.0 $PluginCategory = "vSphere"
# Start of Settings # End of Settings $myStatistic = $Tags | ForEach-Object -Begin { $wordCounts=@{} } -Process { $wordCounts.$_++ } -End { $wordCounts } $myStatistic.GetEnumerator() | Sort Name $Title = "Host OS License type count" $Header = "Licenses types" $Comments = "The following number ofLicenses are enbaled for Hosts" $Display = "Table" $Author = "Markus Kraus" $PluginVersion = 1.0 $PluginCategory = "vSphere"
Welche Hosts haben welche Lizenzen mit Auslastung
## Report Host Usage $myView = @() $TagedVMHosts = Get-VMHost -Tag $mySLESTag, $myRHELTag [int] $StatsRange = 1440 [int] $TimeRange = "-" + $StatsRange foreach ($TagedVMHost in $TagedVMHosts) { $Start = (Get-Date).AddMinutes($TimeRange) $HostStatCpu = Get-Stat -Realtime -Stat cpu.usage.average -Entity $TagedVMHost -Start $Start -Verbose:$False | Measure-Object -Property value -Average -Maximum -Minimum $HostStatMem = Get-Stat -Realtime -Stat mem.usage.average -Entity $TagedVMHost -Start $Start -Verbose:$False | Measure-Object -Property value -Average -Maximum -Minimum $Report = [PSCustomObject] @{ Name = $TagedVMHost.name Cluster = $TagedVMHost.Parent.Name HostOsTag = [Array]($TagedVMHost | Get-TagAssignment -Category "OS License").Tag HostMemUsage = $([math]::round( ($TagedVMHost.MemoryUsageGB / $TagedVMHost.MemoryTotalGB) * 100,1 )) HostCpuUsage = $([math]::round( ($TagedVMHost.CpuUsageMhz/ $TagedVMHost.CpuTotalMhz) * 100,1 )) HostMemUsageAvg = $([math]::round( $HostStatMem.Average,1 )) HostCpuUsageAvg = $([math]::round( $HostStatCpu.Average,1 )) HostMemUsageMax = $([math]::round( $HostStatMem.Maximum,1 )) HostCpuUsageMax = $([math]::round( $HostStatCpu.Maximum,1 )) } $MyView += $Report } $myView | Sort Cluster, Name | ft -AutoSize
Reporting per vSphere vCheck
Auch dieser Report kann extrem sinnvoll für den vSphere vCheck von Alan Renouf sein.
# Start of Settings # End of Settings $mySLESTag = Get-Tag -Category "OS License" -Name SLES -Server $Server $myRHELTag = Get-Tag -Category "OS License" -Name RHEL -Server $Server $myUsageView = @() $TagedVMHosts = Get-VMHost -Tag $mySLESTag, $myRHELTag [int] $StatsRange = 1440 [int] $TimeRange = "-" + $StatsRange foreach ($TagedVMHost in $TagedVMHosts) { $Start = (Get-Date).AddMinutes($TimeRange) $HostStatCpu = Get-Stat -Realtime -Stat cpu.usage.average -Entity $TagedVMHost -Start $Start -Verbose:$False | Measure-Object -Property value -Average -Maximum -Minimum $HostStatMem = Get-Stat -Realtime -Stat mem.usage.average -Entity $TagedVMHost -Start $Start -Verbose:$False | Measure-Object -Property value -Average -Maximum -Minimum $Report = [PSCustomObject] @{ Name = $TagedVMHost.name Cluster = $TagedVMHost.Parent.Name HostOsTag = [String](($Tag = [Array]($TagedVMHost | Get-TagAssignment -Category "OS License").Tag) -join ", " ) HostMemUsage = $([math]::round( ($TagedVMHost.MemoryUsageGB / $TagedVMHost.MemoryTotalGB) * 100,1 )) HostCpuUsage = $([math]::round( ($TagedVMHost.CpuUsageMhz/ $TagedVMHost.CpuTotalMhz) * 100,1 )) HostMemUsageAvg = $([math]::round( $HostStatMem.Average,1 )) HostCpuUsageAvg = $([math]::round( $HostStatCpu.Average,1 )) HostMemUsageMax = $([math]::round( $HostStatMem.Maximum,1 )) HostCpuUsageMax = $([math]::round( $HostStatCpu.Maximum,1 )) } $myUsageView += $Report } $myUsageView | Sort Cluster, Name $Title = "Host Usage with License Tags" $Header = "Host Usage with License Tags" $Comments = "The following hosts are enabled for custom OS Licenses" $Display = "Table" $Author = "Markus Kraus" $PluginVersion = 1.0 $PluginCategory = "vSphere" $TableFormat = @{"HostMemUsageMax" = @(@{ "-gt '75'" = "Row,class|warning"}); "HostCpuUsageMax" = @(@{ "-gt '75'" = "Row,class|warning"}); }
VM Tag zu Host Tag Compliance
## Report VM Tag to Host Tag Compliance $myView = @() $allTags = Get-Tag -Category "OS License" foreach ($allTag in $allTags) { Foreach ($myVM in (Get-VM -Tag $allTag)) { $VMOSTag = ($myVM | Get-TagAssignment -Category "OS License").Tag $VMHost = ($myVM.VMHost) $HostOSTag = ( $VMHost | Get-TagAssignment -Category "OS License").Tag $Report = [PSCustomObject] @{ Name = $myVM.Name PowerState = $myVM.PowerState VMOSTag = $VMOSTag Host = $VMHost.Name HostOSTag = $HostOSTag PlacedWell = if ( $HostOSTag -contains $VMOSTag ) {$true} else {$false} } $MyView += $Report } } $myView | ft -AutoSize $myView | where {$_.PlacedWell -eq $False} | ft -AutoSize
VM Group Compliance
## Report VM Group Compliance $myView = @() $allGroups = Get-DrsVMGroup | Where {$_.Name -like "OS-*" -and $_.Cluster -notlike "Test*"} foreach ($allGroup in $allGroups) { [Array]$myVMNames = $allGroup.VM if ($myVMNames.count -ge 1) { Foreach ($myVMName in $myVMNames) { $myVM = get-Cluster -Name $allGroup.Cluster | Get-VM -Name $myVMName $VMHost = ($myVM.VMHost) $VMOSTag = ($myVM | Get-TagAssignment -Category "OS License").Tag $Report = [PSCustomObject] @{ Name = $myVM.Name PowerState = $myVM.PowerState VMOSTag = $VMOSTag Host = $VMHost.Name HostOSTag = $HostOSTag VMGroup = $allGroup.Name PlacedWell = if ( $HostOSTag -contains $VMOSTag -and $allGroup.Name -match $VMOSTag.Name ) {$true} else {$false} } $MyView += $Report } } } $myView | ft -AutoSize $myView | where {$_.PlacedWell -eq $False} | ft -AutoSize
Host Group Compliance
## Report Host Group Compliance $myView = @() $allGroups = Get-DrsVMHostGroup | Where {$_.Name -like "OS-*" -and $_.Cluster -notlike "Test*"} foreach ($allGroup in $allGroups) { [Array]$myVMHostNames = $allGroup.VMHost if ($myVMHostNames.count -ge 1) { Foreach ($myVMHostName in $myVMHostNames) { $myVMHost = Get-Cluster -Name $allGroup.Cluster | Get-VMHost -Name $myVMHostName $VMHostOSTag = ($myVMHost | Get-TagAssignment -Category "OS License").Tag $Report = [PSCustomObject] @{ Name = $myVMHost.Name ConnectionState = $myVMHost.ConnectionState VMHostOSTag = $VMHostOSTag VMHostGroup = $allGroup.Name PlacedWell = if ([Array]($VMHostOSTag.Name).Contains(($allGroup.Name -split "-")[1]) ) {$true} else {$false} } $MyView += $Report } } } $myView | ft -AutoSize $myView | where {$_.PlacedWell -eq $False} | ft -AutoSize
Monitoring DRS Affinity Rules by Tag
Da durch das Bilden von kleinere Host Gruppen innerhalb der Cluster das Monitoring und Forecasting etwas komplexer wird, sollten man hier zusätzlich auf den vRealize Operations Manager setzen.
Wie bereits schon vorher erwähnt, lassen sich die Tags hier auch wunderbar weiter verwenden.
Gruppe per Tag bilden:
Beispiel für eine Analyse:
Beispiel für einen Report:
Auch VMware vRealize Log Insight kann sehr gut verwendet werden um die DRS Verstöße in einem Dashboard aufzubereiten:
Filter sieht zum Beispiel so aus:
Selbstverständlich können dann auch Alarme auf die Suchen gelegt werden.
Externe Infos zu Tags: