We are currently using TeamCity for CI builds and we are trying to set up automated deployments as well.
The project I'm currently trying to deploy is a Windows service that sits under an F5 load balancer. In the future we would also like to automate the deployment of our IIS websites which also sit under the F5.
From TeamCity we can execute PowerShell scripts to unistall the windows service on the desired server, push our files to it, then reinstall the service.
However, I'm having trouble figuring out how to deal with the load balancer. We would want to disable 1 node at a time, watch for all the connections to drop, then deploy our code and bring the node back up.
This seems like it would be a very common issue, but I'm finding surprisingly little information about how to do it.
Thanks!
Answered
Thanks Jonathon Rossi for the iControl Powershell cmdlets!
For other users' sakes, here is a sample of shutting down, monitoring for connections to drop, pushing code, and then turning back on the F5 load balancer through a powershell script
For these scripts to work you will first have to install the F5 iControl cmdlets from the links provided in the Answer below
#PULL IN OUR F5 UTILITY FUNCTIONS
. .\F5Functions.ps1
#DEFINE LOGIC TO DEPLOY CODE TO A NODE THAT HAS ALREADY BEEN REMOVED FROM THE LOAD BALANCER
function Deploy(
[F5Node]$Node
)
{
Write-Host "Deploying To: "$Node.Name
#TODO: Remotely shut down services, push code, start back up services
}
#DEFINE NODES
$nodes = @()
$nodes += New-Object F5Node -ArgumentList @("TestNode1", "1.1.1.1")
$nodes += New-Object F5Node -ArgumentList @("TestNode2", "1.1.1.2")
#DEPLOY
DeployToNodes -Nodes $nodes -F5Host $F5Host -F5UserName $F5UserName -F5Password $F5Password
And here is the reusable F5Functions script
#Load the F5 powershell iControl snapin
Add-PSSnapin iControlSnapin;
Write-Host "Imported F5 function!!!"
Add-Type @'
public class F5Node
{
public F5Node(string name, string address){
Address = address;
Name = name;
}
public string Address {get;set;}
public string Name {get;set;}
public string QualifiedName {get{return "/Common/" + Name;}}
}
'@
function DeployToNodes(
[string]$F5Host = $(throw "Missing Required Parameter"),
[string]$F5UserName = $(throw "Missing Required Parameter"),
[string]$F5Password = $(throw "Missing Required Parameter"),
[F5Node[]]$Nodes = $(throw "Missing Required Parameter"),
[int]$MaxWaitTime = 300 #seconds... defaults to 5 minutes
){
Authenticate -F5Host $F5Host -F5UserName $F5UserName -F5Password $F5Password
foreach($node in $Nodes){
DisableNode -Node $node
WaitForConnectionsToDrop -Node $node -MaxWaitTime $MaxWaitTime
#Assume the Script that included this script defined a Deploy Method with a Node param
Deploy -Node $node
EnableNode -Node $node
}
}
function Authenticate(
[string]$F5Host = $(throw "Missing Required Parameter"),
[string]$F5UserName = $(throw "Missing Required Parameter"),
[string]$F5Password = $(throw "Missing Required Parameter")
)
{
Write-Host "Authenticating to F5..."
Initialize-F5.iControl -HostName $F5Host -Username $F5UserName -Password $F5Password
Write-Host "Authentication Success!!!"
}
function ParseStatistic(
[iControl.CommonStatistic[]]$StatsCollection = $(throw "Missing Required Parameter"),
[string]$StatName = $(throw "Missing Required Parameter")
)
{
for($i=0; $i -lt $StatsCollection.Count; $i++){
if($StatsCollection[$i].type.ToString() -eq $StatName){
return $StatsCollection[$i].value.low
break
}
}
}
function GetStats(
[F5Node]$Node = $(throw "Missing Required Parameter")
)
{
$arr = @($Node.QualifiedName)
$nodeStats = (Get-F5.iControl).LocalLBNodeAddressV2.get_statistics($arr)
return $nodeStats.statistics.statistics
#foreach($memberStats in $poolStats.statistics){
# if($memberStats.member.address.ToString() -eq $Node -and $memberStats.member.port -eq $Port){
# return $memberStats.statistics
# }
#}
}
function GetStatistic(
[F5Node]$Node = $(throw "Missing Required Parameter"),
[string]$StatName = $(throw "Missing Required Parameter")
)
{
$stats = GetStats -Node $Node
$stat = ParseStatistic -StatsCollection $stats -StatName $StatName
return $stat
}
function DisableNode(
[F5Node]$Node = $(throw "Missing Required Parameter")
)
{
Disable-F5.LTMNodeAddress -Node $Node.Address
Write-Host "Disabled Node '$Node'"
}
function EnableNode(
[F5Node]$Node = $(throw "Missing Required Parameter")
)
{
Enable-F5.LTMNodeAddress -Node $Node.Address
Write-Host "Enabled Node '$Node'"
}
function WaitForConnectionsToDrop(
[F5Node]$Node = $(throw "Missing Required Parameter"),
[int]$MaxWaitTime = 300
)
{
$connections = GetCurrentConnections -Node $Node
$elapsed = [System.Diagnostics.Stopwatch]::StartNew();
while($connections -gt 0 -and $elapsed.ElapsedMilliseconds -lt ($MaxWaitTime * 1000)){
Start-Sleep -Seconds 10
$connections = GetCurrentConnections -Node $Node
}
}
function GetCurrentConnections(
[F5Node]$Node = $(throw "Missing Required Parameter")
)
{
$connections = GetStatistic -Node $Node -StatName "STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS"
$name = $Node.Name + ":" + $Node.Address
Write-Host "$connections connections remaining on '$name'"
return $connections
}
I haven't used it, but have you looked at the F5 iControl web service API and the F5 iControl PowerShell cmdlets provided by F5. The PowerShell cmdlets have been around since 2007 and can be downloaded from F5 DevCentral.
It looks like there are Enable-Member
and Disable-Member
cmdlets that you'll be able to use.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With