Merge pull request #108 from cloud-neutral-workshop/codex/update-haproxy-configuration-roles

Add HAProxy vhosts role with map-driven layout
This commit is contained in:
cloudneutral 2025-12-27 20:59:24 +08:00 committed by GitHub
commit 21d1930ef5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 240 additions and 0 deletions

View File

@ -0,0 +1,58 @@
# HAProxy Role
This role provisions a thin, include-only HAProxy configuration tree under `/etc/haproxy/`. It follows a map-driven SNI routing strategy so that new hostnames can be added without touching the frontend logic.
## Layout
```
/etc/haproxy/
├── haproxy.cfg # Main entry point (kept thin)
├── global.cfg # global + defaults
├── frontends/
│ ├── fe_443.cfg
│ └── fe_stats.cfg
├── backends/
│ ├── console/
│ │ ├── bk_cn.cfg
│ │ └── bk_global.cfg
│ ├── xray/
│ │ ├── bk_xray_jp.cfg
│ │ ├── bk_xray_sg.cfg
│ │ └── bk_xray_hk.cfg
│ └── fallback/
│ └── bk_blackhole.cfg
├── maps/
│ ├── sni.map
│ └── sni_backend.map
├── certs/ # For HTTP/TLS termination if needed
├── scripts/
│ └── reload.sh
└── logs/
```
### Main configuration (`haproxy.cfg`)
- Includes `global.cfg` for shared defaults.
- Includes all frontends and recursively includes business backends.
- Contains no business logic to keep reloads predictable.
### Global/defaults (`global.cfg`)
Applies TCP defaults and keeps per-node tuning minimal. HK/JP can adjust `maxconn` per-node if needed.
### Frontends
- `fe_443.cfg` performs TLS inspection and routes via the SNI map (`maps/sni_backend.map`), falling back to `bk_blackhole`.
- `fe_stats.cfg` exposes the HAProxy stats UI on `:8404`.
### Maps
- `maps/sni_backend.map` maps SNI hosts to backends; adding a domain means adding a single line.
- `maps/sni.map` documents the expected format when TLS termination is required.
### Backends
Business backends are grouped by domain family (console/xray/fallback) with TCP health checks and consistent timings. The fallback backend intentionally blackholes unmatched traffic.
### Reload helper
`scripts/reload.sh` validates the configuration and reloads HAProxy gracefully (preferring `systemctl` when present).
## Usage
1. Include the role in a play targeting your HAProxy hosts.
2. Update `maps/sni_backend.map` with the desired hostname-to-backend mapping.
3. Adjust backend server endpoints per site; keep the directory layout identical across regions.
4. Run the play to copy the configuration tree and trigger a graceful reload via the handler.

View File

@ -0,0 +1,5 @@
---
haproxy_conf_dir: /etc/haproxy
haproxy_user: root
haproxy_group: root
haproxy_reload_cmd: /etc/haproxy/scripts/reload.sh

View File

@ -0,0 +1,7 @@
backend bk_console_cn
balance roundrobin
option tcp-check
default-server inter 3s fall 3 rise 2
server cn1 10.10.1.1:8443 check
server cn2 10.10.1.2:8443 check

View File

@ -0,0 +1,7 @@
backend bk_console_global
balance roundrobin
option tcp-check
default-server inter 3s fall 3 rise 2
server g1 10.10.0.1:8443 check
server g2 10.10.0.2:8443 check

View File

@ -0,0 +1,2 @@
backend bk_blackhole
server reject 127.0.0.1:1

View File

@ -0,0 +1,7 @@
backend bk_xray_hk
balance roundrobin
option tcp-check
default-server inter 3s fall 3 rise 2
server hk1 10.22.0.1:1443 check send-proxy-v2
server hk2 10.22.0.2:1443 check send-proxy-v2

View File

@ -0,0 +1,7 @@
backend bk_xray_jp
balance roundrobin
option tcp-check
default-server inter 3s fall 3 rise 2
server jp1 10.20.0.1:1443 check send-proxy-v2
server jp2 10.20.0.2:1443 check send-proxy-v2

View File

@ -0,0 +1,7 @@
backend bk_xray_sg
balance roundrobin
option tcp-check
default-server inter 3s fall 3 rise 2
server sg1 10.21.0.1:1443 check send-proxy-v2
server sg2 10.21.0.2:1443 check send-proxy-v2

View File

@ -0,0 +1,9 @@
frontend fe_443
bind :443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend %[req.ssl_sni,lower,map(/etc/haproxy/maps/sni_backend.map)]
default_backend bk_blackhole

