Script – VMware vSphere Sizing

Um mir und meinen Kollegen die wiederkehrende Aufgabe der Datensammlung zwecks eines VMware vSphere Sizing zu erleichtern habe ich hierzu ein PowerCLI Skript geschrieben. Dieses sammelt in einer bereits bestehenden VMware vSphere Umgebung einige Informationen als Grundlage für ein neues Hardware Sizing.

Fokus der gesammelten Daten ist das Server Sizing. In der aktuellen Version sind aber auch auch bereits einige Storage Daten enthalten.

Selbstverständlich sind diese Daten alleine nicht ausreichend um ein Sizing zu erstellen. Für mich bedeutet ein qualifiziertes Sizing eine ganzheitliche Betrachtung der Anforderungen und Prozesse des Kunden.

Ausgabe des VMware vSphere Sizing Skripts

VMware vSphere Sizing Script v1.2

Volle Ausgabe der aktuellen Version 1.2

VMware vSphere Sizing Script without Stats v1.2

Ausgabe ohne Stats

VMware vSphere Sizing Script verbose v1.2

Auch eine ausführliche Ausgabe der Erfassung ist möglich

Aufruf des VMware vSphere Sizing Skripts

Ich habe das Skript mit dem Ziel der möglicht einfachen Handhabung als PowerShell Modul angelegt.

Import-Module C:\Scripts\vSphere-Modules\Recommend-Sizing.psm1
Import-Module -Name VMware.VimAutomation.Core
Connect-VIServer <vCenter FQDN>
Recommend-Sizing -ClusterNames Cluster01, Cluster02 [-Stats -StatsRange 480] -Verbose

Zur weiteren Verarbeitung können die Daten natürlich auch exportiert werden:

Recommend-Sizing -ClusterNames Cluster01 [-Stats -StatsRange 60] -Verbose | Out-File C:\Temp\out.txt
Recommend-Sizing -ClusterNames Cluster01 [-Stats -StatsRange 60] -Verbose | Export-Csv C:\Temp\out.csv

Beispiel aus der Praxis:

Recommend-Sizing_Example.xls Google Docs

 

 

Bei einer großen Anzahl von vSphere Clustern kann der Aufruf auch so aussehen:

Recommend-Sizing -ClusterNames (Get-Cluster).Name

Werte des VMware vSphere Sizing Resultats

Name Beschreibung
Cluster Cluster Name
HAEnabled Cluster HA Status
DrsEnabled Cluster DRS Status
Hosts Anzahl der Hosts im Cluster
HostsAverageMemoryUsageGB Durchschnittlich genutzter RAM in GB der Hosts im Cluster
HostsAverageMemoryUsage Durchschnittlich genutzter RAM in % der Hosts im Cluster
HostsAverageCpuUsageMhz Durchschnittlich genutzter CPU in MHz der Hosts im Cluster
HostsAverageCpuUsage Durchschnittlich genutzter CPU in % der Hosts im Cluster
PhysicalCPUCores Anzahl der physikalischen CPUI Cores der Hosts im Cluster
LogicalCPUThreads Anzahl der logischen CPU Threads der Hosts im Cluster
VMs Anzahl der VMs im Cluster
ActiveVMs Anzahl der eingeschalteten VMs im Cluster
VMvCPUs Anzahl der vCPUs aller VMs im Cluster
vCPUpCPUratio Verhältnis vCPUs zu der logischen CPU Threads
PhysicalMemoryGB Physikalischer RAM in GB im Cluster
AllocatedVMMemoryGB Allokierter RAM in GB im Cluster
ClusterMemoryUsage Allokierter RAM in % im Cluster
SumVMDiskSpaceGB Summer der vDisk Größen aller VMs im Cluster
SumDatastoreSpaceGB Summer aller Datastore Kapazitäten im Cluster
SumDatastoreUsedSpaceGB Summer aller genutzter Datastore Kapazitäten im Cluster
AverageVMIOPSWriteAvg Durchschnittliche Write IOPS aller VMs im Cluster
AverageVMIOPSReadAvg Durchschnittliche Read IOPS aller VMs im Cluster
AverageVMCPUUsageAvg Durchschnittliche CPU Nutzung in % aller VMs im Cluster
AverageVMMEMUsageAvg Durchschnittliche RAM Nutzung in % aller VMs im Cluster

VMware vSphere Sizing Skript

