How I made my Web Application highly available on VMWare using Powershell

Posted on

For the purpose of todays post we will be using the free load balancer that Kemp offers for evaluating products and functionalities. Today we are going to ensure our web application is highly available and behind two LoadMasters in a high availability configuration so that we can easily manage all the server nodes if required, spread the total number of connections across all of them, monitor traffic etc. We will achieve this by automating the entire process of deployment, licensing, and configuration, using PowerShell.

A little note on HA configuration (from website)

The High Availability (HA) feature of the LoadMaster guarantees the availability of your server farm. HA is achieved by a hot-standby, failover mechanism. Two identical LoadMaster units are integrated into the network as a cluster. One machine serves as the active LoadMaster and the second one remains in a standby, idle state – always prepared to take over the activities from the active server. This cluster appears as a single logical unit to the internet side and to the server farm side connections.

With a HA cluster, each network interface has an individual IP address and one shared IP address which is shared with the partner unit. The shared IP address is identical for both LoadMaster appliances, though it is associated with only the active LoadMaster at any given time.

The goal of redundant LoadMasters is to provide reliable traffic management, even if one LoadMaster becomes unavailable. The advantages of HA are as follows:

  • Eliminates a single point of failure.
  • The second (standby) unit monitors the active unit to detect if a failure has occurred.
  • Persistence can be kept using the HA parameters:
    • Inter HA L4 TCP Connection Updates
    • Inter HA L7 Persistency Updates

Disclaimer: the code and techniques explained below are free to use and distribute, however they came without any particular error handling in place. The reason of this is to prevent any additional complexity in the logic that we are going to implement and go straight to the point. All the validation and error handling can be implemented at second stage once the main flow and concepts are understood.

The image below shows the starting environment we are going to use for being converted in what we can see in the second image.

Image 1 – Starting environment
Image 2 – End result

Prerequisites:

  • vSphere access and credentials
  • A computer where PowerShell can be executed
  • A Kemp 360 Central MELA Trial deployed and running – All the information on how to deploy Central can be found here. We will work on the assumption that Central MELA Trial has been already deployed and is running (info below on where to download).
  • LoadMaster VLM deployment files (ovf and vmdk).
  • Kemp LoadMaster Powershell API wrapper.
  • Kemp 360 Central Powershell API wrapper (beta).
  • Kemp Application Template (based on your needs, in our example we will use Apache and IIS)
  • VMWare PowerCLI.
  • Our Web Application server IP

Let’s start with downloading all the necessary tools and librariesDownload Central and the LoadMaster VLM Deployment files

1 – Create a free kemp account if needed and download Kemp360Central for VMware and the LoadMaster VLM from here Extract all the content in a folder that will be used for testing i.e. C:\PowershellAutomationTest\VLM_Images. Please Note the zip file that will be downloaded for Central will contain both Central and the LoadMaster deployment files. Keep that LoadMaster deployment file aside for now since will not be used in these examples.

2 – Download the Kemp LoadMaster Powershell API wrapper from here

extract the content into a folder that can be easily accessible i.e. C:\PowershellAutomationTest\PowerShellWrapper

3 – Download the Kemp 360 Central Powershell API wrapper from here

4 – Download and install VMware PowerCLI Once we have everything setup and configured on the local machine let’s open Microsoft Powershell ISE. From the console navigate to the folder where we extracted all the materials i.e. C:\PowershellAutomationTest\

At this stage we should have in this folder

  • PowerShell LoadMaster APIs wrapper folder
  • PowerShell Central APIs wrapper folder
  • VLM folder within Central and LoadMaster deployment files (like image below)
Image 3 – LoadMaster and Central deployment files

The script I’m going to show you will follow these steps (approach 1)

  1. Import all the necessary modules for the execution
  2. Authenticate against the hypervisor
  3. Locate the OVF images and import in our hypervisor
  4. After this is done the machines needs to be activated then configured, these steps will consist in
    • Starting the Machine and wait until is reachable
    • Accepting the EULA
    • License Against the Kemp web services
    • Accepting the kemp analytics reports (or we will never be able to improve our amazing products ?)
    • Set the administrator password
    • Start the HA configuration

Change the values of the variables based on your needs and run the script

#
# $Id: HA LM Deployment in vSphere afabiano $
#

###
### Importing Modules
###
Import-Module ".\Powershell-7.2.49.1.1562\Kemp.LoadBalancer.Powershell.psm1"
Import-Module ".\Kemp.K360Central.Powershell\Kemp.K360Central.Powershell.psm1"

###
### Declaring Params Needed for logging in the LM
### bal is the default super user
### kempPassword must be decided by yuou
###
$KempAdmin = "bal"
$KempPassword = "YOUR_PASSWORD"

$LoadMasterPort = "443"

