跳转到主要内容
lurno
全部文章
博客

门户、子账户与嵌套组织:培训机构的多租户之问

门户、子账户和嵌套组织是三样不同的东西。你的平台用的是哪一种,决定了你能卖给客户什么、卖多少钱。

The Lurno teamAugmental阅读约 1 分钟

当一家培训机构签下一个想要自有品牌学院的客户时,底层平台的架构决定了这件事是一下午的配置,还是一个六周的项目。有三样不同的东西都被当作多租户来卖:门户、子账户,以及真正嵌套的组织。只有第三种能给客户自己的域名、自己的管理员、自己的报表,以及一条在开发者忘记加过滤条件时依然成立的数据边界。

在客户提出这个模型表达不了的要求之前,这个区分读起来像是厂商的琐碎细节。之后它就变成了一个商业问题,因为你能卖什么,受限于你能隔开什么。

客户真正要的是什么

把需求文档剥到底,剩下四条请求。它们到来的顺序大致如此,而几乎每一个付钱让你培训其员工的客户都会提出它们。

  1. 他们自己的域名。 `learn.acmecorp.com`,不是 `yourcompany.lms-vendor.com/acme`。而且证书不能让浏览器报警。
  2. 他们自己的管理员。 客户那边有人可以添加用户、发布课程、导出报表,而不必先给你发邮件。
  3. 他们自己的报表。 关于他们自己的人、而且只有他们自己的人的数字,形式上要能直接摆到他们董事会面前。
  4. 他们的数据与其他客户的数据分开。 通常是作为一个安全问题提出来的,而且总是采购会往上升级的那一个。

每一种架构都能回答其中一部分,在另一部分上失败。一旦你知道自己站在哪个模型上,这些失败就是可以预料的。

多租户

一个运行中的系统实例服务许多客户组织,每个组织只看得到自己的数据、用户、品牌和配置。租户共享代码和基础设施;它们不共享记录。判断标准不是某个租户能不能换一个 logo——而是一个租户的数据能否从另一个租户的会话中,通过任何途径被访问到,包括一次失误。

三种架构,三重天花板

下面三个模型不是一条成熟度阶梯。它们是给不同买家的不同产品,而每一种都有一个具体的止步之处。

门户

门户是通向同一堆内容和用户的一道独立前门。你定义一群受众——员工、合作伙伴、某一家客户企业——给它一个 URL 和一套主题,然后把人分配进去。好几个企业平台都是围着这个形状做的;LearnUpon 是最清楚的例子,多门户交付就是这款产品的核心。

门户很好地回答了第一条请求。客户拿到一道看起来像他们自己的前门,登录进来的学员从来看不到其他受众。对于一家用一个账户同时培训员工、合作伙伴和客户的公司来说,这往往是恰到好处的结构量。

门户的列表是扁平的,止步之处就在这里。一家有三个区域、还有一位集团级培训总监的客户,不是一个门户,也不是三个互不相干的门户。用扁平模型的机构,最后只能把缺失的层级编码进名字里——`Acme`、`Acme - EMEA`、`Acme - APAC`——然后每季度在表格里手工拼回集团视图。管理员角色也有同样的问题:权限是按门户授予的,所以集团总监拿到三份授权,而某个区域关停时没人记得把它们收回。

Lurno

客户是一个包含自身各区域的组织,所以在顶层授予的角色适用于它下面的一切,集团报表就是那个父节点。

Not this

客户是扁平列表里三条恰好共用一个前缀的条目,集团报表是某人每季度重建一次的表格。

子账户

子账户通常来自计费系统,而不是数据模型。一个付费账户拥有若干子账户,一般只有一层,共用一张用户表、一份订阅,往往还共用一个域名,每个子账户占一条路径。TalentLMS 坦率地承认,比起“完整多租户加每租户自定义域名”,这是一种更轻的隔离;而对一个培训自家员工的小团队来说,这份轻是特性,不是缺陷。

