18.4 平台服务目录与多租户
如果消费者只能申请“一台 PostgreSQL”,平台交付的是资源,不是服务。
一个可用目录应该让消费者在申请前就知道:
得到什么
不保证什么
允许怎样使用
谁负责什么
怎样计量
何时复审
如何退出18.4.1 服务等级、规格、版本与扩展套餐
offering 是完整合同,不是机器型号
本章的
service-catalog.json
为每个 offering 保存:
id / status / environment
service owner
consumer owner requirement
topology
service objectives
isolation
backup class
allowed and exception extension bundles
quotas
lifecycle机器 CPU/内存/磁盘规格当然重要,但它只是实现 offering 的一种资源配置。
同一 pg-ha-standard 可以在不同硬件代际上实现;只要服务合同和经过验证的
容量仍然成立,消费者不应绑定某台主机。
pg-dev
定位:
development-test
single PostgreSQL instance
no HA commitment
explicit expiry
best effort它允许较广的试验 extension bundle,包括 federation-lab。这不意味着开发
环境可以无治理:
- 必须有 consumer owner;
- 连接、存储、temp、statement time 要声明;
- 必须有到期日;
- 敏感生产数据不能因为“只是 dev”就复制进去;
- 实验凭据不能进入 Git;
- 删除仍需精确目标和恢复需求确认。
开发 offering 的目标是加快安全实验,不是变成永不下线的影子生产。
pg-ha-standard
这是 pg36_shop 的默认生产候选:
primary + two replicas
reviewed failure domains
dedicated database by default
dedicated cluster when risk gate requires
continuous WAL + full/differential backup class目录中:
availability_target=99.9%-proposal
rpo_seconds=60
rto_minutes=30这些是要进入第 20、21、24 章验证的目标。它们不能由“三节点”直接推出。
允许的 bundle:
core
search-accepted
spatiotemporal-conditionalvector-pilot 只能走 exception;federation-lab 不允许。
pg-ha-critical
critical 不是把 standard 的数字改得更漂亮。它意味着:
dedicated cluster
explicit synchronous policy
explicit zero-RPO failure-domain scope
hard connection/overload budget
storage includes failure + upgrade headroom
quarterly objective/owner review
architecture and risk approval目录提出:
99.95%-proposal
RPO 0 in explicitly rehearsed failure domain
RTO 10 minutes“RPO 0”必须带范围。同步副本若与 primary 共享电源/机房,不能自动覆盖整个 区域故障;若为可用性临时降级同步策略,也可能改变承诺。
pg-analytics-offline
它不是第二个 source of truth:
read-only dependent service
offline replica
separate analytical query SLO
freshness proposal 60 seconds
not read-your-writes
source cluster backup policy
bounded analytics poolPigsty 官方
Cluster / Instance
把 pg_role: offline 定位为慢查询、ETL、OLAP 与交互分析专用 read-only
replica。隔离的目的不是“让慢查询没人管”,而是不让它默认争用在线 replica。
规格要由容量曲线产生
不要先定义:
small=4c16g
medium=8c32g
large=16c64g再假设业务会自然匹配。更可靠的顺序:
- 定义 workload class;
- 固定数据量、并发和增长;
- 测量容量曲线和饱和点;
- 选定安全 headroom;
- 映射到可采购/可部署资源;
- 定义升降配条件。
硬件 SKU 可以变化,基准 workload 与目标不应随意变化。
版本是一组矩阵
平台版本不只一个 postgresql=18:
OS
kernel / filesystem
PostgreSQL major + minor
extension packages
Patroni
etcd
HAProxy
PgBouncer
pgBackRest
exporters / monitoring
Pigsty release
client drivers
locale / collation兼容矩阵至少回答:
- 哪组是当前支持;
- 哪组是下一升级候选;
- 哪组已弃用;
- package repository 能否重建;
- backup/restore/replica/upgrade 是否覆盖;
- 安全补丁时限;
- 谁批准例外。
extension bundle 是服务的一部分
bundle 不只是安装清单:
{
"id": "vector-pilot",
"status": "pilot",
"extensions": [
{
"name": "vector",
"version_policy": "0.8.4 on validated fixture",
"lifecycle": "pilot"
}
],
"gates": [
"model identity and dimension pinned",
"exact and ANN quality pass",
"backup, restore, replica, upgrade rehearsed"
]
}offering 只引用已审阅 bundle,避免每个用户自行拼装无法升级的组合。
服务目标要分平台与消费者责任
平台可负责:
cluster availability
service routing
backup execution and restore capability
database engine/extension lifecycle
platform monitoring
capacity envelope
incident coordination消费者仍负责:
SQL/schema quality
业务不变量
连接/重试/timeout
流量预测
数据分类
应用兼容测试
on-call contact
错误预算决策责任边界要写 RACI,不能在事故时才争论“数据库没问题还是应用没问题”。
目录本身也需要版本与状态
本章目录:
release=1.6-proposal
status=proposal
review_cycle_days=90当下卷 evidence 完成后,应产生新 release,而不是原地把历史 proposal 改成 “一直就是通过”。目录是决策记录的一部分。
18.4.2 数据库、模式、实例和集群隔离
四层隔离解决不同问题
| 层级 | 隔离了什么 | 没隔离什么 |
|---|---|---|
| schema | 名称、对象组织、部分权限 | database GUC/连接、WAL、资源、故障 |
| database | catalog、连接入口、部分设置 | instance CPU/I/O/WAL、superuser、升级 |
| instance | postmaster、内存、端口、WAL、配置 | host/kernel/storage/network |
| cluster/service | HA/恢复/生命周期边界 | 若共主机/控制平面仍可能相关 |
“tenant 一个 schema”与“tenant 一个 cluster”不只是成本不同,安全与爆炸半径 完全不同。
schema 隔离
适合:
同一信任域
同一生命周期
需要跨 schema 查询
共享 database 设置可接受
对象数量可控风险:
search_path 注入
误授权 CREATE
同名对象解析
共享 connection/GUC
owner/superuser 可见
跨 schema 依赖
升级/删除耦合安全做法:
- 不给不可信运行时角色在公共解析路径中的
CREATE; - 对安全定义函数固定安全
search_path; - 使用显式 schema qualification;
- 分离 NOLOGIN owner 与 LOGIN runtime;
- 定期审计 ACL 与 dependencies。
database 隔离
适合:
同一 instance 中的中等信任边界
独立连接、schema/catalog、extension installation
不要求跨 database transaction/join
共享主机与实例故障可接受优点是 namespace 和连接边界更清楚;代价是:
- PostgreSQL 原生跨 database 查询不透明;
- connection pool/监控/迁移对象增多;
- extension 每 database 管理;
- 仍共享 WAL、CPU、I/O、backup 与 major upgrade;
- instance superuser 仍可越界。
database 不是强资源隔离。
instance 隔离
适合:
不同 preload/GUC/major
独立 restart/upgrade
强一些的内存/连接/WAL边界
不同维护窗口
高风险扩展或 workload同一 host 上多个 instance 仍共享:
CPU
kernel
filesystem/storage
network
host administrator
power/failure必须配 OS/cgroup/volume/port/backup 与监控隔离,否则只是多个 postmaster 抢 同一资源。
cluster 隔离
适合:
独立 HA and recovery objective
独立故障/升级窗口
高敏感或不互信 tenant
重 workload/noisy neighbor
特殊 extension/kernel
法规或数据驻留
独立成本归属代价是节点、备份、监控、升级、值班和容量碎片化。不是所有小数据库都值得 一个三节点 cluster。
选择顺序从信任与故障开始
推荐问:
- tenant 是否互信?
- 数据分类是否允许共实例/共主机?
- 是否允许同一个 superuser/平台团队访问?
- workload 能否互相制造事故?
- 是否需要不同 PostgreSQL/extension/preload?
- 是否需要独立 failover/restore/upgrade?
- 跨 tenant 查询/事务是否必要?
- 成本和运营能力是否承受更高隔离?
安全硬边界优先于便利与成本。
多租户至少有三种模型
shared tables + tenant_id
最省对象;需要强 tenant predicate/RLS 与索引设计
schema per tenant
对象分开;迁移、对象数量、search_path 复杂
database/cluster per tenant
隔离更强;运营和容量成本更高也可以混合:
small trusted tenants -> shared
larger tenants -> database
regulated/high-risk -> dedicated cluster但迁移路径要提前设计:如何从 shared 提升到 dedicated,identity、sequence、 外键、事件与投影如何移动。
RLS 是纵深防御,不是唯一边界
Row-Level Security 可以根据角色/会话上下文限制行,但需要审计:
table owner / BYPASSRLS
FORCE ROW LEVEL SECURITY
session tenant context injection
pooling mode
prepared statements
security definer functions
COPY / maintenance paths
foreign keys and side channels
backup/replica/admin access第 23 章会用正负 tenant 测试验证。不能因为 CREATE POLICY 成功,就宣布
隔离完成。
隔离与可观测性必须同粒度
若配额按 tenant,指标却只能看整个 cluster,就无法:
- 识别 noisy neighbor;
- 归属成本;
- 证明公平性;
- 按 tenant 降级;
- 预测迁移。
需要在不暴露敏感 SQL/数据的前提下,保留 service/database/role/workload class/tenant 等必要维度,并控制 cardinality。
18.4.3 成本归属、配额和生命周期
没有成本归属,平台会奖励浪费
共享数据库中,使用者容易只看到:
“多一张表”
“多一个索引”
“多跑一条报表”平台承担的真实成本:
compute
memory
primary + replica storage
WAL + archive
backup repository
network
monitoring retention
upgrade window
on-call labor
recovery time
capacity headroom成本模型不一定要精确计费,但必须让 owner 看见边际影响。
用服务单位表达成本
可以按组织成熟度从简单到复杂:
allocated cluster share
database GB-month × replica/backup multiplier
connection/CPU class
WAL GB and backup retention
query or workload class
dedicated instance/cluster fixed cost
operator support tier避免只按主表大小计费:索引、TOAST、replica、backup 与 WAL 可能远大于主表。
配额是正确性保护,不只是节省
重要配额:
| 配额 | 保护 |
|---|---|
| connection | 防止 backend/内存/调度耗尽 |
| active query | 防止过度并发 |
| statement timeout | 限制无界执行 |
| lock timeout | 限制排队与级联 |
| idle transaction | 防止长快照/持锁 |
work_mem class | 限制乘法内存 |
temp_file_limit | 防止 spill 填盘 |
| storage/WAL growth | 保护恢复链与容量 |
| logical slot retention | 防止 WAL 无限保留 |
| maintenance window | 保证 vacuum/index/backup |
配额超限行为要确定:
queue
reject
cancel
spill
throttle
degrade
escalate静默超卖不是服务。
连接预算从端到端计算
不要让每个应用实例都配置:
pool_max = max_connections预算应满足:
[ \sum pools + admin + monitoring + maintenance + HA\ reserve \leq safe\ backend\ budget ]
还要考虑:
- 每个应用副本数;
- failover 后流量汇聚;
- pool retry storm;
- transaction vs session pooling;
- offline/replica 独立预算;
- emergency admin 保留。
第 22、34 章会做饱和与过载演练。
生命周期从申请前开始
完整状态机:
request
-> classify data/workload/objective
-> choose offering/isolation/bundles
-> owner and cost approval
-> provision
-> acceptance evidence
-> operate
-> change / exception
-> periodic review
-> deprecate
-> drain/export
-> retain/erase
-> decommission evidence若没有 expiry/decommission,临时数据库会永久占用备份、监控和升级路径。
变更要分普通与例外
普通变更:
catalog-defined offering
supported version
allowed extension bundle
within capacity envelope
standard backup/security例外:
pilot extension in production
custom preload
unsupported version
RPO/RTO override
over-quota
cross-boundary data
lab authentication例外必须有:
owner
risk
compensating control
evidence
expiry
review date
rollback“临时允许”但没有到期日,通常等于永久。
删除与退役是高风险动作
本书实验里的 reset 有精确 token、target、marker 和 active-session guard; 生产退役还要更多:
- 确认 owner 与法律/业务 retention;
- 停止新连接和写入;
- 记录最后 backup/manifest;
- 导出需要保留的数据;
- drain 外部消费者和投影;
- 验证 DNS/service/secret/monitoring 依赖;
- 双人审批 destructive target;
- 擦除或进入保留;
- 保存完成证据。
“在 inventory 中删掉一段 YAML”不等于数据已经安全退役。
复审由事件与周期共同触发
周期复审:
owner
objective
usage/cost
capacity
version/support
exceptions
expiry事件复审:
major workload change
new data class
extension/version change
incident
failed restore/upgrade
ownership change
externalization trigger
provider/platform migration平台目录最终要可验证
本章 validator 会检查:
- offering ID 唯一;
- production offering 有非空
service_objectives; - allowed bundle 全部存在;
- blueprint 引用的 offering 全部存在;
- bundle 与 lower-volume gate 引用闭合;
- lab-only FDW 不得生产准入。
反例把 pg-ha-standard.service_objectives 置空时,必须得到:
E_SERVICE_OBJECTIVE机器验证不能判断 99.9% 是否合理,但能阻止“生产服务连目标字段都没有”的 结构退化。
上一节:明确替代边界 · 返回本章目录 · 下一节:Pigsty 作为参考实现 · 查看全书目录 · 查看索引中心