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.

 

Kies taal:

English (United States)
[en-US]
Dutch (Belgium)
[nl-BE]

Random Quote

It's not true that I had nothing on. I had the radio on.

Door: Marylyn Munroe.