# LoadMaster HA1 Params
$LMHA1_Name = "PWS_DeviceHA1"

# LoadMaster HA2 Params
$LMHA2_Name = "PWS_DeviceHA2"

# Shared IP Params
$LMHA_TAG = "133"
$LMHA_SIP = "10.35.44.$($LMHA_TAG)"

# vSphere Params
$HypervisorIp = "10.35.0.16"
$HypervisorUser = " YOUR_VMWARE_USER "
$HypervisorPassword = "YOUR_VMWARE_PASSWORD"
$HypervisorResourcePool = "YOUR_RESOURCE_POOL"

# LoadMaster OVF Image Path
$VLMImagePath = "C:\Users\afabiano\Documents\PS\VLM_IMAGES\LoadMaster-VLM-7.2.49.1.18450.RELEASE-VMware-VBox-OVF.ovf"

# Kemp Account Credentials
$KempIDACcount = 'YOUR_KEMP_ID'
$KempIDPassword = 'YOUR_PASSWORD'

# Connecting to the Hypervisor
$HypervisorConnectionParams = @{
'Server' = $hypervisorIp
'User' = $hypervisorUser
'Password' = $hypervisorPassword
}
$hypervisorConnection = Connect-VIServer -Server $hypervisorIp -User $hypervisorUser -Password $hypervisorPassword
$VMHost = Get-VMHost
$HypervisorResourcePool = Get-ResourcePool -Name $HypervisorResourcePool

$params = @{
'ImagePath' = $VLMImagePath
'VLMName' = ""
'VMhost' = $VMHost
'VMport' = $LoadMasterPort
'ResourcePool' = $HypervisorResourcePool
'KempIDAccount' = $KempIDACcount
'KempIDPassword' = $KempIDPassword
'KempAdminPassword' = $KempPassword
}

###
### Deploy Machine 1
###
$params['VLMName'] = $LMHA1_Name
Deploy-Machine @params

###
### Deploy Machine 2
###
$params['VLMName'] = $LMHA2_Name
Deploy-Machine @params
Start-Sleep 10
###
### Get the VM Details
###
$LoadMasterHa1 = Get-VM -Name $LMHA1_Name
$LoadMasterHa2 = Get-VM -Name $LMHA2_Name

$LoadMasterHaFirstIP = $LoadMasterHa1.Guest.IPAddress[0]
$LoadMasterHaSecondIP = $LoadMasterHa2.Guest.IPAddress[0]

###
### Configure HA1
###
$HA1Params = @{
'KempAdmin' = $KempAdmin
'KempAdminPassword' = $KempPassword
'VMIP' = $LoadMasterHaFirstIP
'VMport' = $LoadMasterPort
'HAMode' = 'HA First'
'SharedIP' = $LMHA_SIP
'PartnerIP' = $LoadMasterHaSecondIP
}
Configure-HA @HA1Params

###
### Configure HA2
###
$HA2Params = @{
'KempAdmin' = $KempAdmin
'KempAdminPassword' = $KempPassword
'VMIP' = $LoadMasterHaSecondIP
'VMport' = $LoadMasterPort
'HAMode' = 'HA Second'
'SharedIP' = $LMHA_SIP
'PartnerIP' = $LoadMasterHaFirstIP
}
Configure-HA @HA2Params

###
### Restarting the machines for getting the Configuration working
###
Write-Output "Restarting Machines"
Restart-VMGuest -VM $LoadMasterHa1
Restart-VMGuest -VM $LoadMasterHa2

Start-Sleep 40
Write-Output "Machines ready"

###
### Deploy-Machine function
###
Function Deploy-Machine() {
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$ImagePath,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VLMName,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMhost,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMport,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$ResourcePool,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempIDAccount,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempIDPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdminPassword
)

Write-Output "Deploying Machine [$VLMName]"
Import-VApp -Source $ImagePath -Name $VLMName -VMHost $VMhost
Write-Output "Moving the device to Destination resource [$($ResourcePool)]"
Move-VM -VM $VLMName -Destination $ResourcePool
Start-VM -VM $VLMName
###
### At this stage the machine is starting we have to wait approx 20 secs
###
$VMDetails = Get-VM -Name $VLMName
while (!$VMDetails.Guest.IPAddress)
{
Write-Output "-"
Start-Sleep -s 5
$VMDetails = Get-VM -Name $VLMName
}
$VMIPDetials = $VMDetails.Guest.IPAddress
$VMIP = $VMIPDetials[0]
###
### Accepting the EULA
###