function Recommend-Sizing {
<#	
    .NOTES
    ===========================================================================
    Created by: Markus Kraus
    Twitter: @VMarkus_K
    Private Blog: mycloudrevolution.com
    ===========================================================================
    Changelog:  
    2016.11 ver 1.0 Base Release 
    2016.11 ver 1.1 Optional Stats Collection
    2016.11 ver 1.2 VM Stats from Realtime Data and new Counters
    ===========================================================================
    External Code Sources:  
    http://www.lucd.info/2011/04/22/get-the-maximum-iops/
    https://communities.vmware.com/thread/485386
    ===========================================================================
    Tested Against Environment:
    vSphere Version: 5.5 U2, 6.0
    PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1
    PowerShell Version: 4.0, 5.0
    OS Version: Windows 8.1, Server 2012 R2
    ===========================================================================
    Keywords vSphere, ESXi, VM, Storage, Sizing
    ===========================================================================

    .DESCRIPTION
    This Function collects Basic vSphere Informations for a Hardware Sizing Recommandation. Focus is in Compute Ressources.        

    .Example
    Recommend-Sizing -ClusterNames Cluster01, Cluster02 -Stats -StatsRange 60 -Verbose    

    .Example
    Recommend-Sizing -ClusterNames Cluster01, Cluster02 

    .Example
    Recommend-Sizing -ClusterNames Cluster01 

    .PARAMETER ClusterNames
    List of your vSphere Cluser Names to process.

    .PARAMETER Stats
    Enables Stats Collection.

    Warning: At the moment this is only fully tested with vSphere 5.5 and vSphere 6.5!

    .PARAMETER StatsRange
    Time Range in Minutes for the Stats Collection.
    Default is 24h.

#Requires PS -Version 4.0
#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"}
#>

[CmdletBinding()]
param( 
    [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)]
        [Array] $ClusterNames,
    [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1, ParameterSetName = "Stats")]
        [switch] $Stats,
     [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=2, ParameterSetName = "Stats")]
        [int] $StatsRange = 1440      
        
)
Begin {
     if ($Stats) {
        Write-Warning "Stats Collection requested.`nAt the moment this is only fully tested with vSphere 5.5 and vSphere 6.5"
        [int]$TimeRange = "-" + $StatsRange
     }

    $Validate = $True
    #region: Check Clusters
    Write-Verbose "$(Get-Date -Format G) Starting Cluster Validation..." 
    foreach ($ClusterName in $ClusterNames) {
        $TestCluster = Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue -Verbose:$False
        if(!($TestCluster)){
            Write-Warning "No Custer found wth Name $ClusterName!"
            $Validate = $False
        }
        elseif ($TestCluster.count -gt 1) {
            Write-Warning "Multiple Custers found wth Name $ClusterName!`nUse a List of explicit Cluster Names: Recommend-Sizing -ClusterNames Cluster01, Cluster02 "
            $Validate = $False
        }
    }
    Write-Verbose "$(Get-Date -Format G) Cluster Validation completed" 
    #endregion
}