值得问的问题是:这条边界在哪里被强制执行。如果子账户只是行上的一个字段,那么隔离就等于应用代码记得去按什么过滤。

-- 一条活在应用代码里的边界
select * from enrolments where account_id = :current_account;

-- 一条活在数据库里的边界
create policy tenant_read on enrolments
  for select using (org_id = any (current_org_scope()));

第一条查询一直是正确的,直到有人做了一个新的导出接口,漏掉了 `where` 子句。第二条无论如何都仍然正确,因为数据库拒绝返回这些行。采购在问客户数据是怎么隔开的时候,摸的就是这道缝——哪怕它是用认证和审计的词汇来问的。

子账户等同于租户吗?

不等同。子账户是一个租户内部用于分组和计费的构造:它通常共用父账户的用户表、域名、配置,往往还共用管理员。租户是一条边界——自己的域名、自己的管理员、自己的品牌、自己的数据,靠系统强制执行的规则来隔开,而不是靠代码施加的一个过滤条件。一个平台可以有子账户,同时在客户安全评审所关心的每一个意义上,仍然是单租户的。

嵌套组织

在第三种模型里,组织是一等记录,而且组织可以有父级。品牌、域名、用户、角色、内容、报名和报表全都挂在它下面。你的培训公司是一个组织。每家客户是你这个组织里面的一个组织。客户的各个区域是他们那个组织里面的组织,一直嵌到他们真实结构的深度为止。

Your training company              you administer everything below
├── Acme Corp                      learn.acmecorp.com
│   ├── Acme EMEA                  regional admin, EMEA data only
│   └── Acme APAC
├── Borden Group                   training.bordengroup.com
│   └── Borden Manufacturing
└── Public catalogue               open enrolments, your brand

由此得到两样扁平模型给不了你的东西。第一,客户自身的形状不靠命名约定就能表达——你不必把层级编码进字符串。第二是范围:在某个节点上授予的角色适用于该节点下的一切。Acme 的培训总监看到 Acme 和两个区域。EMEA 的经理看到 EMEA。你看到全部。Borden 的任何人都看不到其中任何一部分,而这些都不是谁手工写出来的报表。

它也改变了客户离场意味着什么。在扁平系统里,客户走了,就得有人照着一份门户、用户、分组和报表的清单一条条走完,并且指望这份清单是完整的。当客户是一棵子树时,你删掉这棵子树,挂在它下面的一切随之而去。

三种模型分别怎么回答那四条请求

  • 他们自己的域名。 门户:通常可以,每个门户一个,有时要另外收费。子账户:往往是厂商域名下的一条路径或一个子域名,而不是客户自己的域名。嵌套组织:每个组织一个域名,任意深度均可——前提是平台会自动签发证书。
  • 他们自己的管理员。 门户:可以,但范围限于那一个门户,所以集团级管理员需要在每个门户上各授权一次。子账户:通常是父账户的管理员带一个过滤后的视图。嵌套组织:任意节点上都可以有管理员,权限会向下级联到它下面的一切。
  • 他们自己的报表。 门户:按门户出,跨门户的汇总要看具体产品。子账户:一般是一份报表加一个过滤条件,这没问题——直到客户要求自己来看。嵌套组织:每个节点都是一个报表范围,所以集团视图和区域视图是同一个查询在不同高度上的结果。
  • 他们的数据被隔开。 门户和子账户:应用代码把它们隔到多开,就有多开。嵌套组织:平台在应用层之下强制到什么程度,就隔到什么程度——具体要问:这条规则是不是坐在数据库里。

这些都不说明门户是错的。它们只是给不同买家的不同产品。错误在于:因为演示里换了个 logo 就买下门户模型,然后在签下一个有区域划分的客户那天,撞到天花板。

该向厂商提的五个问题

  1. 一个组织能不能包含另一个组织,能嵌多深?
  2. 如果我在某个节点上给某人授予管理员角色,它适用于该节点下的一切,还是我要在每一层再授一次?
  3. 租户边界是在数据库里强制执行,还是在应用代码里?
  4. 每家客户能不能有自己的域名和自己的证书,还是只能有你们域名下的一个子目录?
  5. 客户离开时,把他们移除究竟删掉了什么——能演示给我看吗?

