HAProxy
HAProxy 提供 TCP 四层转发与 HTTP 七层负载均衡,支持健康检查、TLS 终止、连接管理和 Prometheus 指标。
1. 版本与安装
以下安装命令适用于使用 systemd 的 Debian/Ubuntu。其他发行版应核对软件包、服务单元和默认路径。示例中的 192.0.2.0/24 是文档地址,部署前替换为实际后端地址。
sudo apt-get update
sudo apt-get install -y haproxy curl socat
haproxy -vv
检查 haproxy -vv 的构建选项是否包含 +PROMEX,以及 Available services 是否列出 prometheus-exporter。版本支持该功能不代表发行版一定编译了它。本章配置在 HAProxy 2.4.30 上进行兼容性验证;该旧系列不作为新部署的版本推荐,生产应选择仍受上游或发行版安全维护的版本,并按对应版本手册检查参数。
| 模式 | 适用场景 | 能力边界 |
|---|---|---|
mode http |
网站、REST API、TLS 在 HAProxy 终止 | 能按路径、Host、Header 路由和统计 HTTP 状态码 |
mode tcp |
Kubernetes API、数据库、TLS 透传 | 转发字节流,不能直接按加密后的 HTTP 路径路由 |
2. 完整 HTTP 负载均衡配置
编辑前备份 /etc/haproxy/haproxy.cfg,将下面内容作为独立实验的完整配置。已有生产配置应逐项合并,避免覆盖其他监听器。安装包应已创建 haproxy 用户和组;运行目录通常由服务单元管理,也可在首次启动前创建:
sudo cp -a /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.before-review
sudo install -d -o haproxy -g haproxy -m 750 /run/haproxy
写入 /etc/haproxy/haproxy.cfg:
global
log stdout format raw local0
maxconn 4096
user haproxy
group haproxy
stats socket /run/haproxy/admin.sock user haproxy group haproxy mode 660 level user
stats timeout 10s
defaults
log global
mode http
option dontlognull
timeout connect 3s
timeout client 30s
timeout server 30s
timeout check 2s
timeout http-request 10s
timeout http-keep-alive 10s
timeout queue 10s
frontend fe_http
bind :8080
option httplog
http-request set-header X-Forwarded-For %[src]
http-request set-header X-Forwarded-Proto http
acl api_path path /api
acl api_path path_beg /api/
use_backend be_api if api_path
default_backend be_web
backend be_web
balance roundrobin
option httpchk
http-check send meth GET uri /healthz ver HTTP/1.1 hdr Host app.internal
http-check expect status 200
default-server inter 3s fall 3 rise 2
server web1 192.0.2.11:8080 check
server web2 192.0.2.12:8080 check
backend be_api
balance leastconn
option httpchk
http-check send meth GET uri /healthz ver HTTP/1.1 hdr Host api.internal
http-check expect status 200
default-server inter 3s fall 3 rise 2
server api1 192.0.2.11:9000 check
server api2 192.0.2.12:9000 check
frontend fe_monitor
bind 127.0.0.1:8404
mode http
no log
http-request use-service prometheus-exporter if { path /metrics }
http-request deny deny_status 404 unless { path_beg /stats }
stats enable
stats uri /stats
stats refresh 10s
stats hide-version
配置说明:
fe_http监听 8080;同名 ACL 的两项匹配按“或”组合,/api和/api/…进入 API 后端,/apix进入 Web 后端。本例路径匹配区分大小写,按需统一应用与代理的路径策略。- 示例假定 HAProxy 直接接收客户端连接,因此覆盖
X-Forwarded-For,避免直接信任客户端伪造值。前面还有可信 CDN/代理时,应按可信来源配置地址传递链;后端也应只信任受控代理设置的转发头。 roundrobin轮询,leastconn优先选择当前连接较少的服务器;它们均不复制应用会话。需要会话状态时优先使用共享存储,再按业务考虑粘性会话。check开启主动检查。单纯 TCP 检查只能确认端口可连接;这里进一步要求/healthz返回 200,且发送明确的 Host,避免虚拟主机和重定向干扰。后端需要实现该接口,不能直接复制不存在的路径。inter 3s fall 3 rise 2表示检查间隔及连续失败/成功阈值;超时、调度和当前状态会影响实际摘除时间,不能直接等同于精确的 9 秒。摘除后不再分配新连接,已有连接不会迁移到其他服务器。timeout client/server主要约束相应方向的不活动时间,不是请求总耗时。WebSocket、SSE、长轮询需要按业务设计超时,隧道场景还应核对timeout tunnel,避免无限占用连接。- 日志写标准输出;发行版服务由 systemd 接收输出时可用 journal 查看。其他运行方式需自行接入日志收集。
- Socket 虽命名为
admin.sock,本例只授予level user查询权限,足以读取统计。不要为了监控直接赋予管理权限;/run是临时目录,应确保重启后由服务机制重新创建。 - 8404 同时提供
/stats状态页和/metrics指标。默认只绑定回环地址,不对外开放;与业务 8080 分离。
检查后启动或重载:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl enable --now haproxy
sudo systemctl reload haproxy
sudo systemctl status haproxy --no-pager
sudo journalctl -u haproxy -n 50 --no-pager
curl -i http://127.0.0.1:8080/
curl -i http://127.0.0.1:8080/api
curl -fsS http://127.0.0.1:8404/metrics
haproxy -c 只做配置校验,不能保证端口未被占用、后端可达或证书域名正确;必须继续检查运行状态和业务请求。
3. TLS 终止与 TCP 透传
3.1 HTTPS 在 HAProxy 终止
准备 /etc/haproxy/certs/site.pem,内容包含站点私钥、站点证书和必要的中间证书链,并限制私钥文件权限。先完成证书部署,再追加监听器。把下面选项合并到已有 global 段:
ssl-default-bind-options ssl-min-ver TLSv1.2
追加 HTTPS 前端,复用前面的两个后端:
frontend fe_https
bind :443 ssl crt /etc/haproxy/certs/site.pem alpn h2,http/1.1
mode http
option httplog
http-request set-header X-Forwarded-For %[src]
http-request set-header X-Forwarded-Proto https
acl api_path path /api
acl api_path path_beg /api/
use_backend be_api if api_path
default_backend be_web
本例客户端到 HAProxy 使用 TLS,HAProxy 到后端仍为 HTTP。后端链路也需要加密时,在 server 行配置 ssl、可信 CA、verify required,并明确校验目标主机名与发送 SNI;不要把 verify none 作为生产默认值。生产域名从标准 80 端口跳转到标准 443 时,可在独立 HTTP 前端使用 http-request redirect scheme https code 301;非标准端口需明确构造目标地址,不能假定自动跳到正确端口。
3.2 Kubernetes API 的 TCP 透传
将以下段追加到主配置。此处由各 API Server 终止 TLS,HAProxy 不读取 HTTP 路径,也不需要 API Server 私钥:
frontend fe_k8s_api
bind :6443
mode tcp
option tcplog
timeout client 1h
default_backend be_k8s_api
backend be_k8s_api
mode tcp
balance roundrobin
timeout server 1h
option tcp-check
default-server inter 3s fall 3 rise 2
server cp1 192.0.2.21:6443 check
server cp2 192.0.2.22:6443 check
server cp3 192.0.2.23:6443 check
注意事项:
- 这里检查的是 TCP 可连接性,不等同于 API Server
/readyz成功。若需应用就绪检查,应另外设计带正确 TLS 校验及访问权限的检查请求。 - 客户端使用的负载均衡 DNS 名或 VIP 必须包含在 API Server 证书 SAN 中;kubeconfig 和控制平面的入口地址也应一致。
- watch 等长连接按单条 TCP 连接选定后端,增加后端不会自动重新均衡已有连接。
1h为示例不活动超时,需按业务调整。 - 不要直接对原生 API Server 添加
send-proxy;只有接收端明确支持 PROXY protocol 时才能开启,否则会破坏 TLS 握手。 - 本例不解析透传的 HTTP 流量,监控应关注连接、队列、失败率以及独立 API 探针,不能期待 HTTP 5xx 指标反映透传业务。
4. Prometheus 与 Grafana
4.1 优先使用内置 exporter
官方独立项目 prometheus/haproxy_exporter 已停止维护,优先使用支持 PROMEX 的 HAProxy 二进制及本章 /metrics 配置。内置端点直接由 HAProxy 提供,不需要额外启动 9101 进程。
当 Prometheus 与 HAProxy 在同一主机、同一网络命名空间时,可使用以下完整的最小 /etc/prometheus/prometheus.yml;已有配置应合并 job 和规则文件,保留其他抓取、告警与存储设置:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- ./haproxy-rules.yml
scrape_configs:
- job_name: haproxy-native
metrics_path: /metrics
scrape_timeout: 10s
static_configs:
- targets: ["127.0.0.1:8404"]
labels:
node: lb01
Prometheus 在其他主机或容器时,127.0.0.1 指向它自身。需要把 fe_monitor 绑定改为实际管理网 IP,同时修改 target,并通过防火墙/ACL 只允许监控来源访问。跨不可信网络时增加 TLS 和适当认证;给状态页配置 stats auth 不会自动保护 /metrics,必须覆盖指标请求本身。不要将含口令的 URL 写入命令、进程参数或截图。
4.2 告警规则
保存以下内容为 /etc/prometheus/haproxy-rules.yml。这些规则针对内置 exporter 的指标与本章代理名称;迁移旧 exporter 时先查看实际 /metrics,核对指标名、标签和状态取值。
groups:
- name: haproxy
rules:
- alert: HAProxyMetricsUnavailable
expr: up{job="haproxy-native"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: 'HAProxy 指标抓取失败:{{ $labels.instance }}'
- alert: HAProxyBackendUnavailable
expr: haproxy_backend_status{job="haproxy-native",proxy=~"be_web|be_api",state="UP"} == 0
for: 30s
labels:
severity: critical
annotations:
summary: 'HAProxy 后端池不可用:{{ $labels.proxy }}'
- alert: HAProxyServerDown
expr: haproxy_server_status{job="haproxy-native",proxy=~"be_web|be_api",state="DOWN"} == 1
for: 2m
labels:
severity: warning
annotations:
summary: 'HAProxy 后端节点故障:{{ $labels.proxy }}/{{ $labels.server }}'
- alert: HAProxyHighHTTP5xxRatio
expr: |
(
sum by (instance, proxy) (rate(haproxy_frontend_http_responses_total{job="haproxy-native",proxy="fe_http",code="5xx"}[5m]))
/
sum by (instance, proxy) (rate(haproxy_frontend_http_responses_total{job="haproxy-native",proxy="fe_http"}[5m]))
) > 0.05
and on (instance, proxy)
sum by (instance, proxy) (rate(haproxy_frontend_http_responses_total{job="haproxy-native",proxy="fe_http"}[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: 'HAProxy HTTP 5xx 比例持续超过 5%:{{ $labels.instance }}/{{ $labels.proxy }}'
说明:
up=1只证明抓取成功,不能证明业务可用;所有 Web 后端失效时,HAProxy 仍能暴露指标并对业务返回 503。- 原生状态指标按
state输出 0/1。不要对状态 Gauge 使用rate;累计响应 Counter 则应先rate,再按需要聚合。 - 节点维护状态不一定是
DOWN;预期的维护应结合 Alertmanager 静默,不能假定该规则覆盖所有非 UP 状态。配置中删除 target 后也不会持续产生up=0,重要目标需要结合服务发现与缺失序列检测。 - 5xx 示例仅覆盖
fe_http,并要求总流量超过每秒 1 个响应,避免低流量误报;启用fe_https后按需扩展选择器。多集群汇聚时,聚合维度还应保留集群标签,避免相同 instance 被合并。 - 从故障到通知还有健康检查、抓取、规则评估、
for和通知分组等延迟。规则只产生告警,发送通知还需配置 Prometheus 的alerting和 Alertmanager。
校验配置,成功后按实际服务单元重载:
promtool check rules /etc/prometheus/haproxy-rules.yml
promtool check config /etc/prometheus/prometheus.yml
Prometheus 支持向进程发送 SIGHUP;只有服务单元实现 ExecReload 时才可使用 systemctl reload prometheus。HTTP /-/reload 需要启动参数 --web.enable-lifecycle,不要把未开启的端点当作通用重载方式。最后检查 Targets、Rules 和实际时序数据。
4.3 从旧 exporter 迁移与仪表盘适配
原实验使用独立 exporter 的 9101 端口,从 HAProxy 状态页 CSV 或 Unix Socket 读取数据;其中状态页 9999、9009、8899 是不同实验的自定义监听端口,不能混用。本章统一为业务 8080、原生监控 8404。
确需临时保留旧系统时,应固定经过审核的旧二进制,限制其监听地址与权限;Unix Socket 优先使用只读查询权限。CSV 地址通常带 ;csv,在 Shell 中必须引用整个地址,避免分号被解释为命令分隔符。不要复制旧截图中的账号、口令或启动方式作为新部署方案。
原文 Grafana 模板编号 367、2428、12030 仅作为历史线索。导入前检查模板对应的 exporter、指标名、job/proxy/server 标签及数据源变量;旧 exporter 与原生 exporter 的状态表达和指标可能不同。仪表盘空白时先在 Prometheus 中查询实际指标,再修改面板查询,不能只更换 job 名。
5. 平滑重载、高可用与验收
5.1 重载与运行状态
备份应在编辑配置前完成;测试通过后再重载:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
sudo journalctl -u haproxy -n 50 --no-pager
printf 'show info\n' | sudo socat stdio UNIX-CONNECT:/run/haproxy/admin.sock
printf 'show stat\n' | sudo socat stdio UNIX-CONNECT:/run/haproxy/admin.sock
确认发行版 systemd 单元支持平滑 reload;通常由新 worker 接收连接、旧 worker 排空已有连接。长连接可能让旧进程保留较久,不要以为重载后所有进程和连接都会立即消失。运行时统计计数、连接和健康状态的保留行为需按版本及状态文件配置验证,不能假定跨重载永久保留。加载失败时检查日志,并用备份恢复配置后重新校验与重载。
5.2 消除入口单点
两台 HAProxy 可结合 Keepalived 或 LVS 的 VIP,或放在上游负载均衡服务之后。单台 HAProxy 即使有多个后端,入口仍是单点。
- 两台节点都应部署一致且可校验的配置,健康检查需能反映代理/业务监听是否可用,不能只检查进程存在。
- VIP 漂移不会复制 HAProxy 的既有 TCP 连接;切换可能中断请求,客户端需具备合理的重试和超时策略。非幂等操作不能盲目重试。
- VRRP 依赖网络条件;网络分区、云平台不支持漂移地址、组播限制等需要单独设计。先确认平台支持,再采用 VIP 方案。
- Prometheus 应直接抓取每台 HAProxy 的管理地址,不能仅抓 VIP,否则备用节点故障可能被隐藏。
5.3 验收清单
- 分别请求
/、/api、/api/test、/apix,核对 Web/API 路由边界;检查后端收到的转发头是否可信。 - 停止一个测试后端,等待
fall阈值,验证新请求落到剩余后端;恢复后等待rise阈值,确认重新加入。 - 停止同池所有测试后端,确认业务失败、状态指标变化,同时监控端点仍可抓取;验证告警实际触发和恢复。
- 对真实域名校验 HTTPS 证书链、SAN 与协议协商;TCP 透传用正常 kubeconfig 验证 Kubernetes API 和 watch。
- 在持续请求和长连接下测试配置重载;双节点环境另测主备切换、客户端重试及两节点监控。故障演练应在可控环境进行。
6. 常见故障
| 现象 | 检查方向 |
|---|---|
不识别 prometheus-exporter |
核对 haproxy -vv 中 PROMEX 构建特性,使用包含该模块的维护版本 |
| 配置校验成功但服务启动失败 | 查看 journal,检查监听端口冲突、证书/私钥读取权限、运行目录和安全策略 |
| 返回 503 | 查看后端是否全部 DOWN、健康接口路径/Host/状态码是否正确、连接上限与队列是否耗尽 |
| 返回 502 / 504 | 检查后端响应、连接重置、协议匹配及超时;不要只增加 timeout 掩盖故障 |
/metrics 本机可访问,Prometheus 抓取失败 |
检查是否绑定回环、容器网络命名空间、管理网路由、防火墙和 target 地址 |
| 仪表盘空白或状态异常 | 核对原生/独立 exporter、指标名、标签、Gauge/Counter 用法及数据源变量 |
| 读取 Socket 权限不足 | 检查调用者权限、Socket 的 user/group/mode、目录搜索权限;不要直接改成全员可写 |
| 重载后出现多个 worker | 检查长连接排空与重载日志;按业务制定旧 worker 退出策略,避免直接杀死正常连接 |
7. 官方参考
- HAProxy 各版本文档入口
- HAProxy 2.4 配置手册:本章兼容性验证对应系列,实际部署应使用所装版本手册。
- 内置 Prometheus exporter 说明
- 已停止维护的独立 haproxy_exporter
- Prometheus 配置
- Prometheus 告警规则
评论