HAProxy Uitvoeren in Windows
Algemeen
Om de gebouwde haproxy.exe op Windows uit te voeren, zijn er een paar opties. Je kan het interactief uitvoeren in een opdrachtpromptvenster, het starten via een geplande taak of wanneer je een wrapper gebruikt, kan je het zelfs als een Windows-service uitvoeren.
Het hangt een beetje af van hoe of waarom u HAProxy wilt gebruiken. Als je het gewoon wilt uitvoeren om wat dingen te testen, is de interactieve manier in een opdrachtpromptvenster misschien de gemakkelijkste optie. Als u het echter in een laboratorium- of testomgeving wilt uitvoeren om toegang te krijgen tot verschillende web bronnen erachter, dan is de betere optie om het als een service uit te voeren of het te starten via een geplande taak.
Om HAProxy als een service uit te voeren, heb je een soort wrapper nodig. Voorbeelden van dergelijke wrappers zijn: SrvStart, NSSM enzovoort. Als je er op internet naar zoekt, vind je er nog veel meer. Omdat ik niet afhankelijk wil zijn van meer freeware-software dan nodig is, geef ik de voorkeur aan de optie om HAProxy via een geplande taak uit te voeren, omdat het uiteindelijk precies hetzelfde gedrag zal vertonen. Daarom raad ik geen van de genoemde beschikbare wrappers aan. Ik zal ook niet beschrijven hoe je zulke wrapper moet gebruiken.
In mijn eigen setup en in de voorbeelden die gebruikt worden in dit artikel, wordt volgende folderstructuur gebruikt:
E:\HAProxy
├───bin
├───Certs
├───Conf
├───Log
└───SysLog
- bin - Deze folder bevat de programma bestanden (haproxy.exe en de benodigde DLL's)
- Certs - In deze folder staan de certificaten die gebruikt worden voor SSL offloading.
- Conf - Deze folder bevat de configuratie bestanden.
- Log - Een folder die gebruikt wordt voor de log bestanden van het script dat gebruikt wordt om HAProxy te manipuleren.
- Syslog - In het configuratiebestand kan loggen naar een syslog-server worden geconfigureerd. In deze map worden de syslog-logbestanden opgeslagen.
Ik start alle commando's relatief ten opzichte van het root pad (E:\HAProxy in this example). Naar de executable wordt verwezen als ".\bin\haproxy.exe", het configuratiebestand als ".\Conf\haproxy.conf", de certifcaten als ".\Certs\Certificate.pem", enz.
HAProxy Interactief Uitvoeren
Als je in een opdrachtprompt haproxy.exe uitvoert met de "-?" parameter, worden alle parameters en de beschikbare weergegeven (voorbeeld van v2.6.6):
HAProxy version 2.6.6-274d1a4 2022/09/22 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2027.
Known bugs: http://www.haproxy.org/bugs/bugs-2.6.6.html
Running on: CYGWIN_NT-10.0-20348 3.3.6-341.x86_64 2022-09-05 11:15 UTC x86_64
Usage : haproxy [-f <cfgfile|cfgdir>]* [ -vdVD ] [ -n <maxconn> ] [ -N <maxpconn> ]
[ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]
-v displays version ; -vv shows known build options.
-d enters debug mode ; -db only disables background mode.
-dM[<byte>,help,...] debug memory (default: poison with <byte>/0x50)
-V enters verbose mode (disables quiet mode)
-D goes daemon ; -C changes to <dir> before loading files.
-W master-worker mode.
-q quiet mode : don't display messages
-c check mode : only check config files and exit
-cc check condition : evaluate a condition and exit
-n sets the maximum total # of connections (uses ulimit -n)
-m limits the usable amount of memory (in MB)
-N sets the default, per-proxy maximum # of connections (0)
-L set local peer name (default to hostname)
-p writes pids of all children to this file
-dp disables poll() usage even when available
-dK{class[,...]} dump registered keywords (use 'help' for list)
-dr ignores server address resolution failures
-dV disables SSL verify on servers side
-dW fails if any warning is emitted
-dD diagnostic mode : warn about suspicious configuration statements
-sf/-st [pid ]* finishes/terminates old pids.
-x <unix_socket> get listening sockets from a unix socket
-S <bind>[,<bind options>...] new master CLI
Voorbeelden
Versie controle:
Met het volgende commando kan je de versie van de haproxy.exe opvragen.
E:\HAProxy\bin>haproxy.exe -v
HAProxy version 2.6.6-274d1a4 2022/09/22 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2027.
Known bugs: http://www.haproxy.org/bugs/bugs-2.6.6.html
Running on: CYGWIN_NT-10.0-20348 3.3.6-341.x86_64 2022-09-05 11:15 UTC x86_64
Validatie van het configuratiebestand:
Met het volgende commando, kan je valideren of de syntax van het configuratiebestand Ok is.
E:\HAProxy>.\bin\haproxy.exe -c -f .\Conf\haproxy.cfg
Configuration file is valid
HAProxy uitvoeren:
De simpelste manier om HAProxy uit te voeren is als volgend:
E:\HAProxy>.\bin\haproxy.exe -f .\Conf\haproxy.cfg
Dit commando start als een achtergrond proces. Dit is gelijkaardig aan het volgende commando:
E:\HAProxy>.\bin\haproxy.exe -f .\Conf\haproxy.cfg -D
De enige manier om HAProxy in dit geval te stoppen, is door het proces te beëindigen via taakbeheer. Waarschuwingen, fouten en andere berichten zijn op deze manier niet zichtbaar. Door de parameter "-db" toe te voegen in plaats van "-D", wordt HAProxy interactief uitgevoerd en worden de berichten weergegeven in het opdrachtpromptvenster.
E:\HAProxy>.\bin\haproxy.exe -f .\Conf\haproxy.cfg -db
[WARNING] (1449) : Server SERVERNAME/SERVERNAME is DOWN, reason: Layer4 timeout, check duration: 2014ms. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
[ALERT] (1449) : backend 'BACKENDNAME' has no server available!
Waarschuwingen en alerts hierboven zijn enkel voorbeelden uiteraard ;-)
In dit geval kan het programma gestopt worden met CTRL-C.
HAProxy uitvoeren via een Scheduled Task
Het PowerShell Script
Om HAPRoxy via een scheduled task uit te voeren en om het programma te manipuleren (Starten, stoppen, ...), heb ik een PowerShell script geschreven.
De code van het script "HAProxy.ps1":
#Requires -RunAsAdministrator
#Requires -Version 7.0
Param (
# Path to configuration file.
[string[]]
$ConfigFile = ".\HAProxyConfig.xml",
# Requested action to take: Stop, Start or Restart. Default will be Start or Restart depending on whether HAProxy is running or not.
[string[]]
$RequestedAction
)
function EndScript {
Write-Log -Text "Script [$scriptName.ps1] finished." -File_LOG $LogFile
Write-Log -Text "=========================================================================================" -File_LOG $LogFile
Pop-Location
Pop-Location
Exit
}
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path
Push-Location $ScriptPath
. .\Write-Log.ps1
# Setting logging parameters ($LogFile)
$scriptName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
$LogFilename = "$scriptName.log"
$LogFile = "$PSScriptRoot\Log\$LogFilename"
Write-Log -Text "Script [$scriptName.ps1] started ..." -File_LOG $LogFile
Write-Log -Text "Running on PowerShell version: $($Host.Version.Major)" -File_LOG $LogFile
Write-Log -Text "Value provided for parameter ConfigFile: $ConfigFile" -File_LOG $LogFile
if ($RequestedAction) {
Write-Log -Text "Value provided for parameter RequestedAction: $RequestedAction" -File_LOG $LogFile
$Action = $RequestedAction
} else {
Write-Log -Text "No value provided for parameter RequestedAction ..." -File_LOG $LogFile
Write-Log -Text "Action will be Start or Restart depending on whether HAProxy is running or not." -File_LOG $LogFile
}
Write-Log -Text "Reading default parameters from $ConfigFile" -File_LOG $LogFile
# Read XML file, the HAProxy part ;-)
$HAProxyConfig = ([xml](Get-Content $ConfigFile)).HAProxy
if ($Action -ne "Stop") {
Write-Log -Text "Determining the base path for HAProxy ..." -File_LOG $LogFile
if (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue) {
Write-Log -Text "HAProxy is running, get base path from current process." -File_LOG $LogFile
$HAProxyBasePath = (Get-Process -Name "haproxy" | Select-Object Path | Split-Path) + "\"
if ($HAProxyBasePath.EndsWith("\bin\")) {
$HAProxyBasePath = $HAProxyBasePath.Replace("\bin\", "\")
}
$Action = "Restart"
} else {
Write-Log -Text "HAProxy is NOT running, get base path from configuration file." -File_LOG $LogFile
$HAProxyBasePath = $HAProxyConfig.HAProxyBasePath
$Action = "Start"
}
} else {
$HAProxyBasePath = $HAProxyConfig.HAProxyBasePath
}
Write-Log -Text "Base path for HAProxy is set to $HAProxyBasePath" -File_LOG $LogFile
Push-Location ($HAProxyBasePath)
Write-Log -Text "Effective action that will be taken is: $Action" -File_LOG $LogFile
switch ($Action) {
"Restart" {
Write-Log -Text "Restarting HAProxy ..." -File_LOG $LogFile
$OldPID = (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue).Id
Write-Log -Text "Old PID is: $OldPID" -File_LOG $LogFile
Get-Process -Name "haproxy" | Stop-Process
Start-Sleep -Milliseconds 500
Start-Process -FilePath ".\bin\haproxy.exe" -ArgumentList "-f .\Conf\haproxy.cfg -D"
Start-Sleep -Milliseconds 2000
$NewPID = (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue).Id
Write-Log -Text "New PID is: $NewPID" -File_LOG $LogFile
if (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue) {
Write-Log -Text "HAProxy is running!" -File_LOG $LogFile
} else {
Write-Log -Text "ERROR : Something went wrong and HAProxy is NOT running anymore :-(" -File_LOG $LogFile
}
if ($NewPID -ne $OldPID) {
Write-Log -Text "HAProxy has been restarted." -File_LOG $LogFile
} else {
Write-Log -Text "ERROR : Oops, something went wrong and HAProxy has not been restarted! (Same PID)" -File_LOG $LogFile
}
EndScript
}
"Start" {
Write-Log -Text "Starting HAProxy ..." -File_LOG $LogFile
Start-Process -FilePath ".\bin\haproxy.exe" -ArgumentList "-f .\Conf\haproxy.cfg -D"
Start-Sleep -Milliseconds 2000
$NewPID = (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue).Id
Write-Log -Text "New PID is: $NewPID" -File_LOG $LogFile
if (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue) {
Write-Log -Text "HAProxy is running!" -File_LOG $LogFile
} else {
Write-Log -Text "ERROR : HAProxy is NOT running while it should ... Something went wrong, please check manually!" -File_LOG $LogFile
}
EndScript
}
"Stop" {
Write-Log -Text "Stopping HAProxy ..." -File_LOG $LogFile
if (Get-Process -Name "haproxy") {
Write-Log -Text "HAProxy is running!" -File_LOG $LogFile
Write-Log -Text "Stopping HAProxy ..." -File_LOG $LogFile
Get-Process -Name "haproxy" | Stop-Process -Force
Start-Sleep -Milliseconds 500
if (Get-Process -Name "haproxy" -ErrorAction SilentlyContinue) {
Write-Log -Text "ERROR : HAProxy is still running ... Something went wrong, please check manually!" -File_LOG $LogFile
} else {
Write-Log -Text "HAProxy successfully stopped." -File_LOG $LogFile
}
} else {
Write-Log -Text "Nothing to do as HAProxy is not running." -File_LOG $LogFile
}
EndScript
}
Default {
# Do nothing
Write-Log -Text "ERROR : This part of the script should never be reached. But apparently something went wrong :-(" -File_LOG $LogFile
EndScript
}
}
Voor logging doeleinden maakt het script gebruik van een functie die ik in een extern script heb opgeslagen. De code van het aparte logging script "Write-Log.ps1":
.SYNOPSIS
Writes a message on the screen and to a logfile.
.DESCRIPTION
Writes a message on the screen and to a logfile.
.PARAMETER Text
The message to display and to send to the logfile.
.PARAMETER File_LOG
The full path to the actual logfile to use.
.EXAMPLE
Write-Log -Text "This is the message to log." -File_LOG .\Write-Log.log
This writes "This is the message to log." as follows:
27/12/2021 | 23:55:00 | This is the message to log.
(With the current timestamp, of course)
.NOTES
FunctionName : Write-Log
Created by : Pascal Van Herck
Date Coded : 27/12/2021
.LINK
Home
function Write-Log {
[CmdletBinding()]
Param (
# String to log
[Parameter(Mandatory)]
[string[]]
$Text,
# Full path to logfile
[Parameter(Mandatory)]
[string[]]
$File_LOG
)
$DATE = Get-Date -Format "dd/MM/yyyy | HH:mm:ss"
$Message = $DATE + " | " + $Text
Write-Host $Message
Add-Content -Path $File_LOG -Value ($DATE + " | " + $Text)
}
Het HAProxy.ps1 script maakt gebruik van 2 parameters:
- ConfigFile - De XML config file gebruikt door het script (dit is niet de HAPRoxy config file!). Als deze parameter niet wordt opgegeven, wordt ".\HAProxyConfig.xml" als default gebruikt. De default is met andere woorden een file met de naam HAProxyConfig.xml in dezelfde folder als het script zelf.
- RequestedAction - Hier moet opgegeven worden welke actie er moet worden uitgevoerd. Dit kan een van volgende 3 acties zijn: Stop, Start of Restart. Indien de parameter RequestedAction niet wordt opgegeven, wordt de actie automatisch Start of Restart afhankelijk van het gegeven of HAProxy reeds gestart is of niet.
Het configuratie XML bestand wordt gebruikt om aan te geven waar HAProxy zich bevind.
<HAProxy>
<HAProxyBasePath>E:\HAProxy</HAProxyBasePath>
</HAProxy>
Het script zoekt dan voor de executable bestanden in de bin folder in deze map (E:\HAProxy\bin).
De logfile van het script wordt aangemaakt in de subfolder "Logs" en de naam van het bestand is "[ScriptName].log"
Ervan uitgegaan dat het XML bestand de standaarden volgt qua naam en locatie, kan je HAProxy starten met het volgende commando:
.\HAProxy.ps1 -RequestedAction Start
De Scheduled Task
Als je HAProxy uitvoert via het PowerShell-script, wordt HAProxy uitgevoerd in de huidige gebruikerscontext. Op zich is dat niet echt een probleem, maar het heeft als bijwerking dat HAProxy stopt met werken als de gebruiker uitlogt. Bovendien moet je het script elke keer dat je je opnieuw aanmeldt handmatig starten.
Dat is de reden waarom we HAProxy starten via het PowerShell-script via een scheduled task. Op deze manier kan de taak worden uitgevoerd met het systeemaccount en blijft HAProxy actief, ongeacht of een gebruiker is aangemeld of niet.
De belangrijkste eigenschappen van de scheduled task:
- User account: SYSTEM
- Trigger: At system startup
- Action:
- Program/script: "C:\Program Files\PowerShell\7\pwsh.exe"
- Add arguments (optional): -NoLogo -File .\HAProxy.ps1 -RequestedAction Start
- Start in (optional): E:\HAProxy
Als je HAProxy wilt stoppen nadat het is gestart door de scheduled task, kan je HAProxy stoppen door het proces te beëindigen via taakbeheer of door het PowerShell-script. Houd er wel rekening mee dat het script nu moet worden uitgevoerd met beheerdersrechten omdat het HAProxy proces wordt uitgevoerd met het systeemaccount:
.\HAProxy.ps1 -RequestedAction Stop
Om HAProxy opnieuw te starten, kan je de scheduled task manueel triggeren.