不要被锁定在反锁定的路上
原文:Don’t get locked up into avoiding lock-in
作者:Gregor Hohpe
减少或者避免被锁定,会消耗架构设计工作中的很大一部分成本。这是一个神圣的职责:架构就是提供选项,而锁定则刚好相反。然而锁定不是非白即黑的:摆脱某一方面的锁定,往往意味着在其它方面被锁定。同样地,开源软件之类的流行概念,据说天然的消除锁定,这并非事实。是时候详细考察一下锁定问题,防止我们被锁定在反锁定的路上。
架构师的主要职责之一就是提供选择。这些选项让系统能够容忍变化,有了选择的自由,我们可以耐心的等待信息完整之后才作出决定,以及应对一些预计外的事件。锁定的含义则刚好相反:锁定使得软件很难从一种方案切换到另一种方案。很多架构师可能会将锁定视为大敌,同时认为自己守护着 IT 世界中的自由,在这世界中,组件可以被随意替换和互联。
但是架构从来都不简单——这是个事关妥协的生意。经验丰富的架构师知道,锁定的重要性,可能会超过避免锁定的重要性。锁定有很多方面,有时候还可能是最佳方案。所以我们进入架构师电梯,仔细观察一下锁定这个事。
开源+混合多云=无锁定
近年来,我们用来部署软件的平台越来越强——现代云平台不止告诉我,我们的照片是小狗还是饼干,它们还会编译代码,进行部署,配置必要的基础设施,并保存数据。
这种便利性和生产力的急剧提高,带来了全新的锁定方式。吸引了很多架构师注意的混合多云方案,就是一个用于审视锁定问题的好例子。假设你有一个要部署到云上的应用。这很简单,但是在架构师的视角来看,却会有很多选择、很多权衡,尤其是在锁定方面。
你可能想要把你的应用部署在容器里。这听起来很棒,但是你会使用 ECS 来运行它么?这是 AWS 的专属。考虑 Kubernetes ?它是开源的而且能够在绝大多数环境上运行——其中也包括自建设施。问题解决了么?还没有——你被锁定在 Kubernetes 上了——想想那些 YAML 吧。所以这是从锁定走向锁定。如果你使用的是托管 Kubernetes 例如 GKE 和 EKS,你还可能被锁定到 Kubernetes 的特定版本和特定扩展上。
如果想要让软件运行在私有设施中,也还有 AWS Outposts 的选项,所以你还是有得选。但这还是 AWS 的专有品种。你可能已经被锁定到 VMWare,它也能和 VMWare 集成,所以这有什么不同么?Google 的 Anthos 也是同样产品,它使用开源组件构建而成,但还是专属品:你可以把应用迁移到不同的云上——前提是你继续使用 ANthos。所以这就是锁定的意思,对吧?
另外如果你把你的部署自动化和你的应用运行时漂亮的分割开来,是否意味着切换基础设施更容易了?降低锁定的风险了?嘿,甚至还有跨平台的基础设施即代码的工具呢,是不是就完全消灭这些担忧了?
至于存储方面,AWS S3 如何?其它云供应商提供了 S3 兼容的 API,所以 S3 可以视为兼容多云,没有锁定了,但 S3 的确是 AWS 的专属阿。还可以把所有数据访问藏到抽象层之后,然后适配本地环境,这样总算可以了?
看起来避免锁定不那么简单,甚至会让你迷失在逃离锁定之路上。尽管如此,我推荐 Simon Wardley 的 Take on Hybrid Cloud
锁定的阴影
电梯架构师(乘着架构师电梯上上下下的人)眼中的锁定是灰色的,而不是象有些人的眼里的非黑即白。在考虑系统设计时,他们会意识到象锁定或者耦合这种事情并不是一个非此即彼的事情。两个系统并不能简单的判断耦合与否,同样地,也无法简单的判断是否被锁定到一个产品。这种问题的内部是有一些微妙之处的。例如锁定问题可以拆分成多个维度:
供应商锁定:IT 人嘴里的锁定很多时候指的是这种情况。它描述的是难于从现有供应商切换到其竞争对手。举两个例子,如果想要从 Siebel CRM 迁移到 SalesForce CRM,或者从 IBM DB2 数据库切换到 Oracle,都会是伤筋动骨的事情,这就是锁定。供应商或多或少的会从这种锁定中受益。这种锁定中往往包含了对应的商业安排,例如长期授权和支持协议能够获得更好的折扣。
产品锁定:在从一个供应商的产品迁移到另一个供应商的产品时,供应商和产品都发生了变化,所以两者是可以合二为一的。开源产品能够避免厂商锁定,但是并无法避免产品锁定:如果你在使用 Kubernetes 或 Cassandra,就当然是被锁定到了特定产品的 API、配置和功能上了。如果在一个专业(尤其是企业)环境中工作,你可能还需要商业支持,这样就又产生了供应商锁定。深度定制、集成以及专用扩展,都是产品锁定的形态:这些做法都提高了更换产品的难度,开源产品也无法避免。
版本锁定:除了被锁定在产品上,还可能被锁定到特定版本。新版本如果破坏了现存的定制和扩展(SAP?)。有些版本更新可能还要你重做应用——比如 AngularJS 和 Angular2。还有更差劲的情况就是,版本锁定的传染:某特定的产品版本需要特定(通常是过期的)操作系统版本,或者类似的情况,这会让迁移的尝试变得困难重重。如果供应商决定弃用你的版本,或者停止整条产品线,这种锁定造成的后果就很严重:需要在失去支持和大动干戈之间作出选择。情况还可能进一步恶化:例如你的旧版本系统中发现了严重漏洞,却无法找到对应的更新。
架构锁定:还有可能被锁定到特定类型的架构之中。例如,在大量使用 Kubernetes 的过程中,你可能会构建很多的小服务,这些小服务可以以容器的形式进行部署,对外提供 API。如果想要迁移到 Serverless 架构,就要把服务的粒度向单一功能的方向进行调整,把状态管理转移到外部,实现事件驱动架构等等。这种变更往往意味着对应用架构的整体修改。
平台锁定:产品锁定的一种特例是平台锁定,常见于云平台。这种平台不仅支持应用运行,可能还掌握了你的用户账号以及相关的访问权限、安全策略、基础设施分配等方面。它们还提供了应用级别的服务,例如存储或机器学习,这通常也是专有的。远离这些服务看起来好像能够减少平台锁定,但是这种做法就否定了上云的主要动机。这就让人进退两难了。
技能锁定:在开发人员开始熟悉特定的产品或架构之后,技能锁定就产生了:要使用不同的产品和技术,就需要重新培训(或者招聘)开发人员,这都需要投入。技能的可用性是当今 IT 的一个主要约束,这种锁定也就非常实际了。有些小众的企业产品只有很少的开发者,这就直接导致了开发成本的上升。这种情况在使用定制语言,或者”只需配置“/“无需代码”的情况下尤为常见。
法务锁定:你可能会因为法务问题锁定到特定的解决方案,合规要求就是个常见情况。假设一个云供应商的数据中心在国外,你可能就无法把数据迁移到这个供应商的云上。有的软件即使是可以顺畅的在云上运行,供应商的授权可能也不允许它迁移上云。如果你坚持上云,就会违反授权条款。法务方面的限制远比我们平时所理解的要多,我们面临的选择好像:你的小飞机是由 70 年代设计的使用含铅汽油的过时引擎驱动的,然而新引擎的采用,可能产生巨大的法律风险。
智力锁定:最微不足道的也是最危险的锁定就是对思维的锁定。在和特定的供应商和架构合作之后,可能会把一些假设吸收到你的决策依据里,这可能会导致你拒绝其它方案。例如在面对横向扩展架构时,你可能因为它的扩展不够线性(两倍硬件没有产生两倍性能),得出效率低下的结论,从而拒绝这种方案。在技术层面,这种思考方式忽略了一个问题,这种方案的主旨在于扩展性,而不是效率。或者你会讨厌快速的发布周期,因为你相信频繁的变更会导致更多的缺陷。还有你可能会被告知,编码很昂贵、耗时并易错,所以最好用配置完成一切。
总的说来,锁定绝对不是简单的二元世界,理解了各种不同的锁定方式,有助于作出更加清晰的决策。这个列表也戳穿了一些常见的谬误,例如开源软件神奇的解除锁定的能力。开源软件能够防止厂商锁定,但是绝大多数其它的锁定同样存在。这当然不是说开源软件的坏话,只是说,开源软件并非治愈锁定的良药。
使用模型做好决策
有经验的架构师不会只盯住阴暗面,他们会执行优秀的决策纪律。纪律很重要,因为我们的决策能力往往比我们的自我感觉要差得多。如果这方面有疑问,建议阅读 Kahneman 的 Kahneman’s Thinking, Fast and Slow。
提高决策能力的最有效方法就是使用模型。就算是简单的模型,也能在改善决策的过程中提供很大帮助:
简单但令人回味的模型是伟大科学家的标志,过分的细化和参数化通常意味着平庸。 George Box
所以不要嘲笑深受管理咨询行业宠爱的二乘二矩阵,我们接下来会发现,这是一种非常简单有效的模型。
下一个事关模型的关键点:常识告诉我们,面对不确定性,必须尽快响应——毕竟世界一直在变。但是事与愿违:当我们必须处理很多相互依赖、高度不确定性以及小概率事件时,糟糕的决策会把事情搞得更糟。模型能帮助我们把更多的结构化和规律加入决策过程。是否接受锁定、接受什么样的锁定,都是这样的问题,所以我们要使用一些模型。
关于锁定的二乘二矩阵
一个简单的模型能够让我们克服以锁定为耻的观念。首先我们必须意识到,很难完全杜绝锁定的发生,因此一定程度的锁定在所难免。第二,如果锁定能带来与之相配的收益,那么我们也会乐见其成的,例如一个竞争对手所不具备的的独特的功能。
我们把这些因素用一个最简单的模型来表达——二乘二矩阵:
上面的矩阵使用以下的两个维度来描述我们的选择:
- 切换成本(也就是锁定):对我们来说,迁移到别的方案有多难?
- 唯一的实用价值:我们从这个解决方案中获取了什么无法被其它工具取代的好处?
我们可以看看这四个分区了:
Disposable:没有独特功能且易于更换的组件是可以不太担心的。我们可以和他们维持现状,如果遇到问题,可以轻松的进行替换。普通的东西,普通的对待就很好。例如很多开发者的 IDE(EMACS 可能是个例外)都是这样的:随意混合搭配,无需过于依赖。存储你所有照片和个人数据的云存储,很大程度上把你的手机也变成了可抛弃的,稍候还会对这个例子进行更多介绍。
Accepted Lock-in:这个区域指的是把你锁定到特定产品和供应商的组件,但是这种锁定是有回报的——得到了独特的功能。虽然我们提倡减少锁定,但是这种交换相对来说是比较容易接受的。例如使用了 Google Cloud 的 BigQuery 或者 AWS 的 Bare Metal Instance,很明显就是被锁定了,然而这个锁定是根据收益作出的决策。如果是一个小应用,使用 AWS 原生服务也是可以的,这是因为没有迁移的需要,而缩减开发和运维成本是更重要的事情。
Caution:这部分是最不受欢迎的区域了,产生了锁定,但是又没有与之想匹配的回报。传统的关系数据库就可以放到这个位置——使用商用数据库真的增加了你的收入了么?没有。然而向外迁移可能需要很大投入。如果为发射到外太空的嵌入式系统选择了特定的硬件,这也没什么问题——几乎没有迁移的机会。
Ideal:这是最佳区域——提供了独特的实用价值,但是还能够方便的切换。听起来这好像是我们的理想境界,但是你会发现这个区域的定义是矛盾的:如果一个解决方案提供了唯一的实用价值,其它竞品无法提供,那他的切换就是困难的。S3 可能就是这个类别中的一个例子:多个云供应商都接受了同样的 API,迁移出去,例如迁移到 GCP 相对来说是很方便的。每个实现都会在某些方面有一些明显优势,要保护跨平台的可移植性,很重要的一点就是:不允许 API 保留授权或者取得专利。
这个模型的确很简单,把你的软件(或者硬件)组件放到这个矩阵里面是个值得尝试的做法。这样的方法不仅为你的风险进行了可视化,还把你的决策传达给了利益相关者。
举一个日常的例子,你可能决定使用下列物品,这些物品有各自的功能,也有锁定风险(从右上角开始逆时针方向)。
你所钟爱的 iPhone 把你锁定到了供应商的生态系统中,但是也给了你独有的体验,所以你认为这是可以接受的锁定。
移动通信服务商的合约把你锁定到了单一的网络上,但是各个服务商的区别其实不大,所以把它放到 Caution 是合适的。
充电器有标准接口,不幸的是很多 iPhone 不是,但是还有各种转换装置让这个小玩意处在 Disposable 位置。
很多 App,例如 Messaging,提供了功能,例如和朋友进行联系,但是他们的的设计就是方便切换的,例如通过手机的联系人名单,所以可以放在 Ideal。
这里要注意的就是唯一实用价值:每个供应商都会提供一些唯一功能——这就是差异化。然而这里需要关注的是这些功能是否能转化为唯一价值。例如有的云供应商提供了能够服务于十亿用户的强大全球网络。这令人印象深刻,也具备唯一性,但这对普通的企业来说却没什么意义,他们可能只服务于百万用户,也仅在单一国家内提供服务。当然也有人在有限速的小国家开法拉利的,并非所有决定都是理性的,但法拉利和云平台不同,可能给出不同的实用价值。
锁定的实际成本
这个简单的矩阵太有用了,完全停不下来。前面的矩阵把切换成本作为单一元素(维度),现在可以将其拆分为两个维度:
这个矩阵把替换的成本从替换的可能性(主动或者被动)拆分开来。较低的替换可能性结合较低的替换成本应该不会令人困扰,但是相对的替换成本较高、又有较高替换概率的就值得注意了。在另外一角,虽然替换成本高企,但是发生的可能性不大——这一区域可能需要做一些保全措施,措施包括限制更改范围,或者增加运维成本。你也可以选择接受这种风险——在 Oracle 和 DB2 之间进行切换的机会并不多。最后如果切换的可能性很大,成本又不高,那就无需费神了——拥抱变化,设计系统,完成切换。但奇怪的是,尽管大量的小范围变化很容易实现,但这种场景往往不会象左上角那样得到大量关注,这就是决策过程中经常出现的错误:难于完成的戏剧化场景,往往因为一个“万一”,吸引了更多的注意力。
在我们谈论锁定的意愿时,可能需要在多个角度考虑一下切换的理由:供应商退出业务、提价、或者无力支持现有规模以及功能需求。有趣的是,减少锁定的愿望经常成为谈判的手段:在续约谈判中,你可能会提示你的供应商,在产品架构设计角度来看,从他们的产品中切换是可行的,成本也是可以接受的。这样你就给出了 BATNA(Best Alternative To a Negotiated Agreement)够低的信息。这种架构方式虽然不一定会用起来,但它会产生实际的威慑力,就如冷战期间的物资储备一样。你可能只是伪装,并不会真的去除锁定,但是这种情况下,你最好是个好玩家,以免被供应商翻了底牌——比如和你的开发人员打探消息。
减少锁定:执行价格
再回到我们起初提到的选项类比问题,如果避免锁定给了你(多个)选项,那么切换成本就是这个选项的执行价格。有价值的选项应该能降低切换成本。我们当然希望所有系统都能在绿色区域中,具备最小的切换成本,但是实际发生的投资可能并不总能降低。
例如很多架构师会反对锁定到特定的数据库或者云供应商。然而发生切换的机率如何?5% 或者更少?那么你怎么才能把切换成本从 50000(假设)美金降低到接近于 0?切换成本远远大于 2500 美金(50000 * 5%)。因此最小化转换成本并非(架构设计的)唯一目标,很容易变成过度投资。这也类似过度保险:支付巨额溢价,能把免赔偿额度降低到 0,但这通常不是最经济最合理的选择。
一个最终模型(这是唯一一次不使用矩阵方式)能够帮助你决定,在降低切换成本上投入多少才是合适的。下图的蓝线,是转换可能性和转换成本的乘积,代表了转换的负债。这张图展示了它和前期投资的关系:
进行投资肯定能减少债务,或者降低执行成本、降低切换可能性也都能降低。例如使用 ORM 框架是一个较小的投资,能够降低对数据库厂商的锁定。还可以创建一个元数据语言,能够转换成每个厂商的数据库的本地存储过程语法。这能让你在不被锁定的情况下释放数据库的所有性能,但是这就需要为一个相对比较小众的场景进行大量投资了。
红线很有意思,表达的是前期投资和潜在债务的累计。这是应该尽量降低的总体花费。在多数情况下,随着前期投资的提高,会进入一个最佳区域。针对降低锁定的额外投资实际上会导致更高的总体成本。原因也很简单:投资回报率,尤其是在切换概率较低的时候。如果我们把架构做成超级有弹性,我们可能会进入过度投资的范围。Yagni(You ain’t gonna need it)的家伙们会走向另外一端——中庸之道是快乐之源。
避免锁定的总成本
现在在锁定方面我们对锁定的成本做了一些研究,我们需要更进一步的看看避免锁定的总体成本,前面的模型,我们假设避免锁定是一个简单的成本问题。实际上这个成本能够分解为几个不同方面。
工时:这需要一些额外的工作,最终都要算成工时的。如果我们选择在 Kubernetes 上部署容器以减少云提供商锁定,就需要投入工时学习新工具,编写 Dockerfile,配置 Kubernetes 等。
成本:额外的现金成本,比如说产品授权,雇佣外部供应商或者参加 KubeCon。
利用率不足:这是一种间接成本。为了不被锁定,经常会避免使用供应商特有的功能。这样就造成了对既有软件的使用不足。这样就意味着,要么投入工时补足缺失的部分,要么任由产品存在短板。
复杂性:复杂性是个经常被忽视的核心元素。很多减少锁定的方法就是引入新的抽象层:JDBC、容器、通用 API。所有的有用的工具,都会增加系统的总体复杂性,也就增加了新成员的学习成本,以及系统错误的机率。
新的锁定:避免一种锁定,往往会引起新的锁定。例如为了避免 AWS CloudFormation,取而代之的是 Terraform 或者 Pulumi,它们都支持多个云供应商。然而现在你就被锁定在额外供应商的其它产品上了,还是要鉴别一下这是否是你想要的。
架构师要计算减少锁定的成本,应该对这个列表做一个检查,看是不是存在什么盲点。同样地,避免锁定的尝试可能会是有泄漏的,例如 Terrorform 是个好工具,但是它的脚本使用了很多供应商特定的构造。实现细节的泄漏,就会提高云间切换的成本。
整合视角
有了这么多的理论铺垫,我们看看一些贴地气的例子。
部署容器
一个公司会把他们的代码打包为 Docker 容器,部署在 AWS ECS 上,所以它们锁定在了 AWS 上。应该引入开源的 Kubernetes 来避免锁定么?速度是它们的主要问题,当前的 ECS 解决方案表现很好,我认为迁移可能难有回报。切换云供应商的概率很低,它们有更重要的事情可以做。
建议:接受锁定。
关系型数据库访问
很多应用程序会使用关系型数据库,有很多厂商和开源产品。然而 SQL 的方言、存储过程以及定制的管理控制台都是锁定的。你要投资多少来避免锁定呢?多数语言和运行时通用框架(例如 Hibernetes)都以低成本提供了某种程度的数据库中立。如果希望降低执行价格,还应该避免使用 SQL 函数以及存储过程,但这会降低产品性能或提高硬件水平。
建议:使用低成本机制来降低锁定程度。不要想着零成本切换。
迁移上云
除了把数据库从一个供应商切换到另一个,你可能更感兴趣的是把应用和数据库迁移到云上。除了技术考量之外,你需要考虑一下有些供应商的授权协议可能会让这种迁移很不划算。这种情况下,选择一个开源数据库可能是个更好的办法。
建议:如果能够满足你的需要,那么选择一个开源的数据库,可能需要接受某种程度的锁定。
多云
很多企业痴迷于可移植到多云的想法,病体除了更复杂、更精密也更昂贵的计划,这些计划表面上可以让它们免于被云供应商锁定。然而大多数这些尝试,都在否定上云的初衷:低阻力以及使用托管服务(例如存储和数据库)的能力。
建议:谨慎从事。参考我在多云方面的文章。
用思维的速度做架构
似乎人们可以投入大量时间去关心锁定的问题。有些人甚至会因为我们的方法太过“学术性”而不予理会,学术性这个词我通常都无法认为是个贬义词,这是我们受教育的地方。不过老式的非此即彼的架构方法,是不是更简单,可能还更高效呢?
事实上,思维的速度很快。只需要几分钟就能够读完这篇文章里提到的所有模型,并作出良好的决策——无需任何花俏的工具,只需要纸或白板。快速完成架构思考的关键是集中注意力。而要做出精致的幻灯片,需要提前几周进行安排,通常也不会有具备实际专业知识的人参与并作出决策。
鸣谢
感谢作出有益反馈并提供输入的几位朋友:
Scott Davis