跳至内容

22.6 Pigsty 服务接入层

Pigsty 不发明 PostgreSQL 的主库、副本或 session 语义。它把:

inventory intent
  -> Patroni role API
      -> HAProxy service
          -> PgBouncer/PostgreSQL destination
              -> DNS/VIP/client service material
                  -> metrics and administration

组合成可交付实现。

理解 Pigsty 服务层的关键不是记命令,而是能把任何观察反向映射到原生组件。

22.6.1 服务定义、角色选择与端口

默认变量

本章参考实现的关键声明形态:

pgbouncer_enabled: true
pgbouncer_port: 6432
pgbouncer_poolmode: transaction
pgbouncer_sslmode: disable

pg_service_provider: ''
pg_default_service_dest: pgbouncer
pg_default_services:
  - { name: primary, port: 5433, dest: default,
      check: /primary, selector: "[]" }
  - { name: replica, port: 5434, dest: default,
      check: /read-only, selector: "[]",
      backup: "[? pg_role == `primary` || pg_role == `offline` ]" }
  - { name: default, port: 5436, dest: postgres,
      check: /primary, selector: "[]" }
  - { name: offline, port: 5438, dest: postgres,
      check: /replica,
      selector: "[? pg_role == `offline` || pg_offline_query ]",
      backup: "[? pg_role == `replica` && !pg_offline_query]" }

版本和自定义配置可能不同。读取实际 inventory、role defaults 与 rendered file,不要把这段当成跨版本常量。

dest

服务 destination 可以表达:

default     使用 pg_default_service_dest
postgres    PostgreSQL pg_port,常见 5432
pgbouncer   PgBouncer pgbouncer_port,常见 6432
number      指定端口

因此:

primary/replica dest=default

在本章 pg_default_service_dest=pgbouncer 时走连接池;如果用户改成 postgres,同一个 5433/5434 就绕过池。

端口名不能替实际路径。

check

check 是 Patroni REST health path:

/primary
/replica
/read-only

HAProxy 对成员的 8008 检查,数据流则去 dest。角色判断来自 Patroni, 不是 HAProxy 解析 PostgreSQL protocol。

selector

selector 从 cluster member inventory 中选普通 backend。

selector: "[]"

表示全集。

offline 示例只选择:

pg_role == offline OR pg_offline_query

这让平台能把特定 replica 标为重查询目标。selector 是期望集合,运行时健康 检查仍可能摘除不合格成员。

backup

backup selector 形成 HAProxy backup server。它决定正常集合不可用时是否 降级。

要把 backup 语义写进服务合同:

  • replica 回 primary 是否允许;
  • offline 回普通 replica 是否允许;
  • backup 激活是否告警;
  • 目标是否有容量;
  • client target_session_attrs 会接受还是拒绝。

pg_service_provider

默认空值通常在每个 PostgreSQL node 上交付 local HAProxy service。

也可指定专用 HAProxy node group。此时要重新设计:

  • provider 高可用;
  • provider 到数据库网络;
  • DNS/VIP/multi-host;
  • config rollout;
  • source IP/HBA;
  • stats/metrics;
  • 故障域。

把 HAProxy 从数据库节点移出,不自动获得入口 HA。

VIP 与 DNS

相关声明包括:

pg_vip_enabled: false
pg_vip_address: 127.0.0.1/24
pg_vip_interface: auto
pg_dns_suffix: ''
pg_dns_target: auto

这是交付入口的机制选择。启用前要按第 22.1.3 节验证网络、仲裁、DNS cache 与证书,不能因为变量存在就宣称通过。

自定义业务服务

可以在 pg_services 添加服务,而不是修改默认列表。例如概念上:

pg_services:
  - name: shop-ro
    port: 5444
    dest: pgbouncer
    check: /read-only
    selector: "[? pg_role == `replica` && !pg_offline_query]"

生产声明还应补:

  • backup/fail-closed;
  • maxconn;
  • balance;
  • options/rise/fall;
  • owner 和用途;
  • TLS/网络;
  • driver endpoint。

不要为每个应用随意开端口;只有语义或资源/失败域不同才需要新服务。

22.6.2 PgBouncer、HAProxy 与数据库的证据链

第一步:声明证据

从 reviewed inventory 提取 secret-free projection:

cluster/member addresses
pg_role/pg_offline_query
pg_services/default services
default destination
PgBouncer mode/port/budget
VIP/DNS/provider
declared users and pgbouncer participation

凭据值不进入报告,只记录:

present
source class
rotation owner

第二步:rendered HAProxy

本章 /etc/haproxy/pg-test-*.cfg 投影:

primary  :5433 -> all members :6432, /primary
replica  :5434 -> members :6432, /read-only, primary backup
default  :5436 -> all members :5432, /primary
offline  :5438 -> pg-test-3 :5432, pg-test-2 backup, /replica

同时核对:

bind/mode/maxconn
balance
health method/path/status
inter/fastinter/downinter
rise/fall
shutdown-sessions
slowstart
backend maxconn/maxqueue
member address/destination/check port/backup

只核对文件 diff 仍不够;进程可能未 reload 或 runtime state 不同。

第三步:HAProxy runtime

从 stats socket/API 看:

frontend OPEN
backend UP/DOWN
health code
last state change
sessions/queue
backup activation

敏感 stats user/password 不输出。使用 local protected socket 比把管理页面凭据 写入脚本更安全。

第四步:PgBouncer config

在 local Unix admin socket:

SHOW CONFIG;
SHOW DATABASES;
SHOW USERS;
SHOW POOLS;
SHOW STATS;

本章 safe projection:

pool_mode=transaction
listen_addr=0.0.0.0
listen_port=6432
max_client_conn=20000
default_pool_size=50
reserve_pool_size=30
reserve_pool_timeout=1
query_wait_timeout=120
max_db_connections=100
max_user_connections=100
max_prepared_statements=256
server_reset_query=DISCARD ALL
server_reset_query_always=0
client_tls_sslmode=disable
unix_socket_dir=/run/postgresql

不要采集:

  • password;
  • SCRAM verifier;
  • auth file 内容;
  • inventory secret;
  • admin credential。

数据库 LOGIN 不等于池化身份已交付

本章开发过程中故意撞到一个重要边界:

CREATE ROLE pg36_ch22_app LOGIN PASSWORD ...
direct PostgreSQL auth works
PgBouncer auth fails

因为本章 Pigsty 默认:

pgbouncer_auth_query: false

PgBouncer authentication surface 由声明式用户清单管理。只有数据库 catalog 里存在 role,不等于 pooler 的 auth file/query 已经认识它。

生产用户应在 Pigsty pg_users 中声明并明确:

- name: pg36_shop_app
  password: <secret reference/material>
  pgbouncer: true

实际字段与 secret workflow 以当前版本文档和组织规范为准。不要手改 userlist.txt 制造不可追踪漂移。

本章 formal run 因此使用既有、Pigsty 已声明的 nonproduction test 用户, 只创建专属 schema/table;脚本永不修改或删除该 role。

若启用 pgbouncer_auth_query,还要评审:

  • auth_user 与查询权限;
  • query 在 replica/primary 的行为;
  • password rotation;
  • role expiration;
  • auth database;
  • failover;
  • secret exposure。

第五步:PostgreSQL 原生状态

对每个 member:

SELECT pg_is_in_recovery(),
       current_setting('transaction_read_only'),
       current_setting('cluster_name'),
       current_setting('port'),
       pg_postmaster_start_time();

并观察连接预算:

SELECT name, setting, unit, source
FROM pg_settings
WHERE name IN (
  'max_connections',
  'superuser_reserved_connections',
  'reserved_connections',
  'idle_in_transaction_session_timeout',
  'statement_timeout',
  'max_locks_per_transaction',
  'work_mem',
  'temp_buffers'
);

pg_postmaster_start_time() 在本章用来把经过 local Unix socket 的 PgBouncer session 映射回具体 member;inet_server_addr() 对 Unix backend 可能为空。

第六步:从 client 走完整路径

每个 service 用真实 database/user:

SELECT pg_is_in_recovery(),
       current_setting('transaction_read_only')::boolean,
       current_setting('cluster_name'),
       current_setting('port')::integer,
       pg_backend_pid(),
       pg_postmaster_start_time();

预期:

servicerecoveryread_onlypath
primary 5433falsefalseHAProxy → PgBouncer
replica 5434truetrueHAProxy → PgBouncer
default 5436falsefalseHAProxy → PostgreSQL
offline 5438truetrueHAProxy → PostgreSQL

再到每台 PgBouncer SHOW POOLS,证明 pooled endpoint 真正在对应 process 形成了 database/user pool。

第七步:行为证据

配置与角色通过后仍要测:

  • 12-client/2-server queue;
  • backend reassignment;
  • session state 丢失/泄漏;
  • protocol prepared;
  • SQL PREPARE negative;
  • async token visibility;
  • planned switch/reconnect;
  • final config/topology restore。

这才完成从声明到用户体验的链。

证据矩阵

Claim声明渲染runtimeSQL/client
5433 主写service check/destprimary cfgbackend statuswritable
5434 副本优先backup/selectorreplica cfgselected poolread-only/member
事务池pool modepgbouncer iniSHOW CONFIG/POOLSPID reassignment
2 server capruntime overrideN/Asv_active ≤ 212 clients complete
prepared 支持max_preparedSHOW CONFIGtwo server PIDcorrect protocol results
switch recoveryPatroni/servicehealth configtopology/pool refreshtoken reconcile