Process {
    $MyView = @()
    if ($Validate -eq $True) {
        foreach ($ClusterName in $ClusterNames) {
            #region: Get Cluster Objects
            Write-Verbose "$(Get-Date -Format G) Collect $ClusterName Cluster Objects..." 
            $Cluster =  Get-Cluster -Name $ClusterName -Verbose:$False
            $ClusterVMs = $Cluster | Get-VM -Verbose:$False
            $ClusterVMsPoweredOn = $ClusterVMs | where {$_.PowerState -eq "PoweredOn"}
            $ClusterDatastores = $Cluster | Get-Datastore -Verbose:$False
            $ClusterHosts = $Cluster | Get-VMHost -Verbose:$False
            $HostsAverageMemoryUsageGB = [math]::round( ($ClusterHosts | Measure-Object -Average -Property MemoryUsageGB).Average,1 )
            $HostsAverageMemoryUsage = $([math]::round( (($ClusterHosts | Measure-Object -Average -Property MemoryUsageGB).Average / ($ClusterHosts | Measure-Object -Average -Property MemoryTotalGB).Average) * 100,1 ))
            $HostsAverageCpuUsageMhz = [math]::round( ($ClusterHosts | Measure-Object -Average -Property CpuUsageMhz).Average,1 )
            $HostsAverageCpuUsage = $([math]::round( (($ClusterHosts | Measure-Object -Average -Property CpuUsageMhz).Average / ($ClusterHosts | Measure-Object -Average -Property CpuTotalMhz).Average) * 100,1 ))
            Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Cluster Objects completed" 
            #endregion

            #region: CPU Calculation
            Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) CPU Details..." 
            $VMvCPUs = ($ClusterVMs | Measure-Object -Sum -Property NumCpu).sum
            $LogicalThreads = $Cluster.ExtensionData.Summary.NumCpuThreads
            $CpuCores = $Cluster.ExtensionData.Summary.NumCpuCores
            $vCPUpCPUratio = [math]::round( $VMvCPUs / $LogicalThreads,1 )
            Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) CPU Details completed." 
            #endregion

            #region: Memory Calculation
            Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details..." 
            $AllocatedVMMemoryGB = [math]::round( ($ClusterVMs | Measure-Object -Sum -Property MemoryGB).sum )
            $PhysicalMemory = [math]::round( $Cluster.ExtensionData.Summary.TotalMemory / 1073741824,1 )
            $MemoryUsage = [math]::round( ($AllocatedVMMemoryGB / $PhysicalMemory) * 100 ,1 )
            Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details completed" 
            #endregion

            if ($Stats) {
                #region: Creating VM Stats
                Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats..."
                $VMMetrics = "disk.numberwrite.summation","disk.numberread.summation","cpu.usage.average", "mem.usage.average"
                $Start = (Get-Date).AddMinutes($TimeRange)
                $VMStats = Get-Stat -Realtime -Stat $VMMetrics -Entity $ClusterVMsPoweredOn -Start $Start -Verbose:$False
                Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats completed"
                #endregion
                
                #region: Creating VM Stats Report
                Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report..."
                $ReportVMPerf = @() 
                $ReportVMPerf = $VMStats | Group-Object -Property {$_.Entity.Name},Instance | %{
                                    New-Object PSObject -Property @{
                                    IOPSWriteAvg = ($_.Group | `
                                    where{$_.MetricId -eq "disk.numberwrite.summation"} | `
                                    Measure-Object -Property Value -Average).Average
                                    IOPSReadAvg = ($_.Group | `
                                    where{$_.MetricId -eq "disk.numberread.summation"} | `
                                    Measure-Object -Property Value -Average).Average
                                    CPUUsageAvg = ($_.Group | `
                                    where{$_.MetricId -eq "cpu.usage.average"} | `
                                    Measure-Object -Property Value -Average).Average
                                    MEMUsageAvg = ($_.Group | `
                                    where{$_.MetricId -eq "mem.usage.average"} | `
                                    Measure-Object -Property Value -Average).Average
                    }
                }
                Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report completed"
                #endregion
            }
            else {
                Write-Verbose "$(Get-Date -Format G) Stats Collection skipped..."
            }

            #region: Create VM Disk Space Report
            Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report..."
            $reportDiskSpace = @()
            foreach ($ClusterVM in $ClusterVMs){
                $VMDKs = $ClusterVM | get-HardDisk -Verbose:$False
                foreach ($VMDK in $VMDKs) {
                    if ($VMDK -ne $null){
                        [int]$CapacityGB = $VMDK.CapacityKB/1024/1024
                        $Report = [PSCustomObject] @{
                                CapacityGB = $CapacityGB
                            }
                            $reportDiskSpace += $Report
                        }   
                    }
                }
            Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report completed"
            #endregion

            #region: Create Datastore Space Report
            Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) Datastore Space Report..."
            $DatastoreReport = @($ClusterDatastores | Select-Object @{N="CapacityGB";E={[math]::Round($_.CapacityGB,2)}}, @{N="FreeSpaceGB";E={[math]::Round($_.FreeSpaceGB,2)}}, @{N="UsedSpaceGB";E={[math]::Round($_.CapacityGB - $_.FreeSpaceGB,2)}})
            Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) Datastore Space Report completed"
            #endregion

            #region: Create Global Report
            Write-Verbose "$(Get-Date -Format G) Process Global Report..."
            $SizingReport = [PSCustomObject] @{
				Cluster = $Cluster.name
                HAEnabled = $Cluster.HAEnabled
                DrsEnabled = $Cluster.DrsEnabled
                Hosts = $Cluster.ExtensionData.Summary.NumHosts
                HostsAverageMemoryUsageGB = $HostsAverageMemoryUsageGB
                HostsAverageMemoryUsage = "$HostsAverageMemoryUsage %" 
                HostsAverageCpuUsageMhz = $HostsAverageCpuUsageMhz
                HostsAverageCpuUsage = "$HostsAverageCpuUsage %" 
                PhysicalCPUCores = $CpuCores
                LogicalCPUThreads = $LogicalThreads
                VMs =  $ClusterVMs.count
                ActiveVMs =  $ClusterVMsPoweredOn.count
                VMvCPUs = $VMvCPUs
				vCPUpCPUratio = "$vCPUpCPUratio : 1"
                PhysicalMemoryGB = $PhysicalMemory
                AllocatedVMMemoryGB = $AllocatedVMMemoryGB        
				ClusterMemoryUsage = "$MemoryUsage %"
                SumVMDiskSpaceGB = [math]::round( ($reportDiskSpace | Measure-Object -Sum -Property CapacityGB).sum, 1 )
                SumDatastoreSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property CapacityGB).sum, 1 )
                SumDatastoreUsedSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property UsedSpaceGB).sum, 1 )
                AverageVMIOPSWriteAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSWriteAvg).Average,1 )
                AverageVMIOPSReadAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSReadAvg).Average,1 )
                AverageVMCPUUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property CPUUsageAvg).Average,1 )) %"
                AverageVMMEMUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property MEMUsageAvg).Average,1 )) %"
			}
		    $MyView += $SizingReport
            Write-Verbose "$(Get-Date -Format G) Process Global Report completed"
            #endregion
		}

        }
        Else {
            Write-Error "Validation Failed! Processing Skipped"
        }
        
    }

    End {
        $MyView
    }

}

 

