DRS Affinity Rules by Tag

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:

DRS Affinity Rules by Tag - Diagram

DRS Affinity Rules by Tag – Diagram

  1. An den VMs mit lizenzpflichtigen Gastbetriebssystemen wird das entsprechende Tag angelegt
  2. Pro Cluster bekommt mindestens ein ESXi Host pro Lizenztyp ebenfalls das oder die jeweiligen Tags
  3. Anhand dieser Tags werden automatisiert DRS Gruppen gebildet
  4. 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:

Mandatory DRS Rules and HA

Should or Must VM-Host affinity rules?

Ergebnis DRS Affinity Rules by Tag

Auf der vSphere Cluster Ebene wurden nun durch das Skript als erstes mehrere VM und Host Gruppen erzeugt.

Innerhalb eines Clusters darf es keine Identischen Gruppen Namen geben, auch wenn es Host und VM Gruppen sind.

Cluster übergreifend können identische Namen jedoch existieren

DRS Affinity Rules by Tag - VM/Host Groups

Dann wurden die „zusammengehörigen“ Gruppen in einer VM to Host Affinity Rule verbunden.

DRS Affinity Rules by Tag - VM/Host Rules

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

DRS Affinity Rules by Tag - License Report

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

Der Plugin Code ist aktuell nur ein Test und wird so nur in meiner Umgebung funktionieren!

DRS Affinity Rules by Tag - vCheck License Report

# 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

DRS Affinity Rules by Tag - Host Usage Detailed

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

DRS Affinity Rules by Tag - vCheck Host Usage

# 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

DRS Affinity Rules by Tag - VM/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

DRS Affinity Rules by Tag - 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

DRS Affinity Rules by Tag - 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:

DRS Affinity Rules by Tag - vROPs Group

Beispiel für eine Analyse:

DRS Affinity Rules by Tag - vROPs Analysis

Beispiel für einen Report:

DRS Affinity Rules by Tag - vROPs Report

Auch VMware vRealize Log Insight kann sehr gut verwendet werden um die DRS Verstöße in einem Dashboard aufzubereiten:

DRS Affinity Rules by Tag - vLI Dashboard

Filter sieht zum Beispiel so aus:

DRS Affinity Rules by Tag - vLI Dashboard Filter

Selbstverständlich können dann auch Alarme auf die Suchen gelegt werden.

Externe Infos zu Tags:

 

Leave a Reply