你好!我想谈谈为什么大型科技公司热衷于为其基础设施创建专有解决方案。答案似乎很明显:这只不过是 NIH 综合症。但这个答案还远远不够完整,更谈不上客观了。
我是Yandex平台工程团队的CTO,我们的目标是协助工程师构建从代码编写到运营服务的整个开发周期,使其更加高效。这包括流程简化:我们不仅开发即服务产品,还协助在公司内实施它们。这适用于 Yandex 的规模:我们的服务被公司内数千名开发人员使用。
为了解决问题,我们经常开发专有工具而不是实施现成的工具。例如,当我还是团队中的程序员时,我用 C++ 和 Python 开发了一个定量监控系统,并帮助将其扩展到数百亿个处理指标。因此,根据我自己的经验,我知道是什么动机和发展路径导致了内部工具的出现:下面,我将尝试以我们的解决方案为例来找出它们创建的根本原因。
设定任务。我们内部运行时云(RTC)的目标是为内部用户提供简单的部署和流量管理工具。 RTC 用户是开发 Yandex 服务的工程师。他们需要启动他们在某处创建的数以万计的应用程序,向那里发送用户请求,平衡负载并处理事件等等。
对内部云的需求出现在 2010 年代初,当时服务数量已达数百个,分配的核心总数每年以数十% 的速度增长。为每项服务配备专用服务器变得非常昂贵,我们需要能够在单个服务器上运行来自多个服务的应用程序的工具。我们一开始对这些工具有几个要求:
本质上,我们需要 Kubernetes(随着时间的推移,RTC 已经非常接近了)。但问题是:k8s 是在 2014 年才发布的。Apache Mesos 当时就已经存在,但还处于起步阶段。
基本功能的实现。我们开始使用一种 MVP 来解决问题 - 一组简单的工具,它更像是一组自动执行日常操作的构建块,例如:
随着时间的推移,使用这些构建块构建完整的服务布局图成为可能(类似于持续交付)。经过一定次数的迭代,2013年出现了Nanny,一个管理RTC上运行的服务的系统。
Nanny 的另一个基本方面是基于资源消耗实现应用程序隔离。我们最初在没有资源隔离的情况下从各种服务启动应用程序,这导致了大量的操作问题和事件。
当时唯一现成的解决方案是当时已经停止开发的 LXC,以及无法仅使用 IPv6 并在更新 dockerd 时重新启动所有容器的 Docker,这使得无法在不影响用户的情况下更新 dockerd。结果,我们开始开发我们的
解决利用问题。当时,内部云中的资源管理是通过提交到存储库来完成的。然而,这减缓了 Yandex 的发展,并且与提高利用率的任务相冲突:为了解决这个问题,我们需要将我们的 MapReduce 系统放置在云端,即
为了将 YTsaurus 引入 RTC,需要能够动态管理 pod,而不是通过存储库提交。因此,我们在2018年创建了
新的成长烦恼。同一时期,k8s 发展成为更加成熟的解决方案,并于 2017 年成为 AWS 服务之一。但它仍然没有满足我们的所有要求:
YTsaurus 积极使用创建嵌套 Porto 容器的功能,而不是创建单个调度程序。当然,我们可以自己在 k8s 中添加对相同双栈的支持。然而,Linux 内核开发经验表明,并非所有内容都可以发送到开源,我们努力将与上游内核的增量保持在最低限度,以简化新版本的更新。
我们今天的解决方案。 RTC的架构与Kubernetes非常相似。用户以某种规范的形式声明性地描述他们的服务,该规范描述了如何启动指定的应用程序以及在哪些数据中心中启动。每个数据中心都有自己的 Yandex Planner 安装,它一方面充当所有集群对象的数据库,另一方面充当 Pod 调度程序。数据中心中的每台服务器都运行一个代理,该代理从 Yandex Planner 接收 Pod 规范,并使用我们专有的 Porto 容器化系统启动它们。
目前,RTC已上线数万个服务,为超过10万台服务器分配超过500万个核心。每天,服务规范都会发生超过 100,000 次变更。
计划。如果 k8s 可以处理我们的规模怎么办?特别是自从 k8s 生态系统在某些时候开始在功能方面超过我们之后。切换到 k8s 并希望现成的工具最终能够提供我们需要的数量不是更好吗?实际上,我们仍然是 k8s 的小众消费者,因为只有一小部分公司运营规模如此之大,而且每家公司都有自己的内部云解决方案。
另一个需要记住的关键点是移民问题。根据2018年7月
2021年,我们在选择发展策略时,预估了从一种部署系统迁移到另一种部署系统需要花费多少成本。 Yandex 迁移到普通 k8s 将是一项极其昂贵的任务,需要数百人年的时间。
通过这种简单的方式,我们最终得到了我们内心的云,即使我们设定了这样的目标,我们在未来五年内也不太可能放弃它。
与 k8s 相比,缺乏内部云功能该怎么办?实际上,我们的客户可以在 Yandex Cloud 中使用托管 Kubernetes。此选项主要用于必须满足严格合规性要求的项目 - 这是一小部分团队,不到 1%。由于上述原因,其他人认为搬家并没有多大好处。
同时,我们也在积极研究k8s,考虑如何更加接近普遍接受的标准。我们已经在一些任务中积极试验 k8s,例如云引导或在整个 Yandex 规模上组织 IaaC。理想情况下,我们希望重用 k8s 接口,同时维护我们自己的实现,尽可能根据我们的需求定制。剩下的就是弄清楚如何在实践中做到这一点。
问题及解决方案要求。我们的单一存储库 Arcadia 与我们的内部云有着相同的主要目标:提供便捷的开发工具。对于存储库来说,这包括整个开发生态系统:
Arcadia 大约与 Yandex 的内部云同时出现。创建单一存储库的原因之一是需要在 Yandex 中重用代码。当时,这受到了多个构建系统的阻碍。需要一个支持高效分布式构建的统一系统才能在整个 Yandex 规模上工作。它还应该稳定且可用。
实施统一的构建系统。我们专有的 ya make 构建系统于 2013 年首次亮相,当时它仅适用于 C++ 代码。在 ya make 之前,我们使用了 CMake,但它的速度使其无法扩展到单一存储库的规模。专有的 ya make 与 Arcadia 一起工作得更快。没有其他开源选项可以解决我们的问题:例如,Bazel 发布得晚得多,在 2015 年。
版本控制系统缩放。 Yandex 之前使用 SVN 作为其版本控制系统。 SVN虽然容量很大,但仍然有限且难以维护。此外,我们意识到我们最终会遇到 SVN 功能和便利性的限制。例如,启发式方法用于实现仅下载存储库所需部分或选择性签出的能力。于是,2016年,我们开始尝试SVN之外的其他版本控制系统。
Mercurial 是第一选择。但我们遇到的主要问题是速度。我们花了一年半的时间尝试将 Mercurial 投入生产,但结果令人失望。例如,我们最终不得不重写 Mercurial 的部分内容以支持 FUSE,或者我们必须将整个存储库带到每个开发人员的笔记本电脑上。
最终,事实证明从头开始编写内部解决方案更便宜,2019 年,Arc 出现了 - 一个为 Arcadia 用户提供的新版本控制系统,具有类似 git 的 UX。 Arc 的基础是 FUSE(用户空间中的文件系统)而不是选择性签出。此外,YDB 作为一个可扩展的数据库,与 Mercurial 相比,大大简化了 Arc 的操作。
经常有人问我们为什么不使用 git。因为它也有规模和功能限制:如果我们只将 Arcadia 主干导入 git,那么在这种规模下 git status 将需要几分钟的时间。与此同时,没有建立在 git 之上的稳定的 FUSE 实现:VFS for Git 不再被开发,EdenFS 最终变成了 Sapling,但这发生得晚得多。
解决方案的当前状态和未来计划。要开始开发,内部用户只需在我们的单一存储库中创建一个文件夹,编写代码,然后告诉您如何通过添加构建清单来构建他的应用程序。因此,用户会收到拉取请求、CI 配置以及重用公司中任何代码的能力。
从规模来看,trunk目前包含1000万个文件,整个存储库超过2 TiB,每周提交超过3万次。
因此,在我们创建的生态系统中,我们必须从头开始创建许多组件。然而,它现在正朝着符合全球标准的方向发展。例如,Arc 支持使用 Git 来处理一组预定义的项目。
那么,为什么大型科技公司必须发明自己的解决方案,并且为什么不能用符合普遍接受标准的解决方案来取代它们呢?
创新。大公司经常被要求开发解决方案来解决未来只会变得司空见惯的问题。这就是有可能成为市场标准的创新解决方案的出现方式。
公司解决的问题并不总是面临公司本身以外的任何人。有时,大型科技公司在解决特定问题上的经验可以帮助整个行业通过采取完全不同的发展道路来避免该问题。预测市场发展并不总是可能的,因此,专有解决方案的不同示例会产生截然不同的结果。
ClickHouse 是一个真正成功的创新项目的例子,它极大地丰富了在线分析处理(OLAP)领域。然而,并非所有项目都是如此。 Porto 最初是一个开源项目,但由于多种原因未能获得关注。尽管它的一些功能(例如创建嵌套容器的能力)仍然是独一无二的。
规模。这一点在某些方面与上一点类似,因为并不是每个公司都面临可扩展性问题。曾经有一段时间,640 KB 对于每个人来说都绰绰有余,不是吗?
事实上,系统负载的指数级增长是Arcadia和内部云发展的最重要原因之一。这就是开发 Arc 和 Yandex Planner 的原因。 Arc 的创建是为了满足对用户友好的 VCS 的需求,该 VCS 可以让用户轻松地使用主干中包含数千万个文件的单一存储库。 Yandex Planner 的创建是为了满足与数万个节点和数百万个 Pod 的集群有效工作的需求。
公共工具仍然存在扩展问题(毕竟,这是一种相对罕见的情况,而且投资它通常根本无利可图)。
惯性。考虑一个解决公司内部问题的内部工具。积极使用该工具的公司将投入资源来更好地根据其需求对其进行定制,最终将其转变为高度专业化的工具。这个过程可以持续数年。
现在考虑这样一种可能性:在某个时候,会出现解决该特定问题的普遍接受的标准。在这种情况下,专业化可能仍然是决定内部解决方案的重要因素。考虑构建系统。我们在 Arcadia 使用 ya make,尽管有来自 Google 的 Bazel。它们在概念上相似,但当您深入了解细节时,会发现许多重要场景的实现方式有所不同,因为每个工作负载的负载模式可能截然不同。因此,几乎肯定需要对已经消耗的资源进行再投资,以定制新的普遍接受的标准。
迁徙。如果上一节解决了使项目适应用户的问题,那么现在让我们解决迁移用户本身的问题。在我看来,迁移应该被称为科技领域继命名之后的下一个最重要的问题。如果我们假设我们已经拥有一种公司内部工具并希望将其替换为标准化工具,那么我们将不可避免地需要迁移。
根据我们开发内部云的经验,我们知道许多迁移的例子。大规模迁移需要时间,因此必须在较长时间内同时支持这两种工具。如果这个过程涉及大量用户,管理问题是不可避免的。在没有用户参与的情况下尝试迁移当然是值得的,但这并不总是可行的。
业务连续性。坦率地说,这一点最近已经变得足够重要了。此前,由于担心供应商锁定,只有少数公司认真对待这一问题。将关键流程委托给可以随时终止协作的供应商是有风险的。 JetBrains 就是一个典型的例子,它限制某些公司使用其 IDE。另一个典型的例子是 Github Enterprise,它已开始暂停俄罗斯用户帐户。
内部解决方案通常不受此问题的影响。一方面,仍然有开源解决方案。另一方面,也不能保证开源模型会一直伴随着你:例如,Facebook 内部开发的对 Hadoop MapReduce 调度软件的改进 Corona,由于无法提交而首先出现。向上游扩展 Hadoop 所需的补丁。
同时,该问题的法律方面也会影响开源:例如,在 golang 或 k8s 中提交需要签署 CLA。这会继续成为一个问题吗?
NIH。是的,除了客观原因之外,做出的决定也有可能是不务实的。这就是 NIH 综合症的最佳表现。
例如,为了消除批处理对计算的影响,我们尝试在 Linux 内核中创建自己的调度程序。实际上,它并没有带来什么好处。人们可以利用 Linux 内核的现有功能。然而,劳动力成本越高,就越需要花更多的精力去阐述和解决问题,患上NIH综合症的可能性就越低。
总而言之,正如您所看到的,大公司经常需要内部解决方案。其中大部分将在未来与尚未成熟的全球统一标准解决方案融合,而其余的将成为历史。无论如何,在专有解决方案和现成解决方案之间做出选择仍然是一个难题,如果不先了解背景并估算此类项目的成本,就无法回答这个问题。