Das Modul ist auch in meinem vSphere Modules GitHub Projekt zu finden:

My VMware vSphere Modules

 

 

Geplante Features

Die aktuelle Version ist für mich noch nicht das finale Ergebnis, dafür gibt es zu viele Faktoren in einem Sizingprozess.

Geplant sind aktuell folgende zusätzliche Werte:

  • VM Active Memory
  • VM Min / Max / Average vCPU Konfiguration
  • VM Min / Max / Average Memory Konfiguration
  • VM Min / Max / Average CPU Ready %
  • VM Min / Max / Average CPU MHz
  • Datstore Min / Max / Average Latency
  • Datstore Min / Max / Average IOPS

Bei den aktuellen Werten stehen folgende Änderungen aus:

  • HostsAverage*: Berechnung aus Stats statt Momentaufnahme

Geplante Verbesserungen

  • Bessere Ausgabe der Stats bzw. wenn keine Stats gefordert wurden diese weglassen

Wie arbeite ich mit den gesammelten Informationen

Auch wenn man mit dem Kunden „nur“ über das Sizing der Compute Ressourcen spricht kommt es sehr genau darauf an, welches Verfügbarkeits -, Disaster Recovery – und Backup – Konzept angestrebt wird. Natürlich darf auch nicht der Typ des Workloads außer Acht gelassen werden. Eine VDI oder Terminal Server Umgebung zum Beispiel agieren völlig anders als gewöhnliche Server Applikationen. Von den speziellen Anforderungen eines In Memory Datenbank Systems wie SAP HANA mal ganz abgesehen.

Dazu kommen dann noch Aspekte wie Lizenzierung oder Sicherheitszonen zum tragen. Beides kann erfordern, zusätzliche vSphere Cluster zu designen.

Zu guter letzt ist für mich immer noch sehr wichtig, welche Projekte über den geplanten Einsatzraum der neuen Hardware anstehen. Ein geplantes ERP Projekt z.B. kann einen enormen zuwachs an VMs bedeuten! Ebenso würde der geplante Einsatz von VMware vRealize Automation ganz andere Anforderungen an Skalierung, Agilität und Überwachung aufwerfen!

Am Ende können sehr viele Faktoren das Sizing beeinflussen:

  • Angestrebte Konsolidierungsrate
  • Angestrebte Überbuchung
  • Anforderungen der Anwendungen
  • Bereitstellungskonzept
  • Disaster Recovery Konzept
  • Backup Konzept
  • Anforderungen an die Verfügbarkeit
  • Netzwerkkonzept
  • Storagekonzept
  • Sicherheitsanforderungen
  • OS und Anwendungslizenzierung

Alles läuft also auf eines hinaus: Es muss unbedingt genau mit dem Kunden gesprochen werden und alle Faktoren beleuchtet werden!

Nichts desto trotz geben die gesammelten Daten aus dem Skript einen Anhaltspunkt an die minimalen Anforderungen an die Hardware.

Euer Input würde mir sehr helfen

Um das Skript noch hilfreicher zu machen, würde ich mir sehr freuen wenn ihr mir Rückmeldung geht. Mich würde interessieren welche Werte ihr für euer Sizing zu Rate zieht und wie Ihr das Skript verwendet, bzw. warum ihr es nicht verwenden könnt.

2 Comments

  1. Robert 20. Januar 2017
    • Markus Kraus 20. Januar 2017

Leave a Reply