Write-Output "Accepting the EULA"
$LMResponse = Read-LicenseEULA -LoadBalancer $VMIP -LBPort $VMport
$MagicString = $LMResponse.Data.Eula.MagicString
$LMResponse = Confirm-LicenseEULA -LoadBalancer $VMIP -LBPort $VMport -Magic $MagicString
$MagicString = $LMResponse.Data.Eula2.MagicString
###
### Licensing the device Against the Kemp web services
###
Write-Output "Licensing"
$LMResponse = Request-LicenseOnline -LoadBalancer $VMIP -LBPort $VMport -KempId $KempIDAccount -Password $KempIDPassword
###
### Accepting the Kemp analytics reports (or we will never be able to improve our amazing products:) )
###
Write-Output "Analytics"
$LMResponse = Confirm-LicenseEULA2 -LoadBalancer $VMIP -LBPort $VMport -Magic $MagicString -Accept yes
###
### Set the initial password
###
Write-Output "Initial password"
$LMResponse = Set-LicenseInitialPassword -LoadBalancer $VMIP -LBPort $VMport -Passwd $KempAdminPassword
}

Function Configure-HA() {
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdminPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMport,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$HAMode,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$SharedIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$PartnerIP
)
$SecureString = ConvertTo-SecureString -String $KempAdminPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString

Write-Output "Connecting to the device [$LMIP]:$LMPort"
Write-Output "------------------------------"
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMport -Credential $LMCredentials

Write-Output "Setting HA Mode [$HAMode]"
Write-Output "------------------------------"
Set-LmHAMode -HaMode $HAMode
Set-NetworkInterface -InterfaceID 0 -Shared $SharedIP

# Waith 15 secs
Write-Output "Wait after setting the Shared ip on [$LMIP] -> $($SharedIP)"
Write-Output "------------------------------"
Start-Sleep -s 15

# Configuring the Partner IP
Write-Output "Setting the Partner IP on [$LMIP] -> $PartnerIP"
Write-Output "------------------------------"
Set-NetworkInterface -InterfaceID 0 -Partner $PartnerIP
Write-Output "configured the partner on device 2" + $PartnerIP
Write-Output "------------------------------"
#Write-Output "========================================================="
Start-Sleep -s 10

#Set-LMHAConfiguration -havhid $HAID
Write-Output "Device Configured"
}

At the end of the execution of this script we should have the two LoadMaster configured in HA. As we can see from the image below the top one is the Active Master, the middle one is the Standby unit and the last one is the Shared IP UI.

Image 4 – LoadMasters in HA

What is left is at this stage is the web application connection part that will have these steps:

  • Upload a template
  • Create a VS
  • Link my web application real server(s) to the VS created
  • Test ?

Upload a template can be executed by this command

Install-Template -Path file_path Credentials

In our script we will have this piece of code that is dealing with that, and personally, I prefer to use a function so that can be reused later. Instead of calling this function against the SharedIP device, we can leverage the LoadMaster HA functionality and apply a small change to our flow. What we can do is to split the flow in 2, where the first part will configure the first device with all we need (Template for the web application, create the VirtualServices and add one or more Real Servers). The second step is to create the second machine and start the HA configuration so that the entire VS configuration will be replicated as soon the two machines are in sync.

The new flow will then be as follow:

  • Deploy machine 1 as for the previous steps
  • Configure machine 1
    • upload the template (one function)
    • create the VS configuration (one function)
    • Add the real servers
  • Deploy Machine 2
  • Configure HA
  • Test

For doing that we had to slightly change the previous script that now will looks like this

#
# $Id: HA LM Deployment in vSphere afabiano $
#

###
### Importing Modules
###
Import-Module ".\Powershell-7.2.49.1.1562\Kemp.LoadBalancer.Powershell.psm1"
Import-Module ".\Kemp.K360Central.Powershell\Kemp.K360Central.Powershell.psm1"

###
### Declaring Params Needed
###
$KempAdmin = "bal"
$KempPassword = " YOUR_PASSWORD "

$LoadMasterPort = "443"

# LoadMaster HA1 Params
$LMHA1_Name = "PWS_DeviceHA1"

# LoadMaster HA2 Params
$LMHA2_Name = "PWS_DeviceHA2"

# Shared IP Params
$LMHA_TAG = "133"
$LMHA_SIP = "10.35.34.$($LMHA_TAG)"

# vSphere Params
$HypervisorIp = "YOUR_HYPERVISOR_IP"
$HypervisorUser = " YOUR_HYPERVISOR_USER"
$HypervisorPassword = "YOUR_HYPERVISOR_USER_PASSWORD"
$HypervisorResourcePool = "YOUR_RESOURCE_POOL"
$NetworkPool = "YOUR_NETWORK_POOL"

# LoadMaster OVF Image Path
$VLMImagePath = "OVF IMAGE LOCATION\filename.ovf"

# Template Path
$TemplatePath = "TEMPLATE_PATH\apache-http.tmpl"

# Kemp Account Credentials
$KempIDACcount = 'KEMPID'
$KempIDPassword = 'KEMPID_PASSWORD'

