背景
Kubernetes已成为大中型企业调度和管理服务的实际方式。事实证明,Kubernetes 与微服务设计模式相结合,是管理从网站到数据处理管道等各种服务的有用工具。但整个生态系统都认为 Kubernetes 存在成本问题,不幸的是,(Kubernetes作为)削减成本的主要方法,其本身就是一种负担。
问题不在于 Kubernetes,在于我们构建应用程序的方式。
为什么 Kubernetes 成本如此之高
笔者自 Kubernetes 社区成立初期就参与其中,Kubernetes 的早期优势之一就是节省成本。我们认为,正在构建的这个系统可以通过减少虚拟机数量来帮助管理成本,而这一假设绝对是正确的……
但长远来看,“使用 Kubernetes 节约成本”这一观点可能是错误的。实际上,目前业界普遍认为 Kubernetes 的运行成本非常昂贵。
哪里出了差错?
简而言之,不,没有任何问题。事实上,Kubernetes 看待世界的方式非常成功,它改变了我们部署和维护应用程序的思维方式,并且 Kubernetes 本身比早期更加成熟。同样,微服务设计模式得到了广泛部署,以至于大多数时候我们都不会意识到我们现在默认编写的是小型服务,而非容器出现前流行的单体超级应用程序。
可以说,节省成本始终是 Kubernetes 的“附加利益”,而不是设计目标。如果这种说法已经过时,那并不是因为 Kubernetes 放弃了这一目标。而是因为随着 Kubernetes 背后的整个模型的发展和成熟,事实证明其节约成本并不属实。
在本文,我们可以讨论Kubernetes 从“更加便宜”到“非常昂贵”的原因。
过去认为,Kubernetes 比使用每个微服务都在自己的虚拟机上运行的系统更便宜。考虑到当时的经济状况,确实如此。但这种比较不再有价值,因为Kubernetes有系统化的平台,进而改变了云计算的经济性。
容器可靠性的成本
早期,Kubernetes 引入了“副本控制器(Replication Controller)”的概念,后来成为部署(Deployments)和副本集(ReplicaSets)。所有这些抽象设计都是为了解决容器和虚拟机的一项缺点:容器和虚拟机启动缓慢。这使得它们在故障转移场景(当节点死亡时)或扩展事件(当流量激增到当前实例无法处理负载时)期间很难发挥作用。
曾几何时,在 Kubernetes 出现之前,人们是这样解决问题的:通过预先配置服务器或虚拟机,然后在生产中轮换使用这些服务器或虚拟机。Kubernetes 的复制让我们可以轻松提出“我需要三个实例”或“我需要五个实例”,并且 Kubernetes 控制平面可以自动管理所有这些实例——保持它们健康、从故障中恢复并优雅地处理部署。
但这是 Kubernetes 开始变得昂贵的原因。为了处理扩展和故障,Kubernetes 运行了一个应用程序的N 个实例,其中N往往至少是 3 个,但通常是 5 个或更多。这种复制的关键之一是应用程序应该分布在多个集群节点上。这是因为:
如果节点死亡、重新启动或停止响应,该节点上调度的任何容器都将不可用。因此, Kubernetes 将流量路由到其他节点上的容器实例。
随着流量增加,负载(在某种程度上)平均分配给所有正在运行的实例。因此,当其他容器闲置时,不会有一个容器在负载下崩溃。
这意味着,在任何时候,你都要为运行三个、五个或更多应用程序实例付费。即使负载确实很低且故障频率少,您也需要为流量突然激增或意外中断做好准备。这意味着始终保持闲置产能在线,也就是所谓的“超额配置”。
Fermyon
Kubernetes 推出不久后,自动扩展器的出现改善了这一状况。自动扩展器会监测网络或 CPU 使用率的增加,并自动增加容量。水平 Pod Autoscaler(最流行的 Kubernetes 自动扩展器)只需在负载增加时启动更多应用的副本。
然而,由于容器启动缓慢(需要几秒或几分钟)并且负载本质上是不可预测的,因此自动缩放器必须相对较早地触发并相对较晚地终止过剩容量。它们可以节省成本吗?通常是的。但它们是万能解药吗?答案是否定的。
如上图所示,即使自动缩放器预计流量会增加,但启动时和负载下降后也会发生浪费。
Sidecar (边车模式)是资源消耗者
副本并非 Kubernetes 昂贵的唯一原因,Sidecar 模式也不可忽视。一个 Pod 可能运行多个容器。通常其中一个是主应用程序,其他容器辅助 sidecar。一个微服务可能有单独的 sidecar,用于数据服务、指标收集、扩展等。每个 sidecar 都需要自己的内存池、CPU、存储等。
再说一次,我们并不是认为这是一件坏事。这种配置展示了 Kubernetes 的强大。整个操作包络(operational envelope)可以以 sidecar 的形式包裹在应用程序周围。但值得注意的是,现在一个微服务可能有四到五个 sidecar,这意味着当你运行 5 个副本时,你现在正在运行大约 25 或 30 个容器。
这导致平台工程师不仅需要扩展集群(添加更多节点),还需要增强现有节点的内存和 CPU 容量。
“成本控制”不应该只是附加功能
当云技术刚刚站稳脚跟时,世界经济正在从 2007 年的衰退中复苏。到 2015 年Kubernetes 出现时,科技正处于繁荣时期。直到 2022 年底,经济压力才真正开始压低云计算成本。在成本优化并非最优先考虑因素的情况下,云技术成熟起来。
到 2022 年,我们当前的云设计模式已经固化。我们接受了“昂贵”,转而支持“稳健”和“容错”。随后,经济下滑,是时候调整我们的云计算支出成本了。
不出所料,围绕以上问题,一个行业逐渐发展起来。Kubernetes 至少有十几种成本优化工具,这些工具主要通过以下方式控制成本:(a) 合理调整集群规模,以及 (b) 尽可能购买廉价的计算资源。
将这一过程类比为控制汽车汽油消耗可能比较恰当。为了控制成本,我们可能(a)在知道现在不需要加满油的情况下,只把油箱加满一半;(b)只要看到加油站把价格降得足够低,就购买更便宜的汽油。
并不是说,这对于今天拥有汽车的我们来说,是一个糟糕的策略。如果云计算在经济压力更大的时期发展起来,Kubernetes 可能会将这些功能内置到控制平面的核心中,就像今天的汽油车比汽油价格较低时制造的汽车更省油一样。
不过,延伸一下我们的比喻,也许最好的解决方案就是从汽油发动机换成电动汽车。在 Kubernetes 案例中,这表现为从完全基于容器的运行时切换到使用其他东西。
容器运行成本高昂
我们在容器上构建了太多基础设施,而容器本身的运行成本很高。造成这种情况的因素有以下三个:
容器启动缓慢:容器需要几秒钟甚至一分钟才能完全上线,其中一些是低级容器运行时开销,有些只是启动和初始化长期运行的服务器的成本。但这太慢了,系统无法对扩展需求做出反应。系统一定要积极主动,也就是说,它必须根据预期负载进行扩展,而不是在负载出现时进行扩展。
容器持续消耗资源(即使在没有负载的情况下):由于容器启动缓慢,Kubernetes 采用的微服务架构版本建议,每个容器都拥有一个长期运行(数小时、数天甚至数月)的软件服务器(又称守护进程),该服务器可持续运行并处理多个并发请求。因此,即使不处理负载,长期运行的服务器也一直在消耗资源。
作为一种格式,容器比它们包含的应用程序更笨重。:从某种意义上说,笨重是仁者见仁智者见智的。当然,数千兆字节的虚拟机映像相比,容器是小了点。但是,如果将 2 MB 的微服务打包到 25 MB 的基本映像中,那么该映像在移动、启动和运行时就会产生更多开销。
如果我们能够减少或消除这三个问题,我们就可以大幅降低Kubernetes 的运行成本。我们可以达到任何成本控制方案相加都无法达到的效率水平。
Serverless 和 WebAssembly 提供了答案
这就是无服务器计算概念的由来。当我谈论无服务器计算时,我的意思是没有软件服务器(没有守护进程)会一直运行。相反,无服务器应用程序会在收到请求时启动,并在处理请求后立即关闭。有时,这样的系统称为事件驱动处理,因为事件(如 HTTP 请求)会启动一个进程,该进程的唯一工作就是处理该事件。
以(近似)这种方式运行的现有系统有:AWS Lambda、OpenWhisk、Azure Functions 和 Google Cloud Functions。这些系统各有优缺点,大多数都无法在 Kubernetes 内部运行。让我们看一下无服务器系统需要什么才能运行良好并具有成本效益。
当集群处理应用程序的单个请求时,生命周期如下所示:
应用程序的实例启动并给出请求。
该实例将运行直到返回响应。
实例关闭并释放资源。
无服务器应用程序不会长时间运行。一个应用程序也不会为每个实例处理多个请求。如果传入 4,321 个并发请求,则会生成 4,321 个应用程序实例,以便每个实例只能处理一个请求。任何进程的运行时间不应超过几分钟(最好少于半秒)。
三个特征变得非常重要:
启动速度必须是超音速的!应用程序必须在几毫秒或更短的时间内启动。
资源消耗必须最小化。应用程序必须节约使用内存、CPU,甚至 GPU,在最短的时间内锁定资源。
二进制格式必须尽可能小。理想情况下,二进制文件仅包含应用程序代码及其直接需要访问的文件。
然而,理想的无服务器平台必须具备的三点对于容器来说却是弱点,我们需要与一种与容器不同的格式。
WebAssembly 提供了这种配置文件。让我们来看个现有例子,Spin是一款开源工具,用于以无服务器(或事件驱动)风格创建和运行 WebAssembly 应用程序。它的冷启动时间不到一毫秒(相比之下,启动一个容器需要几十秒甚至更长的时间),它使用的系统资源极少,而且通常可以非常有效地对这些资源进行分时访问。
例如,只有在处理请求时,Spin 才会消耗 CPU、GPU 和内存。然后,这些资源会被立即释放出来,供另一个应用程序使用。WebAssembly 的二进制格式非常简洁,在 WebAssembly 中,2 MB 的应用程序大约为 2 MB。不像容器那样增加大量开销。
因此,我们可以使用一种称为“低配置(underprovisioning)”的技术,即为每个节点分配的资源少于同时满负荷运行所有应用程序所需的资源。这是可行的,因为我们知道所有应用程序都不会满负荷运行。
从这里,我们开始看到无服务器设计本身是如何提高成本效益的。
Fermyon
计算能力随需求同步扩展,因为每个无服务器应用程序都会及时调用来处理请求,然后立即关闭。使用 Spin 和 WebAssembly 等真正的无服务器技术,我们可以在 Kubernetes 集群内部自动优化资源分配,从而节省大量成本。
实现这种状态需要做一些工作。我们必须编写无服务器函数,每个函数处理一个微服务的工作,而不是编写长期运行的守护进程。一个无服务器应用程序(如 Spin 应用程序)可以实现多个函数,每个函数都是一个 WebAssembly 二进制文件。也就是说,我们的服务实际上可能比微服务架构通常产生的服务还要小。但这样一来,它们的运行成本更低,维护起来也更容易!
使用这种模式是将集群效率最大化、成本最小化的最快途径。
使用 Kubernetes 节省开支
有些云工作负载并不适合无服务器。通常情况下,数据库在容器中运行效果更好。在数据可以缓存在内存中的长期运行进程中,数据库的运行效率更高。为每个请求启动和停止数据库可能会导致严重的性能损失。Redis(发布/订阅队列和键/值存储)等服务在长期运行的进程中也得到更好的管理。
但 Web 应用程序、数据处理管道、REST 服务、聊天机器人、网站、CMS 系统,甚至人工智能推理,在创建和运行无服务器应用程序时成本更低。因此,从长远来看,在 Kubernetes 中使用无服务器应用将为您节省大量资金。
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721