为什么你家的 K8s 应用平台不好用?
围绕 K8s 构建应用平台的常见问题
问题一,用户诉求与 Kubernetes API 之间的鸿沟
大家都知道 Kubernetes 这个项目的定位是 “Platform for Platforms”,它提供了一系列统一的概念和扩展方式,屏蔽了底层存储、网络、计算等基础设施的实现细节,使得平台构建者基于 K8s 可以快速构建出一个场景化能力/平台出来。但是这往往也使得这些平台的构建者陷入了误区,直接把 K8s 的概念暴露给了业务用户,还美其名曰“原生 K8s 体验”,这带来的最直接的问题就是用户学不会,不愿意迁移到这些“先进”的云原生平台上来。
图 1 业务和 K8s 的概念差异
事实上这就是平台构建者们需要解决的问题。大家可以看下图 1,业务用户关心的应用管理概念里面,在研发层面是代码、镜像、应用、CI/CD 流程等,而 K8s 的对应概念是 Pod、Deployment、CRD 等等;在运维层面是弹性策略、灰度发布策略等,而 K8s 对应的概念是 Ingress, MetricsPodAutoScaler 等等;在用户操作方式层面是 GUI、CLI 等,而 K8s 就是写 YAML 文件。毫无疑问这里面存在大量差异导致产生了一个很难逾越的概念鸿沟,研发人员需要花大量时间学习业务无关的知识,这更加使得业务压力雪上加霜。
所以做的好的平台针对这一问题,通常会在 K8s 的基础上包一层,针对业务场景提供“业务能够理解的”概念和服务。但是这样做,就往往会出现另外两方面的问题。
问题二,针对不同场景构建的应用平台形成大量“谷仓”
针对大一点的公司会出现“谷仓”问题,因为大公司包含众多的业务线,很容易形成不同场景的业务平台,虽然大家都是基于 K8s 构建的,但是在上面形成了风格迥异的概念和接口。如图 2 所示,这些不同的平台就像一个个“谷仓”,用户体验和技术问题依然存在。
图 2 不同场景应用平台形成谷仓问题
用户体验上,在使用单个平台的情况下还好,但是实际用户需求往往并不单一。比如一个业务用户在“无状态应用平台 - A”这边创建了一个微服务,然后又不得不去“有状态应用平台 - B”创建一个中间件,使得用户在对接不同场景的应用时缺乏统一性,更谈不上横向交互能力了。
在技术上,不同场景的平台都基于 K8s 构建了独立的能力模块,往往容易出现重复造轮子的现象,每个独立的能力都缺少横向复用和可移植性,导致碎片化比较严重,技术上也很难积累沉淀,本质上就造成人力资源的浪费。
问题三,直接基于 K8s 构建 PaaS 跟不上生态发展
针对小一点的公司,虽然没有“谷仓”问题,但是构建一个 PaaS 去盖住 K8s 的复杂概念并不是“银弹”。大家可以想象,为了把 K8s 的复杂概念转换成用户能理解的业务概念,常规的做法就是做一层平台专属的 API,通过这层 API 做转换,如图所示。
图 3 构建 PaaS 盖住 K8s 问题
这层 API 在最初的时候没有问题,但是随着用户需求的增长,以及 K8s 生态的快速发展,这层 API 就会成为瓶颈,挡住平台用户使用繁荣的生态能力。这个问题在这两年已经成为了共识,导致平台构建者疲于奔命地努力封装 API, 到头来发现用户已经学会了社区 K8s 上某个新功能的玩法,还不如直接把 K8s API 暴露出去。然后大量的平台又退化到直接暴露 K8s API 这一层次上,这又回到了第一个问题上面。
构建好用的“K8s 应用平台”核心问题在哪?
在研究观察了大量开源和商业化的应用管理产品以后,我们得出了一些有意思的结论。
结论一:抽象可以有效降低平台的学习和使用门槛
图 4 不同项目或产品的抽象程度
如图 4 所示,以业内比较典型的Kubernetes、Knative、Racher Rio 以及Heroku 这四个平台为例。
其中,Kubernetes 的定位是 Platform for Platform,它只针对基础设施做封装来赋能平台团队,不面向最终用户做抽象。所以业界共识和最佳实践,都是需要基于 Kubernetes 做二次开发,至少也得用一个图形界面来进行二次封装,才能给用户使用。在这里,我们把 Kubernetes 作为抽象程度最底的基准。
Knative,只面向 Serverless 场景(函数模式,按流量扩容计费,无状态,事件驱动)设计。它通过自己的 CRD 在 Kubernetes 的基础上做了一层模型限制,把入口紧紧限制在 Service/Route/Configuration/Event 这几个 Serverless 独有的概念上,从而在 Kubernetes 之上提供了 Serverless 的使用体验,相比于 Kubernetes 已经大大降低了用户的使用心智,但 Knative 的定位依然不是面向最终用户的,而是为了构建 Cloud Run 这样面向用户的产品而设计的。
RIO 是一个基于 Kubernetes 实现的 PaaS,它提供了一个 CLI,围绕应用生命周期管理将很多底层 K8s 的功能包装成了简单的 CLI 命令,如 rio run 代表应用运行,rio route 增加一条访问路由,这个使用体验的心智负担比 Knative 要低很多,基本上已经回归到了业务研发人员能够理解的层面上。
Heroku 是业内最知名的 PaaS,它并非构建在 Kubernetes 之上,但它早在十几年前就开始推崇“以应用为中心”的理念,易用性相较于 RIO 更进一步。其抽象也已经是大家熟知的应用(heroku apps),域名(heroku domains),发布,工作流等用户级的概念,使用起来基本不需要针对这些概念做额外学习,在整个业界的反馈中,Heroku 的使用体验也是标杆级的。
结论二:抽象程度越高,平台的扩展性和被平台团队的认可度显著降低
图 5 抽象程度越高扩展性却越差
如图 5 所示,刚刚我们看到抽象程度越高越易用的四个平台,在扩展性上也呈现出了显著的差异。
同样,依然拿 Kubernetes 作为基准,它不提供用户层抽象,天然就是一个面向平台团队的工具定位,通过 CRD + Controller 的方式来进行扩展。
Knative 虽然也在让用户操作 K8s,但是因为其模型限制在了少数几个概念对象上,所以大家虽然可以在背后实现 Controller 做扩展,但是扩展的字段却只能局限在原先 Knative 的模型对象上,只能通过在对象上加 annotation(类似于打标签加备注)的方式来 workaround。所以说,Knative 作为 Serverless 场景下的平台构建工具尚具备一定的可扩展性,但要服务更广泛的场景,几乎不可能。
RIO 的定位是基于 K8s 的 “Micro PaaS”,它在扩展性上没有给出方案,也就是说除了通过修改代码添加功能之外,我们找不到其他扩展方式。
Heroku 作为一个经典 PaaS,自己定义了一系列的规范来提供扩展性,同时开创了自己的插件生态。但这个生态相比于 Kubernetes 的今天的生态来说,就完全“不值一提”了。可以说,Heroku 的可扩展性问题,是它最终从主流平台淡出的一个重要原因,感兴趣的朋友可以阅读笔者之前的文章《Heroku 的得与失》。
总体来看 ,抽象程度越高,最终用户的使用体验就越好,但平台的可扩展性就越差,就越不可能在平台团队中得到采纳,这就解释了今天 Kubernetes 的被采纳度,远远高于 Heroku。
抽象和应用本身的标准化模型缺一不可
那么是不是把 K8s 的对象保留,通过构建模板的方式把抽象做出来,然后用户只需要填模板需要渲染的值,就好用了呢? 答案并非如此,Helm 就是这种模式的典型代表。 虽然 helm 也受到了广泛的欢迎,但是它的问题也客观存在:
状态回流:helm 把对象下发下去以后,并没有统一的状态回流机制。举例来说,你把 Deployment 和 Service 组装成一个 helm 应用安装下去后,那么 Deployment 和 Service 是否创建成功了,有没有出错这些状态,helm 是无法直接查看这些细节的,你又得下沉到 K8s 这些概念里去。尤其是当这些对象不是 K8s 内置对象的时候,这个问题就更为明显了。
注册、发现、冲突管理:除了围绕扩展的能力如何使用,能力的注册与发现同样重要。如何让用户自助式的注册以及发现平台层的能力,以及这些能力哪些是互补、哪些是冲突的,也是应用管理需要解决的问题。而 helm 的定位本质上是一个打包部署的工具,并没有考虑这方面的问题。
发布与版本化:虽然 helm 也有版本的概念,也有 upgrade 命令,但是熟悉 helm 的朋友一定知道 helm 的升级其实是违背发布原则的,因为它完全没有任何的灰度,而是直接粗暴的使用新版本替代老版本。一个生产级别的应用管理平台一定是有着丰富的发布策略保证稳定可靠的平台。
通用能力建设:除了发布以外,其实还有许多能力是通用的,比如弹性扩缩、访问路由、日志监控等等,这些通用能力围绕 helm 模式也很难建设。
答案
KubeCon NA 正式发布的 KubeVela项目(github 地址:http://kubevela.io/) 正是基于上述这些问题的探索得到的答案,一个简单易用且高度可扩展的应用管理平台与核心引擎。
版权声明: 本文为 InfoQ 作者【孙健波】的原创文章。
原文链接:【http://xie.infoq.cn/article/7e4b9bf40e8f2bb3f29f09ee5】。文章转载请联系作者。
评论