HAProxy
HAProxy 是 Patroni 官方文档推荐放在集群前面的负载
均衡器:客户端只连一个稳定地址,HAProxy 对各成员的 REST API 做健康检查,把写
请求路由到当前 leader(读写分离模式下,读请求路由到 replica)。pgcli 将其作为
独立的顶层插件运行——共享的路由基础设施,而非实例级 sidecar——镜像固定为
官方 haproxy:3.2.23-alpine。
平台支持: HAProxy 插件目前仅支持 Linux。它通过主机网络访问 Patroni 成员,而 macOS 的
podman machine不会把主机网络暴露给容器。macOS 上NewHAProxyManager会快速失败并给出明确提示;pg addon list仍可显示已安装 实例,但没有实时状态。
工作原理
路由方式遵循 Patroni 官方 haproxy.cfg 的模式。每个后端就是一个 Patroni 成员,每个成员有两个关键端口:
- PostgreSQL 端口 —— HAProxy 实际转发连接的目标端口;
- REST API 端口 —— HAProxy 发送
httpchk健康检查的端口。
Patroni 的 REST API 对下列 GET 请求的应答(这些 GET 按设计无需认证):
| 端点 | 返回 200 的节点 |
|---|---|
GET / |
仅 leader |
GET /replica(可加 ?lag=<上限>) |
仅 replica,且延迟在限制之内 |
于是写监听器里唯一 UP 的服务器就是 leader,读监听器里唯一 UP 的就是各
replica——健康检查状态一变,故障切换自动生效。pgcli 把上述配置渲染到
<base-dir>/addon/haproxy/<name>/haproxy.cfg,并以只读方式挂载进容器。
两种模式
用 --mode 选择:
unified(默认)——单个监听器把所有流量发给当前 leader。简单, 每条连接都可读可写。leader 切换时,写监听器用on-marked-down shutdown-sessions掐断旧连接,让客户端重连到新 leader。split—— 读写分离:_<name>_rw监听器把写请求发给 leader,另一个_<name>_ro监听器把读请求轮询分发到各 replica(可按 lag 过滤)。客户端 端口从 1 个变成 2 个。
统计页
每个实例还会得到一个 HTTP stats 监听器,可在浏览器里观察服务器
up/down 状态:http://<listen>:<stats-port>/。
安装
后端成员用两种方式之一提供(互斥):
--node NAME=HOST:PGPORT:RESTPORT(可重复)——显式列出;--ha <scope>—— 自动从pg ha管理的 Patroni scope 中推导所有本机 成员,直接使用各成员的 PostgreSQL 与 REST API 端口。
--node 各字段的含义,以 node1=10.0.0.11:35532:8008 为例:
| 字段 | 含义 |
|---|---|
node1 |
成员名——成为 haproxy.cfg 中的 server 名,也是 stats 页里的标签 |
10.0.0.11 |
本机可访问到该成员的地址(成员的 advertise-host;同机可写 127.0.0.1) |
35532 |
成员的 PostgreSQL 端口——HAProxy 实际转发连接的目标 |
8008 |
成员的 Patroni REST API 端口——健康检查打这个端口 |
用 --ha 时这四个值都来自 pg.yaml:Patroni 成员名、其 advertise-host
(默认 127.0.0.1)、以及 host_port / restapi_port。
安装输出会报告分配到的端口和现成的连接串:
之后增删成员
后端列表跟随 pg.yaml 中该 scope 的本机成员,因此任何拓扑变化——增员或
移除——都通过重跑同一条 install 命令同步:它重新推导完整后端列表并重建
容器。
用 pg ha create <scope> --member <m> 扩容后,新成员还不在运行中的 HAProxy
配置里:
对称地,pg ha remove <scope> --member <m> 之后,被移除的成员会作为过期后端
残留在配置里——重跑 install 即可将其剔除:
显式 --node 安装时没有自动推导:直接改 --node 列表本身(增加或删除某个
spec)再重跑——目标列表每次整体替换,命令里写的就是最终全集。
跨主机集群
--ha 只从本机的 pg.yaml 推导后端,而它只记录本机 pg ha create 管理的
成员。位于其他主机的成员从来就不是本机的后端——在那边增删成员,本机
无需重新同步(也不会有残留)。若想用一个 HAProxy 前置整个集群,请改用显式
的 --node 列表,逐个填上各成员的 advertise 主机地址:
注意:在单台主机上用 --ha 安装的实例,写流量只会路由到本机的 leader——
一旦故障切换把 leader 提到了远程成员,rw 监听器就没有 UP 的服务器了,直到
leader 迁回来。对于 leader 可能漂移的跨主机集群,优先用显式的、列出全部成员的
--node 列表(或每台主机各跑一个 HAProxy,再用上层的 VIP / DNS 前置它)。
读写分离使用示例
用 --mode split 安装后,会暴露两个客户端端口:
| 端口 | 流量 | 后端 |
|---|---|---|
write_port(默认 5000) |
读写 | 仅 leader |
read_port(默认 5001) |
只读 | 各 replica(轮询,lag ≤ max-lag) |
stats_port(默认 5002) |
HTTP 统计页 | — |
故障切换后 HAProxy 通过健康检查自动识别新 leader —— 无需改连接串。浏览器打开
http://127.0.0.1:5002/ 可实时查看后端状态。
端口
端口从主机级端口池自动分配:每个实例连续取 3 个空闲端口(rw,split 模式下再
加 ro,最后 stats),起始值为 haproxy_start_port(默认 5000)。也可以显式
指定:
端口池会探测占用并记录所有 HAProxy 实例已分配的端口,因此同一台机器上可以跑
多个实例而不会冲突。split 实例占 3 个端口(rw / ro / stats),unified
实例占 2 个(rw / stats):
绑定地址默认 127.0.0.1;如需暴露到网络,用 --listen 0.0.0.0(或在
pg.yaml 里设 listen)。除非后端确有需要,请保持 loopback——这里 PostgreSQL
前面没有任何认证。
配置
全部配置位于 pg.yaml 顶层 addons.haproxy 映射中,以实例名为 key:
targets 由 install 渲染并重新推导;mode、端口、replica_max_lag、
image_tag 可在这里手调,然后 pg addon install haproxy --name lb ... 生效。
渲染出的 haproxy.cfg 不含任何密钥——只有主机、端口和健康检查 URI。
查看列表
启动与停止
宿主机重启后,无需重新渲染配置即可拉起实例:
install 总是重建容器以保证配置生效;start 只启动已有容器(状态不当时会
用磁盘上的 haproxy.cfg 自愈重建)。
开机自启
容器带有 --restart unless-stopped 策略(应对崩溃,而非重启)。要在宿主机
重启后拉起实例:
这会设置 autostart: true 并安装 / 刷新开机服务(见开机自启)。
开机时是仅启动语义:它启动已存在的容器、读取磁盘上已有的 haproxy.cfg,
不会重新渲染配置——请先安装实例。开机服务会在 Patroni 成员之后启动
HAProxy,因此其后端也已在拉起中。pg autostart status 会列出每个目标的状态。
移除
停止并删除容器,同时删除其配置目录。
日志
容器日志走 stdout(log stdout format raw local0 info),健康检查状态变化与
连接事件都会出现在这里。
故障排除
- stats 页所有后端都 DOWN。 健康检查打的是各成员的 REST API 端口。先确认
其可达:
curl http://<host>:<rest_port>/在 leader 上返回 200、其余 503;/replica正相反。返回000说明 REST API 挂了或端口写错。 --ha找不到成员。--ha只推导pg ha在pg.yaml中管理的本机 成员。远程成员或 pgcli 不管理的 scope,请用--node显式列出。- 手动
--rw-port撞端口。 请选端口池(haproxy_start_port起)之外的 端口,否则会被自动分配器视为已占用。 - macOS。 暂不支持——HAProxy 需要主机网络访问 Patroni 成员,podman machine 虚拟机不提供该能力。