# VS Parameter ## to be changed based on your needs
$VSIP = '10.35.34.12'
$VSPort = '443'
$VSProtocol = 'tcp'
$TemplateName = 'Apache HTTPS Offloaded'

# RS Parameter ## to be changed based on your needs
$RS1IP = '10.35.34.11'
$RS1Port = '80'
$RS2IP = '10.35.34.13'
$RS2Port = '80'

###
### Deploy-Machine function
###
Function Deploy-Machine() {
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$ImagePath,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VLMName,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMhost,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMport,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$ResourcePool,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempIDAccount,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempIDPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdminPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$NetworkPool
)

Write-Output "Deploying Machine [$VLMName]"
Import-VApp -Source $ImagePath -Name $VLMName -VMHost $VMhost
Write-Output "Moving the device to Destination resource [$($ResourcePool)]"
Move-VM -VM $VLMName -Destination $ResourcePool
###
### Change the network
###
Get-vm $VLMName | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $NetworkPool -Confirm:$false
Start-VM -VM $VLMName
###
### At this stage the machine is starting we have to wait approx 20 secs
###
$VMDetails = Get-VM -Name $VLMName
while (!$VMDetails.Guest.IPAddress)
{
Write-Output "-"
Start-Sleep -s 5
$VMDetails = Get-VM -Name $VLMName
}
$VMIPDetials = $VMDetails.Guest.IPAddress
$VMIP = $VMIPDetials[0]
###
### Accepting the EULA
###

Write-Output "Accepting the EULA"
$LMResponse = Read-LicenseEULA -LoadBalancer $VMIP -LBPort $VMport
$MagicString = $LMResponse.Data.Eula.MagicString
$LMResponse = Confirm-LicenseEULA -LoadBalancer $VMIP -LBPort $VMport -Magic $MagicString
$MagicString = $LMResponse.Data.Eula2.MagicString
###
### Licensing the device Against the Kemp web services
###
Write-Output "Licensing"
$LMResponse = Request-LicenseOnline -LoadBalancer $VMIP -LBPort $VMport -KempId $KempIDAccount -Password $KempIDPassword
###
### Accepting the Kemp analytics reports (or we will never be able to improve our amazing products:) )
###
Write-Output "Analytics"
$LMResponse = Confirm-LicenseEULA2 -LoadBalancer $VMIP -LBPort $VMport -Magic $MagicString -Accept yes
###
### Set the initial password
###
Write-Output "Initial password"
$LMResponse = Set-LicenseInitialPassword -LoadBalancer $VMIP -LBPort $VMport -Passwd $KempAdminPassword
}

Function Configure-HA() {
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdminPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMport,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$HAMode,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$SharedIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$PartnerIP
)
$SecureString = ConvertTo-SecureString -String $KempAdminPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString

Write-Output "Connecting to the device [$LMIP]:$LMPort"
Write-Output "------------------------------"
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMport -Credential $LMCredentials

Write-Output "Setting HA Mode [$HAMode]"
Write-Output "------------------------------"
Set-LmHAMode -HaMode $HAMode
Set-NetworkInterface -InterfaceID 0 -Shared $SharedIP

# Waith 15 secs
Write-Output "Wait after setting the Shared ip on [$LMIP] -> $($SharedIP)"
Write-Output "------------------------------"
Start-Sleep -s 15

# Configuring the Partner IP
Write-Output "Setting the Partner IP on [$LMIP] -> $PartnerIP"
Write-Output "------------------------------"
Set-NetworkInterface -InterfaceID 0 -Partner $PartnerIP
Write-Output "configured the partner on device 2" + $PartnerIP
Write-Output "------------------------------"
#Write-Output "========================================================="
Start-Sleep -s 10

#Set-LMHAConfiguration -havhid $HAID
Write-Output "Device Configured"
}

Function Upload-Template-To-LM() {
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$TemplateLocation,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMport,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdminPassword
)
$SecureString = ConvertTo-SecureString -String $KempAdminPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMport -Credential $LMCredentials
Install-Template -Path $TemplateLocation
}

Function Create-VS()
{
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSProtocol,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$TemplateName
)
$SecureString = ConvertTo-SecureString -String $KempPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMPort -Credential $LMCredentials
###
### Add VS
###
New-AdcVirtualService -VirtualService $VSIP -VSPort $VSPort -VSProtocol $VSProtocol -Template $TemplateName
}

Function Create-RS()
{
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSProtocol,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$RSIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$RSPort
)
$SecureString = ConvertTo-SecureString -String $KempPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMPort -Credential $LMCredentials
###
### Add the RealServers
###
New-AdcRealServer -VirtualService $VSIP -VSPort $VSPort -VSProtocol $VSProtocol -RealServer $RSIP -RealServerPort $RSPort -Non_Local $true
}

