不要被避免锁定而困住
架构能量的很大一部分都花在了减少或避免锁定上。这是一个相当崇高的目标:架构旨在为我们提供选择,而锁定则相反。但是,锁定不是一个简单的真或假问题:避免被锁定在一个方面通常会将你锁定在另一个方面。此外,诸如开源自动消除锁定之类的流行观念,事实证明并非完全正确。是时候仔细看看锁定,这样你就不会被避免锁定而困住!
2019 年 9 月 9 日
架构师的主要目标之一是 创建选项。这些选项使系统能够容忍更改,因此我们可以推迟决策,直到获得更多信息或对不可预见事件做出反应。锁定则相反:它使从一种解决方案切换到另一种解决方案变得困难。因此,许多架构师可能认为它是他们的死敌,而他们则将自己视为 IT 系统自由世界的守护者,在该自由世界中,组件可以随意替换和互连。
锁定 - 架构师的死敌?
但架构很少那么简单 - 它是权衡取舍的业务。经验丰富的架构师知道,锁定不仅仅是宣称必须避免它。锁定具有许多方面,甚至可能是首选解决方案。因此,让我们进入 架构电梯,仔细看看锁定。
开源混合多云 == 无锁定?
如今,我们正在其上部署软件的平台变得越来越强大 - 现代云平台不仅告诉我们 我们的照片是显示小狗还是松饼,它们还会编译我们的代码、部署它、配置必要的基础设施以及存储我们的数据。
这种极大的便利性和生产力助推器也带来了全新的锁定形式。混合/多云设置(如今似乎吸引了许多架构师的注意)是你在处理锁定时必须考虑的事情的一个很好的例子。假设你有一个想要部署到云的应用程序。这很容易做到,但从架构师的角度来看,有很多选择,甚至更多权衡取舍,尤其是与锁定相关的权衡取舍。
你可能希望在容器中部署你的应用程序。这听起来不错,但是你应该使用 AWS 的弹性容器服务 (ECS) 来运行它们吗?毕竟,它是亚马逊云的专有产品。更喜欢 Kubernetes?它是开源的,可以在大多数环境中运行,包括本地环境。问题解决了吗?不完全是 - 现在你被绑定到 Kubernetes - 考虑一下所有那些宝贵的 YAML 文件!所以你用另一个锁定换了一个锁定,不是吗?如果你使用托管的 Kubernetes 服务,例如 Google 的 GKE 或 Amazon 的 EKS,你可能也会被绑定到特定版本的 Kubernetes 和专有扩展。
如果你需要你的软件在本地运行,你也可以选择 AWS Outposts,这样你确实有一些选择。但这又是专有的。它与 VMWare 集成,你可能已经被锁定到 VMWare,所以它真的有区别吗?Google 的等效产品,新近推出的 Anthos,是使用开源组件构建的,但仍然是专有产品:你可以将应用程序迁移到不同的云 - 只要你继续使用 Anthos。这正是锁定的定义,不是吗?
或者,如果你将部署自动化与应用程序运行时很好地分离,这是否会使基础设施切换变得相当容易,从而减少所有这些锁定的影响?嘿,甚至还有跨平台的基础设施即代码工具。难道这些工具不应该完全消除这些担忧吗?
对于你的存储需求,如何使用 AWS S3?其他云提供商提供与 S3 兼容的 API,那么即使 S3 是专有的,它也可以被认为是多云兼容且无锁定的吗?你也可以将所有数据访问包装在抽象层后面,从而将任何依赖项本地化。这是一个好主意吗?
看来避免锁定并不那么容易,甚至可能让你被困在试图逃避锁定的过程中。为了突出云架构仍然很有趣,我将引用 Simon Wardley 对混合云的看法。
锁定的阴影
锁定不是非此即彼的事情。
电梯架构师(那些上下乘坐 架构电梯 的人)在许多人只看到黑白的地方看到了灰色。在考虑系统设计时,他们意识到像锁定或耦合这样的共同属性不是二元的。两个系统不仅仅是耦合或解耦,就像你不仅仅是被锁定到某个产品或没有被锁定到某个产品一样。这两个属性都有许多细微差别。例如,锁定分解为许多维度
- 供应商锁定:当 IT 人员提到“锁定”时,这通常是他们所指的类型。它描述了从一个供应商切换到竞争对手的难度。例如,如果你从 Siebel CRM 迁移到 SalesForce CRM 或从 IBM DB2 数据库迁移到 Oracle 数据库,这将花费你一臂之力,那么你就被“锁定”了。这种类型的锁定很常见,因为供应商通常(或多或少明显地)从中受益。这种锁定包括商业安排,例如长期许可和支持协议,这些协议让你在当时获得了许可费的折扣。
- 产品锁定:相关但不同的是被锁定到某个产品。当你从一个供应商的产品迁移到另一个供应商的产品时,你通常会同时更改供应商和产品,因此这两个很容易混淆。开源产品可以避免供应商锁定,但它们不会消除产品锁定:如果你使用 Kubernetes 或 Cassandra,你肯定会被锁定到特定产品的 API、配置和功能。如果你在专业(尤其是企业)环境中工作,你也需要商业支持,这将再次将你锁定到供应商合同中 - 见上文。大量定制、集成点和专有扩展是产品锁定的形式:它们使切换到另一个产品变得困难,即使它是开源的。
- 版本锁定:除了被锁定到某个产品之外,你甚至可能被锁定到特定版本。如果版本升级破坏了你构建的现有定制和扩展(SAP,有人吗?),那么版本升级可能会很昂贵。其他版本升级实际上要求你重写你的应用程序 - AngularJS 与 Angular 2 令人想起。更糟糕的是,版本锁定会传播:某个产品版本可能需要某个(通常是过时的)操作系统版本,依此类推,这会将任何迁移尝试变成一个削羊毛的练习。当你发现供应商决定弃用你的版本或停止整个产品线时,你会特别强烈地感受到这种锁定:你必须在没有支持的情况下选择继续使用或进行重大改造。事情可能会变得更糟,例如,如果在你的旧版本中发现了重大安全漏洞,并且没有提供补丁。
- 架构锁定:你可能也被锁定到特定类型的架构中。例如。当你广泛使用 Kubernetes 时,你很可能正在构建小型服务,这些服务会公开 API 并可以作为容器部署。如果你想迁移到无服务器架构,你将希望将服务的粒度更改为更接近单个函数,外部化状态管理,利用事件架构,以及可能还有其他一些事情。这种变化并非微不足道,而是意味着对你的应用程序架构进行重大改造。
- 平台锁定:产品锁定的一个特殊形式是被锁定到某个平台,尤其是云平台。这些平台不仅运行你的应用程序,而且还可能保存你的用户帐户和关联的访问权限、安全策略、基础设施细分以及更多方面。它们还提供应用程序级服务,例如存储或机器学习服务,这些服务通常是专有的。远离这些服务似乎是减少平台锁定的方法,但这会否定迁移到云的主要动机之一。非软件人员称之为发现自己处于进退两难的境地。
- 技能锁定:随着你的开发人员开始熟悉特定类型的产品或架构,你将拥有技能锁定:你需要时间来重新培训(或雇用)开发人员来使用不同的产品或技术。由于技能可用性是当今 IT 商店的主要限制之一,因此这种类型的锁定是真实存在的。一些利基企业产品拥有特别有限的开发人员供应,导致你的开发人员成本上升。这种影响对于使用自定义语言或(具有讽刺意味的是)对于“仅配置”/ 无代码框架的产品来说尤其明显。
- 法律锁定:你可能出于法律原因(例如合规性)而被锁定到特定解决方案。例如,如果你无法将数据迁移到另一个云提供商的数据中心(该数据中心位于你的国家/地区之外),你可能无法迁移数据。你的软件提供商的许可证也可能不允许你将系统迁移到云,即使它们可以完美运行。如果你决定无论如何都要这样做,你将违反许可条款。法律方面渗透到工程的更多方面,超出了我们通常的假设:你的小型发动机飞机很可能由 1970 年代设计的发动机提供动力,并且燃烧高铅汽油:新的发动机设计面临着很高的法律责任。
- 心理锁定:最微妙,也是最危险的锁定类型是影响你思维的锁定类型。在使用了一组特定的供应商和架构之后,你很可能会将假设融入你的决策过程,这可能会导致你拒绝替代方案。例如,你可能会拒绝扩展架构,因为它们没有线性扩展(当你将硬件翻倍时,你不会获得两倍的性能)。虽然从技术上讲是正确的,但这种思维方式忽略了可扩展性而不是效率是主要驱动力这一事实。或者你可能对短发布周期感到不满,因为你已经观察到频繁的更改会导致更多缺陷。当然,你被告知编码很昂贵、耗时且容易出错,因此最好通过配置来完成所有操作。
开源软件不是锁定的灵丹妙药。
总之,锁定远非非此即彼的事情,因此了解不同的类型可以帮助你做出更明智的架构决策。该列表还揭穿了一些常见的误解,例如使用开源软件神奇地消除了锁定。开源可以减少供应商锁定,但大多数其他类型的锁定仍然存在。这并不意味着开源不好,但它不是锁定的灵丹妙药。
使用模型做出更好的决策
经验丰富的架构师不仅看到了更多的灰色,而且还实践了良好的决策纪律。这很重要,因为我们比我们通常愿意相信的决策者要糟糕得多 - 如果你有任何疑问,请快速阅读 卡尼曼的《思考,快与慢》。
提高决策能力最有效的方法之一是使用模型。即使是简单的模型,甚至尤其是简单的模型,在提高决策能力方面也出奇地有效。
简单但发人深省的模型是伟大科学家的标志,而过度阐述和过度参数化往往是平庸的标志。
-- 乔治·博克斯
这就是为什么你不应该嘲笑管理咨询师们如此喜爱的著名二乘二矩阵。正如我们很快就会发现的那样,它是最简单,因此也是最有效的模型之一。
环境越不确定,结构化模型越能帮助你做出更好的决策。
关于模型,还有一个重要的点:一个普遍的观点告诉我们,面对不确定性,你几乎必须“凭直觉行事”——毕竟,一切都在变化。但事实恰恰相反:当我们不得不处理许多相互依赖关系、高度不确定性和小概率时,我们普遍糟糕的决策只会变得更糟。因此,这就是模型最能帮助我们为决策带来急需的结构和纪律的地方。决定是否以及在多大程度上接受锁定,就属于这一类,所以让我们使用一些模型。
锁定作为二维矩阵
一个简单的模型可以帮助我们克服“锁定 = 坏”的刻板印象。首先,我们必须意识到,很难不锁定任何东西,所以一定程度的锁定是不可避免的。其次,如果我们获得了相应的回报,例如以独特功能或竞争产品不提供的效用形式,我们可能会很乐意接受一定程度的锁定。
让我们在一个非常简单的模型中表达这些因素——一个二乘二矩阵。
该矩阵概述了我们沿着以下轴线的选择。
- 切换成本(又称“锁定”):我们切换到另一个解决方案的难度有多大?
- 独特效用:与替代方案相比,我们从该解决方案中获得了多少收益?
现在我们可以考虑每个象限。
- 一次性:没有独特效用且易于替换的组件是我们最不需要担心的。我们可以保持现状,或者,如果我们遇到任何问题,我们可以轻松地替换它们。对于普通的东西来说,这并不是一个糟糕的选择。例如,大多数开发人员 IDE(EMACS 可能是一个值得注意的例外!)都属于这一类:随意组合,不要太依恋它们。用于所有照片和其他个人数据的云存储也已将你的智能手机设备很大程度上移入了这个框,但我们稍后会详细介绍。
- 可接受的锁定:沿着对角线的是将你锁定到特定产品或供应商的组件,但作为回报,它们提供了独特的特性或效用。虽然我们通常更喜欢更少的锁定,但这种权衡可能是可以接受的。你可能会使用像 Google Cloud BigQuery 或 AWS Bare Metal Instances 这样的产品,明知你被锁定了,但你已经根据你获得的回报做出了明智的决定。对于一个小应用程序,你可能也乐意使用本地的 AWS 服务,因为迁移不太可能,而开发和运营工作量的减少非常受欢迎。
- 谨慎:最不利的框是将你锁定但没有给你带来太多独特效用的框。你的传统关系数据库可能属于这个框——使用任何专有数据库真的会增加你的收入吗?不,不会。但是,迁移可能会花费很多精力,所以你最好确保你不太可能需要这样做。如果你为你的嵌入式系统选择了一个特定的硬件,然后将其发射到外太空,那可能没问题——迁移的可能性很低。
- 理想:最好的东西是既能给你带来独特效用,又能轻松地切换。虽然这听起来像是理想的目标,但你必须承认,这个框有点自相矛盾:如果一个解决方案给你带来了独特的效用,那么根据定义,竞争产品就不会拥有它,这使得迁移变得困难。S3 可能是此类别的合适示例——多个云供应商已采用相同的 API,这使得切换到例如 GCP 相对容易。尽管如此,每个实现都有一些关于位置、性能等的独特优势。为了保护这种跨差异化产品的可移植性,重要的是我们不允许 API 被版权或专利保护。
虽然该模型不可否认地很简单,但将你的软件(也许还有硬件)组件放入这个矩阵中是一个值得的练习。它不仅可视化了你的风险敞口,而且还能够很好地将你的决策传达给各种利益相关者。
对于一个日常的例子,你可能已经决定使用以下项目,它们为你提供了不同程度的锁定和效用(从右上角逆时针方向)。
- 你心爱的iPhone将你锁定在一个供应商生态系统中,但它也提供了独特的效用,所以你可能很乐意接受这种可接受的锁定。
- 你的移动运营商合同将你锁定在一个网络中,但与其他网络相比,它并没有真正提供太多效用。最好谨慎行事。
- 你的手机充电器有一个标准的连接器。不幸的是,许多 iPhone 没有,但幸运的是,一个适配器电缆仍然使这个小工具一次性。
- 你许多应用程序,例如消息应用程序,为你提供了效用,例如你的朋友都在使用它,但它们仍然旨在使切换变得容易,例如通过使用你手机的联系人列表。这是理想的。
独特的产品特性并不一定能转化为你独特的效用。
关于独特效用,需要提醒你一句:每个供应商都会给你提供某种形式的独特功能——这就是他们与众不同的方式。但是,这里重要的是,该功能是否转化为你和你组织的具体和独特的价值。例如,一些云提供商在其惊人的全球网络上运行着数十亿用户服务。这令人印象深刻且独特,但不太可能成为那些很乐意为 100 万客户提供服务,并且可能被限制在一个国家/地区开展业务的普通企业的效用。有些人仍然在有严格限速的小国家/地区购买法拉利,所以显然并非所有决策都是完全理性的,但也许法拉利能给你带来比云平台更多的效用。
锁定的实际成本
由于这个简单的矩阵非常有用,所以让我们再做一个。前面的矩阵将切换成本视为一个单一元素(或维度)。一位优秀的架构师可以看出,它可以分解为两个维度。
该矩阵区分了进行切换的成本与你进行切换的可能性。那些可能性低且成本低的因素不应该让你太担心,而相反,那些切换成本高且切换可能性高的因素则不好,应该予以解决。在另一个对角线上,你正在冒险选择那些会让你付出代价但不太可能发生的选项——这就是你想要购买一些保险的地方,例如通过限制变更范围或增加维护预算。你也可以接受风险——你真的需要多长时间才能从 Oracle 迁移到 DB2,反之亦然?最后,如果切换很可能但很便宜,你就实现了敏捷性——你拥抱变化,并为你的系统设计了低执行成本。奇怪的是,尽管许多小的变化加起来很快,但这个象限往往比左上角的象限受到的关注更少。这就是我们糟糕的决策在起作用:不太可能发生的戏剧会得到更多关注,因为万一!
在讨论锁定的可能性时,你将需要考虑各种会让你切换的场景:供应商可能会倒闭、涨价,或者可能不再能够支持你的规模或功能需求。有趣的是,减少锁定的愿望有时会以谈判工具的形式出现:在协商许可证续签时,你可以暗示你的供应商,你已经设计了你的系统,以便从他们的产品中切换出去是现实且廉价的。这可能会帮助你协商到更低的价格,因为你已经传达了你的 BATNA——你的最佳谈判备选方案很低。这是一个实际上并不打算使用的架构选项——它是一种威慑,有点像冷战中的武器储备。你可能能够伪造它,而实际上并没有减少锁定,但你最好是一个优秀的扑克玩家,以防供应商识破你的虚张声势,例如通过在饮水机旁与你的开发人员聊天。
减少锁定:执行价格
再次引用我们从一开始就提到的选项类比,如果避免锁定能给你带来选择,那么进行切换的成本就是该选项的执行价格:这是你执行该选项需要支付的费用。你想要实现的切换成本越低,该选项的价值就越高,因此价格也越高。虽然我们梦想着所有系统都处于“绿色框”中,切换成本最小,但必要的投资可能实际上不会带来回报。
最小化切换成本可能不是最经济的选择。
例如,许多架构师都倾向于不锁定到某个数据库供应商或云提供商。但是,切换的可能性有多大呢?也许 5%,甚至更低?将切换成本从例如 50,000 美元(用于半手动迁移)降低到接近零,需要花费你多少成本?很可能比你预计节省的 2,500 美元(50,000 美元 x 5%)要多得多。因此,最小化切换成本并不是唯一目标,而且很容易导致过度投资。这相当于过度保险:支付巨额保费将免赔额降至零可能会让你安心,但这通常不是最经济的,因此也不是最理性的选择。
最后一个模型(这一次不是矩阵)可以帮助你决定应该在降低切换成本方面投入多少。下图显示了你的负债,定义为切换成本乘以其发生的可能性与你必须进行的预先投资(蓝线)的乘积。
通过投资于选项,你当然可以减少你的负债,方法是减少切换的可能性或降低执行切换的成本。例如,使用像Hibernate这样的对象关系映射(ORM)框架是一项小投资,可以减少数据库供应商锁定。你也可以创建一个元语言,将其翻译成每个数据库供应商的本机存储过程语法。这将使你能够充分利用数据库的性能,而不会依赖于它,但它将需要大量的预先努力才能应对一个不太可能发生的场景。
因此,有趣的功能是红线,它将预先投资添加到潜在负债中。这就是你的总成本,也是你应该最小化的东西。在大多数情况下,随着预先投资的增加,你将朝着最佳范围移动。在减少锁定方面的额外投资实际上会导致更高的总成本。原因很简单:投资回报率会递减,特别是对于概率很小的切换而言。如果我们使我们的架构变得非常灵活,我们很可能陷入过度投资的区域。那些Yagni(你不需要它)的人可能瞄准光谱的另一端——就像往常一样,诀窍是找到一个折衷方案。
避免锁定的总成本
现在我们已经对锁定带来的成本和潜在回报有了相当好的了解,我们需要仔细看看避免锁定的总成本。在前面的模型中,我们假设避免锁定是一项简单的成本。然而,实际上,这种成本可以分解为几个组成部分。
复杂性可能是你为减少锁定而付出的最大代价。
- 工作量:这是在人时方面需要做的额外工作。如果我们选择在 Kubernetes 之上使用容器进行部署以减少云提供商锁定,那么这一项将包括学习新工具、编写 Docker 文件、配置 Kubernetes 等的工作量。
- 费用:这是额外的现金支出,例如产品许可证、聘请外部提供商或参加 KubeCon。
- 低效利用:这种间接成本的产生是因为避免锁定通常会阻止你使用供应商特定的功能。结果,你无法充分利用你使用的软件。这反过来可能意味着你需要付出更多努力来构建缺失的功能,或者会导致你的产品出现弱点。
- 复杂性:复杂性是方程式的核心要素,却被经常忽略。许多减少锁定的努力引入了额外的抽象层:JDBC、容器、通用 API。虽然这些都是有用的工具,但这样的层会增加另一个活动部分,从而增加整个系统的复杂性。这反过来会增加新团队成员的学习工作量,并增加系统错误的可能性。
- 新的锁定:避免一种锁定往往是以另一种锁定为代价的。例如,你可能选择避免使用 AWS CloudFormation,而是使用 Hashicorp 的 Terraform 或 Pulumi,它们都支持多个云提供商。但是,现在你被锁定在另一个供应商的另一个产品中,你需要弄清楚这是否适合你。
在计算避免锁定的成本时,架构师应该快速浏览一下这个列表,以避免盲点。此外,请注意,避免锁定的尝试可能会有漏洞,就像 有漏洞的抽象 一样。例如,Terraform 是一个很好的工具,但它的脚本使用了许多供应商特定的结构。因此,实现细节会“泄露”出来,使得从一个云切换到另一个云的切换成本绝非零。
将它们重新组合在一起
有了这么多理论,让我们来看几个具体的例子。
部署容器
我曾与一家公司合作,他们将大部分代码打包到 Docker 容器中,然后部署到 AWS ECS 上。因此,他们被锁定在 AWS 中。他们是否应该投资将他们的容器编排替换为 Kubernetes,后者是开源的?鉴于功能速度是他们的主要关注点,而当前的 ECS 解决方案对他们来说运作良好,我认为迁移不会带来回报。切换到另一个云提供商的可能性很低,他们有“更大的鱼要炸”。
建议:接受锁定。
关系数据库访问
许多应用程序使用关系数据库,这种数据库可以由众多供应商和开源替代方案提供。但是,SQL 方言、存储过程和定制管理控制台都会导致数据库锁定。你应该投入多少资金来避免这种锁定?对于大多数语言和运行时,常见的映射框架(如 Hibernate)以低成本提供了一定程度的数据库中立性。如果你想进一步降低你的打击价格,你还需要避免 SQL 函数和存储过程,这可能会降低你的产品性能,或者要求你在硬件上花费更多。
建议:使用低成本机制来减少锁定。不要追求零切换成本。
迁移到云
与其从一个数据库供应商切换到另一个数据库供应商,你可能更感兴趣的是将你的应用程序(包括其数据库)迁移到云中。除了技术方面的考虑,你还需要小心一些供应商的许可协议,这些协议可能会使这种迁移变得不经济。在这种情况下,明智的做法是选择一个开源数据库。
建议:如果开源数据库能够满足你的运营和支持需求,请选择它,但接受一定程度的锁定。
多云
许多企业对可移植的多云部署的想法很着迷,并想出了越来越复杂(也更昂贵)的计划,这些计划表面上可以让他们免受云提供商锁定的影响。但是,大多数这些方法都否定了你想要使用云的真正原因:低摩擦和使用托管服务(如存储或数据库)的能力。
建议:谨慎行事。阅读我的 关于多云的文章。
以思想的速度进行架构
似乎人们可以花大量时间思考锁定。有些人甚至可能认为我们的方法是“学术性的”,我反复地无法理解这个词有什么不好,因为我们大多数人都是在那里接受教育的。但是,传统的非黑即白的架构方法不是更简单,也许更有效率吗?
如果你专注并坚持简单的模型,架构思维实际上非常快。
实际上,思考速度非常快。浏览本文中展示的所有模型可能只需要几分钟,并且可以得出记录良好的决策。除了纸张或白板之外,不需要任何花哨的工具。快速架构思维的关键要素仅仅是专注的能力。
将这与为冗长的指导委员会会议准备详细的幻灯片材料相比,这些会议提前几周安排,通常没有真正有能力做出明智决定的专家参加。
电梯架构师更愿意花时间思考,而不是等待会议。
致谢
我要感谢以下个人对本文的宝贵反馈和意见:Manlio Grillo、Michael Plöd、Michele Danieli 和 Scott Davis。
重大修订
2019 年 9 月 9 日:发布最终部分
2019 年 9 月 4 日:发布关于实际成本及其降低的章节
2019 年 9 月 2 日:发布关于锁定模型的章节
2019 年 8 月 29 日:发布关于锁定程度的章节