解决方案
为隔离与规模而设计的多租户 SaaS 平台
我们构建 SaaS 产品之下的平台:租户模型、身份、计费、开通和发布机制。正是这些决定了产品能否像服务第一位客户那样,安全地服务第一百位客户。
问题域
一个 SaaS 产品其实是两套系统。一套是客户看到的应用。另一套是平台:它开通租户、认证用户、执行套餐限制、计量用量、出具账单,并把每个租户的数据与其他租户分隔开。第二套系统很少出现在路线图上,却决定着单位经济模型、企业级就绪程度,以及增长过程会有多痛苦。
最基础的决策是租户模型。在池化(pool)模型中,所有租户共享计算与存储,效率高、运维简单,但要求严格的逻辑隔离。在独立(silo)模型中,每个租户拥有专属资源,隔离性好,运行和发布成本更高。桥接(bridge)模型把两者结合起来,常见做法是共享计算、分库或分 schema,或者标准套餐使用池化基础设施、最大的客户使用专属技术栈。每一种选择都会影响成本、合规沟通、吵闹邻居风险和部署复杂度。
我们在设计平台时,让租户模型的决策明确、由代码强制执行,并在合理可行的范围内可以回退。租户上下文在边缘处一次性确立,并贯穿每一个服务、查询、缓存键、队列消息和日志行。平台中的其他一切,都建立在这一保证之上。
难在哪里
工程挑战
依赖开发者自觉的租户隔离
如果隔离靠的是每条查询都记得加上租户过滤条件,那么漏掉一个子句,就会把一个客户的数据暴露给另一个客户。这是 SaaS 产品可能出现的破坏性最大的故障。隔离必须在应用代码之下强制执行,通过行级安全、限定范围的凭证或独立存储来实现,使遗漏过滤条件时返回的是空结果,而不是全部数据。
吵闹邻居
在共享基础设施中,某个租户的批量导入或失控的报表可能耗尽数据库连接,拖慢所有其他租户的服务。如果没有按租户的限流、配额和工作负载分离,最大客户的行为就决定了最小客户的体验。
企业级身份要求
企业买家期望 SAML 或 OIDC 单点登录、SCIM 账号同步、强制多因素认证,以及来自其目录的角色映射。在自建的用户名密码表上事后补齐这些能力,既慢又容易出错,而且往往会在采购评审阶段成为阻碍。
与账单对不上的计量
按用量定价要求每一个计费事件恰好被计数一次。高负载下丢失的事件、重试时重复计数的事件,或者归到错误账期的事件,都会产生让客户提出异议的账单。计量首先是一个数据完整性问题,其次才是计费集成问题。
面向众多租户的发布
一次锁住大表的 schema 迁移,会让所有租户同时离线。租户独立部署时,问题则反过来:数百套技术栈逐渐漂移到不同版本。两种情况都需要经过设计的发布方案,涵盖向后兼容的迁移、渐进式发布和自动化的机群管理。
缺少按租户的健康视图
聚合仪表盘掩盖了某个租户的请求已经失败一小时的事实。支持团队答不上客户的问题,工程团队也分不清是平台故障还是租户自身的问题。遥测数据必须从第一个请求开始就携带租户标识。
我们如何构建
架构
平台拆分为管理租户的控制平面和服务租户的数据平面。租户上下文在边缘处一次性解析,并在其下的每一层强制执行。
- 01
控制平面
一组服务负责租户的生命周期:注册、开通、套餐分配、配置、停用和注销。开通是一个幂等的工作流,中途失败可以重试,不会留下创建到一半的租户。控制平面是“有哪些租户、它们的资源位于何处”的唯一事实来源。
- 02
租户解析与隔离层
网关依据经过验证的令牌声明或域名识别租户,并将其作为请求上下文向下传递。在池化的 PostgreSQL 设计中,行级安全策略把每条查询绑定到该租户;在桥接和独立设计中,路由层选择租户对应的 schema、数据库或集群。缓存键、对象存储前缀和消息主题也按同样的方式限定范围。
- 03
身份与访问
认证委托给基于标准的身份提供方,它支持按租户配置的 SAML 与 OIDC SSO 连接、SCIM 账号同步和可配置的 MFA 策略。授权基于角色,角色限定在租户范围内,并在集中的策略组件中求值,使各服务的规则保持一致。平台人员对租户数据的访问单独管控、有时间限制并留有日志。
- 04
计量与计费
各服务把带有幂等键的用量事件发送到持久化的事件流。聚合流水线去重后,按租户和账期汇总,再把总量推送给负责开票、税务和收款的计费服务商。原始事件予以保留,任何一行账单都可以重新推算并解释清楚。
- 05
权益与功能开关
套餐以权益的形式表达:租户拥有哪些功能,适用哪些限额。各服务查询权益服务并配合本地缓存,而不是把套餐名称硬编码在代码里。功能开关是另一套独立机制,用于渐进式发布,因此发布代码与为某个客户启用某项能力是两个相互独立的决定。
- 06
发布与迁移流水线
部署采用滚动或蓝绿方式,配有自动健康检查和回滚。Schema 变更遵循扩展与收缩(expand and contract)模式,使新旧应用版本在发布期间可以运行在同一个数据库上。对于独立部署的租户,基础设施即代码和机群编排器(fleet orchestrator)分批应用变更,先从内部租户和低风险租户开始。
- 07
按租户的可观测性
日志、指标和追踪都携带租户标识,因此服务等级目标、错误率和成本可以按租户报告。配额和限流在网关以及连接池、队列等共享资源处执行。告警针对租户级别的劣化触发,而不只是针对全平台的平均值。
租户感知的边缘
- 自定义域名
- 租户解析
- WAF
- 按租户限流
身份与 SSO
- SAML 与 OIDC
- SCIM 账号开通
- 角色与权限
- MFA
控制平面
只管理租户,从不承载其流量
- 租户生命周期
- 套餐与权益
- 计费与计量
- 功能开关
应用平面
- 无状态服务
- 后台任务
- 租户上下文
- Webhook
数据隔离
按套餐层级选择隔离模型
- 行级安全
- 每租户独立 Schema
- 专属数据库
- 每租户独立密钥
按租户的可观测性
- 用量指标
- 带租户标签的链路
- 吵闹邻居告警
- 审计日志
一次合作交付什么
- 租户模型决策记录,附成本与隔离分析
- 支持租户自动开通与注销的控制平面
- 涵盖 SSO、SCIM、MFA 与基于角色访问控制的身份集成
- 用量计量流水线与计费服务商集成
- 权益服务与功能开关框架
- 零停机部署与 schema 迁移流水线
- 按租户的仪表盘、配额与告警
开始之前
需要尽早做出的决策
- 池化、独立,还是桥接
- 当租户数量多且彼此相似时,默认采用在数据库层强制隔离的池化模型;它运行成本最低,发布也最简单。当客户要求专属基础设施、自有加密密钥或特定区域时,选择独立模型,并据此定价。桥接模型默认池化,同时可以把某个租户放到专属资源上,适合既面向小团队又面向大型企业销售的产品。
- 身份与计费:自建还是采购
- 除非它们就是您的产品,否则两者都应采购。托管的身份提供方和计费平台沉淀了多年在协议、税务和支付失败处理方面的边界情况。自建的应当是您特有的部分:租户模型、权益逻辑,以及向计费系统供数的计量流水线。
- 模块化单体,还是微服务
- 早期阶段的平台,通常更适合结构良好、模块边界清晰、单次部署的模块化单体。当某个模块有独特的扩展特征、发布节奏或归属团队时,再把它拆成服务。一开始就拆出许多服务,会在流量和人手都不足以支撑之前成倍增加运维工作。
- 何时提供单租户部署
- 当客户的要求无法通过逻辑隔离满足,并且合同金额足以覆盖运营成本时,才提供单租户部署。在此之前,先确保专属技术栈的开通与升级已经完全自动化。靠手工搭建单租户环境,SaaS 公司会在不知不觉中变成一家托管服务商。
常见问题
常见问题
现有的单租户产品能否改造成多租户?
通常可以,但工作量远不止加一个租户字段。它包括在整条请求路径上引入租户上下文、在数据层强制隔离、迁移现有客户的数据,以及改造后台任务、缓存和文件存储。我们一般分阶段进行,先建立隔离层和控制平面,再分批迁移租户。
如何证明租户隔离有效?
把它当作一项安全属性来测试。自动化测试会通过每一个 API 尝试跨租户读写,并在交付流水线中运行。数据库策略独立于应用代码进行评审和测试,隔离也被纳入渗透测试的范围。
零停机发布是否涵盖数据库变更?
可以涵盖,但需要纪律。变更被拆分为向后兼容的若干步骤:新增结构、双写、回填、切换读取,然后在后续版本中移除旧结构。会锁住大表的操作,使用在线迁移工具或分批执行。
你们基于哪家云构建?
设计不绑定某一家云厂商。我们使用主流公有云,并在托管服务能够减轻运维负担的地方选用托管服务。具体选择通常取决于您现有的云资源、数据驻留要求,以及未来负责运维该平台的团队所具备的技能。