# Connecting to the Hypervisor
$HypervisorConnectionParams = @{
'Server' = $hypervisorIp
'User' = $hypervisorUser
'Password' = $hypervisorPassword
}
$hypervisorConnection = Connect-VIServer -Server $hypervisorIp -User $hypervisorUser -Password $hypervisorPassword
$VMHost = Get-VMHost
$HypervisorResourcePool = Get-ResourcePool -Name $HypervisorResourcePool

###
### Deploy Machine 1
###
$params = @{
'ImagePath' = $VLMImagePath
'VLMName' = ""
'VMhost' = $VMHost
'VMport' = $LoadMasterPort
'ResourcePool' = $HypervisorResourcePool
'KempIDAccount' = $KempIDACcount
'KempIDPassword' = $KempIDPassword
'KempAdminPassword' = $KempPassword
'NetworkPool' = $NetworkPool
}
$params['VLMName'] = $LMHA1_Name
Deploy-Machine @params
###
### Get the VM Details
###
$LoadMasterHa1 = Get-VM -Name $LMHA1_Name
### create a snapshot
New-Snapshot -VM $LoadMasterHa1 -Name "TestSnapshotForHA1" -Description "Automated Snapshot" -Quiesce -Memory
$LoadMasterHaFirstIP = $LoadMasterHa1.Guest.IPAddress[0]

###
### Configure
###
$TemplateParams = @{
'TemplateLocation' = $TemplatePath
'VMIP' = $LoadMasterHaFirstIP
'VMport' = $LoadMasterPort
'KempAdmin' = $KempAdmin
'KempAdminPassword' = $KempPassword
}
Upload-Template-To-LM @TemplateParams

###
### Add the VS and the RealServers
###
$VSPArams = @{
'VMIP' = $LoadMasterHaFirstIP
'VMPort' = $VMport
'KempAdmin' = $KempAdmin
'KempPassword' = $KempPassword
'VSIP' = $VSIP
'VSPort' = $VSPort
'VSProtocol' = $VSProtocol
'TemplateName' = $TemplateName
}
Create-VS @VSPArams

###
### Add the RS to the Virtual Service created
###
$RSPArams = @{
'VMIP' = $LoadMasterHaFirstIP
'VMPort' = $VMport
'KempAdmin' = $KempAdmin
'KempPassword' = $KempPassword
'VSIP' = $VSIP
'VSPort' = $VSPort
'VSProtocol' = $VSProtocol
'RSIP' = $RS1IP
'RSPort' = $RS1Port
}
Create-RS @RSParams
### Add second Real Server
$RSPArams = @{
'VMIP' = $LoadMasterHaFirstIP
'VMPort' = $VMport
'KempAdmin' = $KempAdmin
'KempPassword' = $KempPassword
'VSIP' = $VSIP
'VSPort' = $VSPort
'VSProtocol' = $VSProtocol
'RSIP' = $RS2IP
'RSPort' = $RS2Port
}
Create-RS @RSParams

###
### Deploy Machine 2
###
$params['VLMName'] = $LMHA2_Name
Deploy-Machine @params
Start-Sleep 10

###
### Get the VM Details
###
$LoadMasterHa2 = Get-VM -Name $LMHA2_Name
### create a snapshot
New-Snapshot -VM $LoadMasterHa2 -Name "TestSnapshotForHA2" -Description "Automated Snapshot" -Quiesce -Memory

$LoadMasterHaSecondIP = $LoadMasterHa2.Guest.IPAddress[0]

###
### Configure HA1
###
$HA1Params = @{
'KempAdmin' = $KempAdmin
'KempAdminPassword' = $KempPassword
'VMIP' = $LoadMasterHaFirstIP
'VMport' = $LoadMasterPort
'HAMode' = 'HA First'
'SharedIP' = $LMHA_SIP
'PartnerIP' = $LoadMasterHaSecondIP
}
Configure-HA @HA1Params

###
### Configure HA2
###
$HA2Params = @{
'KempAdmin' = $KempAdmin
'KempAdminPassword' = $KempPassword
'VMIP' = $LoadMasterHaSecondIP
'VMport' = $LoadMasterPort
'HAMode' = 'HA Second'
'SharedIP' = $LMHA_SIP
'PartnerIP' = $LoadMasterHaFirstIP
}
Configure-HA @HA2Params

###
### Restarting the machines for getting the Configuration working
###
Write-Output "Restarting Machines"
Restart-VMGuest -VM $LoadMasterHa1
Restart-VMGuest -VM $LoadMasterHa2

Start-Sleep 40
Write-Output "Machines ready"

At the end of this script execution we will have this

Image 5 – VS configuration page