22.6.3 配置变更、reload 与连接行为验证

不要直接编辑 rendered file

错误流程:

vim /etc/haproxy/pg-test-primary.cfg
systemctl reload haproxy

问题:

  • inventory 不知道;
  • 下次 automation 覆盖;
  • 多节点不一致;
  • review/rollback 不完整;
  • secret/权限可能漂移。

正确流程:

edit reviewed Pigsty declaration
  -> render diff/plan
      -> validate generated config
          -> staged reload
              -> runtime observation
                  -> client behavior test
                      -> commit evidence/rollback

service tag

参考代码中服务生成/reload 由 pg_service 相关 task/tag 管理,典型调用形态:

./pgsql.yml -l pg-test -t pg_service

生产执行前必须按当前 Pigsty 版本查看 help/plan、限定 inventory 和 host。 不要从书中复制命令直接指向未知集群。

render task 会生成 service config,并在 reload 前运行 HAProxy config check。

配置校验

原生检查:

haproxy -f /etc/haproxy/haproxy.cfg -c -q

它证明语法/引用可加载,不证明路由语义正确。

PgBouncer reload:

RELOAD;
SHOW CONFIG;

不是所有配置都支持在线改变;某些需要 reconnect/restart。SHOW CONFIG 的 changeable 列与当前文档共同决定。

reload 与现有连接

必须回答:

old HAProxy process 是否 drain
existing TCP 是否保留
new connections 是否使用新 config
PgBouncer existing client/server pool 是否继承
authentication file rotation 对既有 session 是否影响
pool size 改变对已有 server connection 如何收敛

“reload 成功”不能替代这些答案。

role change 与 config change 是两类变更

config change:

declaration -> render -> reload

role change:

Patroni/DCS -> health convergence -> pool/client refresh

二者可能同时发生,但 rollback 不同。故障切换时不应顺手修改持久配置, 否则难以分辨恢复来自哪项动作。

本章 runtime pool override 只用于实验,并在切换前恢复,正是为了隔离变量。

staged rollout

多入口环境:

  1. 选一个无生产或低流量 provider;
  2. render/check;
  3. reload;
  4. direct health + client probe;
  5. 观察 queue/error/session;
  6. 扩到下一 provider;
  7. 完整端点矩阵;
  8. 保留旧配置与回滚。

若所有 provider 同时 reload,错误配置会同时摧毁入口冗余。

变更后的强制验证

declaration projection equals reviewed intent
rendered files equal expected member/dest/check/backup
all proxy instances loaded intended config
runtime backend states make sense
PgBouncer config/pools within budget
primary endpoint writable
replica/offline endpoint readonly + allowed member
direct endpoint bypasses pool
session/prepared compatibility suite passes
old/new connection behavior matches change plan

如果变更涉及 role/promotion,再执行 pool role-state refresh 检查。

回滚

回滚不是把文件复制回去:

restore declaration
render/check
staged reload
verify runtime
verify client path
close/refresh incompatible existing sessions if needed
record final state

如果数据库 role 已在期间改变,旧 rendered config 的成员角色仍由 health check 动态判断,但 selector/backup/destination 可能不再合适,要重新评审。

secret 与证据

服务变更会接触:

  • inventory password;
  • PgBouncer userlist/verifier;
  • HAProxy stats auth;
  • TLS key;
  • HBA/identity。

证据只保留:

hash/projection/presence
mode/owner
rotation metadata
behavioral result

不要把整个 inventory、auth file 或 config 原文无差别上传。正式 lab 对 临时 credential inventory 要求 mode 0600,使用后删除副本,报告 secret_values_exported=0

本节检查表

[ ] 实际版本的 pg_default_services 已读取
[ ] dest/check/selector/backup 分别解释
[ ] local/dedicated service provider 的失败域明确
[ ] PostgreSQL LOGIN 与 PgBouncer auth delivery 分开验收
[ ] rendered HAProxy 与 runtime stats 都检查
[ ] SHOW CONFIG/POOLS 不导出敏感材料
[ ] client probe 映射到具体 member/role
[ ] 变更来自 inventory,不手改渲染产物
[ ] config check 只是语法门,不是完成条件
[ ] staged reload 验证 old/new connection
[ ] role change 后重新验证 pool state
[ ] rollback 恢复声明、runtime 与 client behavior

参考资料


上一节:连接预算与过载边界 · 返回本章目录 · 下一节:实战:写入、只读与管理三类接入 · 查看全书目录 · 查看索引中心

最后更新于