View File

@ -0,0 +1,5 @@
listen stats
bind :8404
stats enable
stats uri /stats
stats refresh 5s

View File

@ -0,0 +1,14 @@
global
daemon
log /dev/log local0
maxconn 20000
nbthread 1
defaults
mode tcp
log global
option dontlognull
timeout connect 3s
timeout client 1m
timeout server 1m
timeout check 3s

View File

@ -0,0 +1,10 @@
# Main HAProxy configuration file (intentionally thin)
# 引入全局参数
include /etc/haproxy/global.cfg
# Frontends
include /etc/haproxy/frontends/*.cfg
# Backends
include /etc/haproxy/backends/**/*.cfg

View File

@ -0,0 +1,3 @@
# Map SNI to certificate filenames when terminating TLS locally.
# <sni> <certificate-file>
# example.com example-com.pem

View File

@ -0,0 +1,5 @@
cn-console.sv.plus bk_console_cn
global-console.sv.plus bk_console_global
xray-jp.svc.plus bk_xray_jp
xray-sg.svc.plus bk_xray_sg
xray-hk.svc.plus bk_xray_hk

View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
CONFIG="/etc/haproxy/haproxy.cfg"
PIDFILE="/var/run/haproxy.pid"
HAPROXY_BIN="${HAPROXY_BIN:-/usr/sbin/haproxy}"
SYSTEMCTL_BIN="${SYSTEMCTL_BIN:-/bin/systemctl}"
# Validate configuration before applying
if ! "${HAPROXY_BIN}" -c -f "${CONFIG}"; then
echo "HAProxy configuration validation failed" >&2
exit 1
fi
# Reload HAProxy gracefully
if command -v "${SYSTEMCTL_BIN}" >/dev/null 2>&1; then
exec "${SYSTEMCTL_BIN}" reload haproxy
else
if [[ -r "${PIDFILE}" ]]; then
exec "${HAPROXY_BIN}" -f "${CONFIG}" -sf "$(cat "${PIDFILE}")"
else
exec "${HAPROXY_BIN}" -f "${CONFIG}"
fi
fi

View File

@ -0,0 +1,4 @@
---
- name: reload haproxy
ansible.builtin.command: "{{ haproxy_reload_cmd }}"
listen: reload haproxy

View File

@ -0,0 +1,15 @@
---
galaxy_info:
role_name: haproxy
author: gitops
description: "HAProxy role with thin main config, map-driven SNI routing, and organized backends."
min_ansible_version: "2.10"
platforms:
- name: EL
versions: [8, 9]
- name: Debian
versions: [11, 12]
- name: Ubuntu
versions: [20.04, 22.04]
dependencies: []

View File

@ -0,0 +1,51 @@
---
- name: Ensure HAProxy base directory exists
ansible.builtin.file:
path: "{{ haproxy_conf_dir }}"
state: directory
owner: "{{ haproxy_user }}"
group: "{{ haproxy_group }}"
mode: "0755"
- name: Ensure HAProxy subdirectories exist
ansible.builtin.file:
path: "{{ haproxy_conf_dir }}/{{ item }}"
state: directory
owner: "{{ haproxy_user }}"
group: "{{ haproxy_group }}"
mode: "0755"
loop:
- frontends
- backends
- backends/console
- backends/xray
- backends/fallback
- maps
- certs
- scripts
- logs
- name: Deploy HAProxy configuration files
ansible.builtin.copy:
src: "etc/haproxy/{{ item.src }}"
dest: "{{ haproxy_conf_dir }}/{{ item.dest | default(item.src) }}"
owner: "{{ haproxy_user }}"
group: "{{ haproxy_group }}"
mode: "{{ item.mode | default('0644') }}"
loop:
- { src: "haproxy.cfg" }
- { src: "global.cfg" }
- { src: "frontends/fe_443.cfg" }
- { src: "frontends/fe_stats.cfg" }
- { src: "backends/console/bk_cn.cfg" }
- { src: "backends/console/bk_global.cfg" }
- { src: "backends/xray/bk_xray_jp.cfg" }
- { src: "backends/xray/bk_xray_sg.cfg" }
- { src: "backends/xray/bk_xray_hk.cfg" }
- { src: "backends/fallback/bk_blackhole.cfg" }
- { src: "maps/sni_backend.map" }
- { src: "maps/sni.map" }
- { src: "certs/.gitkeep" }
- { src: "logs/.gitkeep" }
- { src: "scripts/reload.sh", mode: "0755" }
notify: reload haproxy