feat(deploy): Traefik active-leader routing + docker-dev compose (Task 63)
- scripts/install/traefik.yml + traefik-dynamic.yml: Traefik static + dynamic
config. One :80 entry point, one router on HostRegexp(otopcua.*), one
service load-balancing admin-a:9000 + admin-b:9000 with /health/active health
check (interval 5s, timeout 2s, expected 200). Followers return 503 from
/health/active so Traefik drops them within the next interval after a
leadership change.
- scripts/install/Install-Traefik.ps1: downloads Traefik for Windows, drops the
yml configs, registers the OtOpcUaTraefik Windows service via sc.exe with
restart-on-failure. Companion to Install-Services.ps1.
- docker-dev/{Dockerfile,docker-compose.yml,traefik-dynamic.yml,README.md}:
Mac-friendly four-node fleet (admin-a + admin-b + driver-a + driver-b) plus
SQL Server 2022 + OpenLDAP + Traefik. Single OtOpcUa.Host image built once;
Compose drives OTOPCUA_ROLES + Cluster:* per container to differentiate the
four hosts. README walks through bring-up + failover smoke + the dev LDAP
users.
Note: untested on macOS (no local Docker — see docs/v2/dev-environment.md).
This commit is contained in:
68
scripts/install/Install-Traefik.ps1
Normal file
68
scripts/install/Install-Traefik.ps1
Normal file
@@ -0,0 +1,68 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs Traefik as a Windows service that routes admin HTTP traffic to whichever
|
||||
OtOpcUa.Host node holds the admin role-leader (via /health/active).
|
||||
|
||||
.DESCRIPTION
|
||||
Downloads the Traefik Windows binary into $InstallRoot, drops traefik.yml +
|
||||
traefik-dynamic.yml from this directory next to it, and registers Traefik as a
|
||||
Windows service via sc.exe with restart-on-failure.
|
||||
|
||||
Companion to Install-Services.ps1. Run on the box that fronts the admin HTTP
|
||||
traffic (typically a separate node from OtOpcUaHost, or co-located on the
|
||||
primary admin node).
|
||||
|
||||
.PARAMETER InstallRoot
|
||||
Where the Traefik binary + config land. Default 'C:\Program Files\Traefik'.
|
||||
|
||||
.PARAMETER TraefikVersion
|
||||
Traefik version to download. Default 'v3.1.6'.
|
||||
|
||||
.EXAMPLE
|
||||
.\Install-Traefik.ps1 -InstallRoot 'C:\Program Files\Traefik'
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$InstallRoot = 'C:\Program Files\Traefik',
|
||||
[string]$TraefikVersion = 'v3.1.6'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not (Test-Path $InstallRoot)) {
|
||||
New-Item -ItemType Directory -Path $InstallRoot | Out-Null
|
||||
}
|
||||
|
||||
$zip = Join-Path $env:TEMP "traefik-$TraefikVersion.zip"
|
||||
$url = "https://github.com/traefik/traefik/releases/download/$TraefikVersion/traefik_${TraefikVersion}_windows_amd64.zip"
|
||||
|
||||
Write-Host "Downloading Traefik $TraefikVersion..."
|
||||
Invoke-WebRequest -Uri $url -OutFile $zip
|
||||
Expand-Archive -Path $zip -DestinationPath $InstallRoot -Force
|
||||
Remove-Item $zip
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
Copy-Item -Force (Join-Path $scriptDir 'traefik.yml') $InstallRoot
|
||||
Copy-Item -Force (Join-Path $scriptDir 'traefik-dynamic.yml') (Join-Path $InstallRoot 'dynamic.yml')
|
||||
|
||||
# Traefik reads dynamic.yml from /etc/traefik on Linux; on Windows place it next to the
|
||||
# binary and point the file provider at it. Edit traefik.yml's `filename:` if you want
|
||||
# to change the location.
|
||||
(Get-Content -Raw (Join-Path $InstallRoot 'traefik.yml')) `
|
||||
-replace '/etc/traefik/dynamic.yml', (Join-Path $InstallRoot 'dynamic.yml').Replace('\', '/') `
|
||||
| Set-Content (Join-Path $InstallRoot 'traefik.yml')
|
||||
|
||||
Write-Host "Installing Traefik Windows service..."
|
||||
& sc.exe create OtOpcUaTraefik binPath= "`"$InstallRoot\traefik.exe`" --configFile=`"$InstallRoot\traefik.yml`"" `
|
||||
DisplayName= 'OtOpcUa Traefik (admin HTTP front door)' `
|
||||
start= auto | Out-Null
|
||||
|
||||
& sc.exe failure OtOpcUaTraefik reset= 86400 actions= restart/5000/restart/30000/restart/60000 | Out-Null
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Installed OtOpcUaTraefik. Edit:"
|
||||
Write-Host " $InstallRoot\dynamic.yml (router + service definitions)"
|
||||
Write-Host "Start with:"
|
||||
Write-Host " sc.exe start OtOpcUaTraefik"
|
||||
Write-Host ""
|
||||
Write-Host "Traefik dashboard: http://localhost:8080 (turn off api.insecure in production)"
|
||||
24
scripts/install/traefik-dynamic.yml
Normal file
24
scripts/install/traefik-dynamic.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
# Dynamic (file-provider) Traefik config for the OtOpcUa admin HTTP routing.
|
||||
# Picked up by traefik.yml's file provider (with watch: true) so router/service
|
||||
# edits hot-reload without a Traefik restart.
|
||||
|
||||
http:
|
||||
routers:
|
||||
otopcua-admin:
|
||||
entryPoints: ["web"]
|
||||
rule: "HostRegexp(`otopcua.*`)"
|
||||
service: otopcua-admin
|
||||
|
||||
services:
|
||||
otopcua-admin:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://admin-a:9000"
|
||||
- url: "http://admin-b:9000"
|
||||
healthCheck:
|
||||
path: /health/active
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
# Default expected status is 2xx. Followers return 503 from
|
||||
# /health/active so Traefik will drop them from the balancer
|
||||
# within the next interval after a leadership change.
|
||||
30
scripts/install/traefik.yml
Normal file
30
scripts/install/traefik.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
# Traefik static configuration for the OtOpcUa fleet HTTP front door.
|
||||
#
|
||||
# Routes admin-role HTTP traffic (Blazor + auth + SignalR + /auth/*) to whichever
|
||||
# OtOpcUa.Host node currently holds the admin role-leader. Uses the /health/active
|
||||
# endpoint as the active-leader signal: a node returns 200 only when it is the
|
||||
# Akka admin role-leader; followers return 503 and Traefik routes around them.
|
||||
#
|
||||
# OPC UA traffic is NOT routed through Traefik — clients connect directly to
|
||||
# opc.tcp://node:4840 on every driver node and use the standard ServiceLevel
|
||||
# heuristic for failover.
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
|
||||
providers:
|
||||
file:
|
||||
filename: /etc/traefik/dynamic.yml
|
||||
watch: true
|
||||
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
log:
|
||||
level: INFO
|
||||
format: common
|
||||
|
||||
accessLog:
|
||||
format: common
|
||||
Reference in New Issue
Block a user