If we try to reach the VS IP 10.35.34.12 we will see our web app

Image 6 – Web App homepage

For testing, you can do this manually just to verify that everything is working as expected, but you can also use Apache bench to verify and check how the system behaves under stress. Let’s give it a quick run

I executed Apache Bench with these parameters

-n 10000 -c 500 while executing the RS page looked like this

Image 7 – RS Page during the test

Virtual Service Stats

Image 8 – Virtual Service Stats page

As you can see from above, in the image 8 all the connections were spread over the 2 RSs

At this stage we have our application up and running in an HA environment in VMware using PowerShell for automating the entire deployment.

Now that we understand what is really involved in such a deployment there is yet another method for doing this deployment that actually, will consists in less steps and will allow us to get more control in some cases prior to the machine deployment i.e. assign in advance IP address so that will not conflict, assign Gateway etc.

This method is using the Kemp360 Central functionality for autolicense and deploy machines in VMware, KVM, XEN. Assuming that your Central is up and running and was activated with the MELA Trial license, the steps we are going to follow will be a little different. What we want to achieve is to have the same configuration of our previous example but this time using Kemp360 Central, that will consist in less steps and less complexity because Central will take care of the VLM image import , instantiation and activation, this means that all that “complexity” is removed from our scripts  because Central will do that for us. Therefore, the steps will be:

  • Create the HA LoadMaster Configuration from Central
  • Deploy to a specific Target Environment (In our initial case will be both on VMWare)
  • Apply the VS configuration
  • Test

The feature we are going to leverage is the LoadMaster Deployment and if you want, you can go through the steps using the UI first that will drive you through the process of creating one or more devices and decide how to deploy (manual approach – download and instantiate, automatic – Central will do everything for us), this will help you in getting familiar with what is happening. There is only one prerequisite that must be met for this flow and is that we should login to Central and create at least one target environment. For doing that, login to Central, navigate to the Target Environments section (see image 10 below), click on the Create new and start filling the required parameters (see image 11). The save button will became available only if the check of the credentials has been successfully accomplished. Once we have the target environment correctly created we can move back to PowerShell. 

Image 10 – LoadMaster Deployment
Image 10 – Target Environment creation

Our Powershell script will initially consist in three functions

  • Create the HA configuration
  • Deploy against VMWare
  • Configure the VSs (we already have this part from the previous approach)
  • Test

For being able to do that we will need to know some details prior to the deployment that are:

  • The network name that we want to assign to the VM that is defined in VMWare
  • The resource pool name
  • The datastore name

Kemp360 Central will map to the correct VMWare IDs and schedule the VMs for deployment with the right parameters.

The script below is a quick test to verify that everything is working as expected with a single machine. The initial import section and variable declaration is the same of our previous examples, below you will find only the call to the function that are responsible for creating the profile and deploy. Please note that creation of profile and deployment are two separate steps so that you can, for example, create first 10 profiles and then decide to deploy with calm at later stage.

###
### Connect and authenticate against central
###
Initialize-K360Central -Address $CentralIP -User $CentralAdmin -Password $CentralPassword -Port 443

###
### Create a LoadMaster deployment instance in Central
### assigning in advance network parameters
###
$LMDeploymentParams = @{
'HAMode' = "SA"
'DeploymentName' = "TestDeploymentFromPWS6"
'DeploymentDescription' = "Test"
'Gateway' = "10.35.34.1"
'Eth0Device1IP' = "10.35.34.120/24"
'WUIPort' = "443"
'Nameserver' = ""
}
$obj = Create-LoadMaster-Deployment @LMDeploymentParams
$DeviceProfileID = $obj.Data.id

###
### Deploy the profile created
###
$params = @{
'TargetEnvironmentName' = 'vCenterPWS'
'ResourcePool'='afabiano'
'NetworkName'='Antonio_34'
'DataStore'='THEALLSPARK'
'AutoPowerOn'='no'
'DeviceProfileId' = $DeviceProfileID
}
$InstanceID = LMInstance-Prepare @params

LMInstance-Deploy -InstanceID $InstanceID -Verbose

After few minutes the machine will be in your environment, turned on/off depending on your preferences and automatically connected to Central as the image below

Image 11 – LoadMaster console boot message
Image 12 – LoadMaster correctly connected to Central

Now is time to make things spicier, let’s automate our previous LoadMaster HA configuration with the template instantiation, connect to Central, and start monitoring the traffic spikes. Our script will be as simple as:

  • Step 1: Deploy with Central
  • Step 2: Configure the VS Template
  • Step3: No step 3