把答案要到一份共享文档里,而不是在电话里听。尤其是第二个问题,往往换来的是演示一个界面,而不是一个回答,而这两者之间的差距是一整年的手工授权。

为什么这个答案决定了你能卖什么

如果你的平台只能给一道前门换主题,那你卖的是你系统上的席位。客户的员工登录看到的是你的品牌,客户的培训经理向你要数字,而每一次续约都是一场关于你的平台每用户成本的对话。

如果你能交给客户一个域名、一批管理员、一套报表,以及一条经得住安全评审的边界,那你卖给他们的是他们自己的学院。这是另一种合同。它的续约方式也不同,因为到那时客户已经有了自己的管理员、自己的习惯,里面还有自己的内容,而且它经得住对方换人负责。

另一半是运营。撑破扁平模型的机构,往往最后变成每家客户跑一套安装。Moodle 明确说明多租户不属于核心产品——要隔开各校区或各客户组织,意味着 Moodle Workplace、一份合作伙伴合同,或者并行的多套安装。并行的安装意味着并行的升级、并行的备份、并行的安全补丁,以及一个你要在平台之外重建的报表层。在你的员工比客户多的时候,它是行得通的。

我能不能干脆每家客户跑一套独立实例?

可以,而且这确实是能拿到的最干净的隔离。代价不在许可,而在运维:每多一家客户,就多一次要测的升级、一份要验证的备份、一轮要打的补丁,和一套要管的凭据,而任何跨客户的问题都只能在系统之外回答。当客户的监管机构有此要求时,独立实例是说得通的。作为一家成长中的机构的默认模型,你每签一单,维护负担就上升一分。

一家培训机构究竟需要多少个租户?

每一个有自己的品牌、自己的管理员或自己的合规义务的客户组织,配一个租户。再往下,客户租户内部的子组织来处理区域、部门和班期。机构通常在一个方向上弄错:他们按课程或按班期建租户,于是品牌、域名和管理成倍增加,却换不来任何隔离上的好处——而客户自己那棵树里的一个节点本来就能干同样的活。

Lurno 站在哪里

Lurno 建在第三种模型上。一个组织可以包含子组织,每个子组织有自己的品牌和自己的自定义域名,并自动配置 TLS。组织内部还有第二棵分支树,在某个分支上授予的角色会沿着它向下级联——这就是上面第二个厂商问题的答案,也是集团培训总监只需一次授权、而不是每个区域一次的原因。

这条边界是在 Postgres 里强制执行的,而不是在应用里:676 次迁移中共 868 条行级安全策略,所以新接口里漏掉一个过滤条件,返回的是空,而不是别人的学员。这是值得向任何厂商核实的一条说法,包括我们——细节在安全页上,品牌和域名那一面在白标上。

已经在这上面跑起来的两种形态,不点名:一家企业学院在一个租户里为 15 家客户企业做培训,以及一家 K-12 出版社在单一平台上运行 114 所学校。两者是同一个结构在不同宽度上的样子。

有两条限制值得直说,因为它们会改变评估结果。支付和结账在开发中——今天客户是在平台之外开票的,所以如果你需要学员在报名当下刷卡付费,那这是一次需要谈的对话,而不是一个功能。合作方登录(静默 SSO)可用,但 SAML 和 OIDC 在路线图上,尚未交付;如果客户的 IT 部门已经决定了他们的员工要怎么认证,那就先问这件事,再问别的。转售培训的机构所适用的其余模型,写在培训机构页上。

简短版

门户给客户一道前门。子账户给客户一个过滤后的视图。嵌套组织给客户一条边界,而边界才是你能标上价的东西。在客户向你要那件你的模型说不出口的东西之前,先弄清楚你买的是哪一种。