跳至内容

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-conditional

vector-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 pool

Pigsty 官方 Cluster / Instancepg_role: offline 定位为慢查询、ETL、OLAP 与交互分析专用 read-only replica。隔离的目的不是“让慢查询没人管”,而是不让它默认争用在线 replica。

规格要由容量曲线产生

不要先定义:

small=4c16g
medium=8c32g
large=16c64g

再假设业务会自然匹配。更可靠的顺序:

  1. 定义 workload class;
  2. 固定数据量、并发和增长;
  3. 测量容量曲线和饱和点;
  4. 选定安全 headroom;
  5. 映射到可采购/可部署资源;
  6. 定义升降配条件。

硬件 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、资源、故障
databasecatalog、连接入口、部分设置instance CPU/I/O/WAL、superuser、升级
instancepostmaster、内存、端口、WAL、配置host/kernel/storage/network
cluster/serviceHA/恢复/生命周期边界若共主机/控制平面仍可能相关

“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。

选择顺序从信任与故障开始

推荐问:

  1. tenant 是否互信?
  2. 数据分类是否允许共实例/共主机?
  3. 是否允许同一个 superuser/平台团队访问?
  4. workload 能否互相制造事故?
  5. 是否需要不同 PostgreSQL/extension/preload?
  6. 是否需要独立 failover/restore/upgrade?
  7. 跨 tenant 查询/事务是否必要?
  8. 成本和运营能力是否承受更高隔离?

安全硬边界优先于便利与成本。

多租户至少有三种模型

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; 生产退役还要更多:

  1. 确认 owner 与法律/业务 retention;
  2. 停止新连接和写入;
  3. 记录最后 backup/manifest;
  4. 导出需要保留的数据;
  5. drain 外部消费者和投影;
  6. 验证 DNS/service/secret/monitoring 依赖;
  7. 双人审批 destructive target;
  8. 擦除或进入保留;
  9. 保存完成证据。

“在 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 作为参考实现 · 查看全书目录 · 查看索引中心

最后更新于