At the end of the execution of this script (in a matter of minutes) we will have our entire environment up and running and fault tolerant. All I had to do is to merge our two scripts and apply some minor adjustments. In particular I had to add a function that is looking for the Virtual Machines to be up a running with the IP accessible. This is because the deployment of the virtual device from Central happen in an asynchronous way to avoid having the user locked on the deployment page for few minutes. The other small adjustment is related to the virtual device names. When we deploy HA pairs with Central in any hypervisor, it will automatically attach the “-1” and “-2” labels at the end of the device name, so that can be distinguished in the hypervisor. For example, if I decide to call my deployment as “superTest” and is an HA configuration, Central will create in the hypervisor superTest-1 and superTest-2. I had to take this in consideration for the function that is waiting the machines to be deployed and ready. This is then the end result (I’m leaving the IP of the interfaces and Devices, everything else should be replaced with your configuration):

#
# $Id: HA LM Deployment in vSphere afabiano $
#
###
### Importing Modules
###
Import-Module ".\Powershell-7.2.49.1.1562\Kemp.LoadBalancer.Powershell.psm1"
Import-Module ".\Kemp.K360Central.Powershell\Kemp.K360Central.Powershell.psm1" -Force

###
### Declaring Params Needed
###
$KempAdmin = "bal"
$KempPassword = "Admin1234"

###
### Central Parameters
###
$CentralAdmin = "admin"
$CentralPassword = "YOUR_CENTRAL_PASSWORD"
$CentralIP = " YOUR_CENTRAL_IP"
$TargetEnvironmentName = "TARGET_ENVIRONMENT_NAME"

$LoadMasterPort = "LOADMASTER_UI_PORT"
$DeploymentName = "DEPLOYMENT_NAME"
$DefaultGateway = "YOUR_DEFAULT_GATEWAY"
$LicenseName = "VLM-MAX"

# LoadMaster HA1 Params in the format ip/cidr
$Eth0Device1IP = "10.35.34.111/24"

# LoadMaster HA2 Params in the format ip/cidr
$Eth0Device2IP = "10.35.34.112/24"

# Shared IP Params
$LMHA_TAG = "143"
$LMHA_SIP = "10.35.34.$($LMHA_TAG)"

# vSphere Params
$HypervisorIp = "YOUR_HYPERVISOR_IP"
$HypervisorUser = " YOUR_HYPERVISOR_USER"
$HypervisorPassword = "YOUR_PASSWORD"
$HypervisorResourcePool = "YOUR_RESOURCE_POOL"
$NetworkPool = "YOUR_NETWORK_POOL"
$DataStore = "YOUR_DATASTORE"
$AutoPowerOn = 'yes'

# Template Path
$TemplatePath = "ABSOLUTE_PATH\TO\THE\TEMPLATE\apache-http.tmpl"

# VS Parameter
$VSIP = '10.35.34.12'
$VSPort = '443'
$VSProtocol = 'tcp'
$TemplateName = 'Apache HTTPS Offloaded'

# RS Parameter
$RS1IP = '10.35.34.11'
$RS1Port = '80'
$RS2IP = '10.35.34.13'
$RS2Port = '80'

Function Upload-Template-To-LM() {
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$TemplateLocation,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMport,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdminPassword
)
$SecureString = ConvertTo-SecureString -String $KempAdminPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMport -Credential $LMCredentials
Install-Template -Path $TemplateLocation
}

Function Create-VS()
{
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSProtocol,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$TemplateName
)
$SecureString = ConvertTo-SecureString -String $KempPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMPort -Credential $LMCredentials
###
### Add VS
###
New-AdcVirtualService -VirtualService $VSIP -VSPort $VSPort -VSProtocol $VSProtocol -Template $TemplateName
}

Function Create-RS()
{
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VMPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempAdmin,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$KempPassword,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSPort,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VSProtocol,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$RSIP,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$RSPort
)
$SecureString = ConvertTo-SecureString -String $KempPassword -AsPlainText -Force
$LMCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $KempAdmin, $SecureString
Initialize-LmConnectionParameters -Address $VMIP -LBPort $VMPort -Credential $LMCredentials
###
### Add the RealServers
###
New-AdcRealServer -VirtualService $VSIP -VSPort $VSPort -VSProtocol $VSProtocol -RealServer $RSIP -RealServerPort $RSPort -Non_Local $true
}

Function Wait-Machines()
{
Param(
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VLMName1,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$VLMName2,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$hypervisorIp,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$hypervisorUser,
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]$hypervisorPassword
)
$hypervisorConnection = Connect-VIServer -Server $hypervisorIp -User $hypervisorUser -Password $hypervisorPassword
$VMHost = Get-VMHost
###
### At this stage the machine is starting we have to wait approx 20 secs
###
Write-Output $VLMName1

$VMDetails1 = Get-VM -Name $VLMName1
$VMDetails2 = Get-VM -Name $VLMName2
while (!$VMDetails1.Guest.IPAddress -and !$VMDetails2.Guest.IPAddress)
{
Write-Output "-"
Write-Output "waiting machine $($VLMName1)"
Start-Sleep -s 10
$VMDetails1 = Get-VM -Name $VLMName1
$VMDetails2 = Get-VM -Name $VLMName2
if ($VMDetails1.PowerState -ne "PoweredOn")
{
$v1 = Start-VM $VLMName1
}
if ($VMDetails2.PowerState -ne "PoweredOn")
{
$v2 = Start-VM $VLMName2
}
}
Start-Sleep -s 5
}

###
### Connect and authenticate against Central
###
Initialize-K360Central -Address $CentralIP -User $CentralAdmin -Password $CentralPassword -Port 443

###
### Create a LoadMaster deployment instance in Central
### assigning in advance network parameters
###
$LMDeploymentParams = @{
'HAMode' = "HA"
'DeploymentName' = $DeploymentName
'DeploymentDescription' = "HA Test"
'Gateway' = $DefaultGateway
'Eth0Device1IP' = $Eth0Device1IP
'Eth0Device2IP' = $Eth0Device2IP
'SharedIP1' = $LMHA_SIP
'WUIPort' = $LoadMasterPort
'Nameserver' = ""
'LicenseName' = $LicenseName
}
$obj = Create-LoadMaster-Deployment @LMDeploymentParams
$DeviceProfileID = $obj.Data.id

###
### Prepare the Deployment
###
$params = @{
'TargetEnvironmentName' = $TargetEnvironmentName
'ResourcePool' = $HypervisorResourcePool
'NetworkName' = $NetworkPool
'DataStore' = $DataStore
'AutoPowerOn' = $AutoPowerOn
'DeviceProfileId' = $DeviceProfileID
}
$InstanceID = LMInstance-Prepare @params

###
### Deploy the machine
### And wait 2 mins for the images to be uploaded
###
LMInstance-Deploy -InstanceID $InstanceID
Start-Sleep -s 240
###
### Prepare the Deployment
###
$params = @{
'VLMName1' = "$($DeploymentName)-1"
'VLMName2' = "$($DeploymentName)-2"
'hypervisorIp' = $HypervisorIp
'hypervisorUser' = $HypervisorUser
'hypervisorPassword' = $HypervisorPassword
}
Wait-Machines @params

###
### Configure
###
$TemplateParams = @{
'TemplateLocation' = $TemplatePath
'VMIP' = $LMHA_SIP
'VMport' = $LoadMasterPort
'KempAdmin' = $KempAdmin
'KempAdminPassword' = $KempPassword
}
Upload-Template-To-LM @TemplateParams

###
### Add the VS and the RealServers
###
$VSPArams = @{
'VMIP' = $LMHA_SIP
'VMPort' = $LoadMasterPort
'KempAdmin' = $KempAdmin
'KempPassword' = $KempPassword
'VSIP' = $VSIP
'VSPort' = $VSPort
'VSProtocol' = $VSProtocol
'TemplateName' = $TemplateName
}
Create-VS @VSPArams

###
### Add the RS to the Virtual Service created
###
$RSPArams = @{
'VMIP' = $LMHA_SIP
'VMPort' = $LoadMasterPort
'KempAdmin' = $KempAdmin
'KempPassword' = $KempPassword
'VSIP' = $VSIP
'VSPort' = $VSPort
'VSProtocol' = $VSProtocol
'RSIP' = $RS1IP
'RSPort' = $RS1Port
}
Create-RS @RSParams
### Add second Real Server
$RSPArams = @{
'VMIP' = $LMHA_SIP
'VMPort' = $LoadMasterPort
'KempAdmin' = $KempAdmin
'KempPassword' = $KempPassword
'VSIP' = $VSIP
'VSPort' = $VSPort
'VSProtocol' = $VSProtocol
'RSIP' = $RS2IP
'RSPort' = $RS2Port
}
Create-RS @RSParams

After few minutes this is what we will see in Kemp360 Central

Image 13 – HA Configuration in Central after the PowerShell execution

Below instead is the LoadMaster UI after accessing the Shared IP (10.35.34.143)

Image 14 – LoadMaster VS Configuration

Now is time to re-run the Apache bench but with the value a bit increased since we need some sort of steady traffic. Below is how the Kemp360 Central Dashboard looked like during the test execution

Image 15 – Central Dashboard during the test execution
Image 16 – LoadMaster Realtime statistics for RS and vS
Image 17 – Central Network traffic graphs during the test

I hope you enjoyed this article!

Posted on

Antonio Fabiano

Antonio is the Software Architect and Design Authority for the Kemp360 Central product with a boundless passion for problem solving, programming and technologies learning. He started in 2000 as freelance software developer and through the years he jumped from one technology to another. In his free time he loves programming outside the love for SSC Napoli.