ubernetes在华为云的历程
华为云最初的PaaS项目是基于CloudFoundy ,2014年的时候开始切换到kubernetes平台,不过当时的重点是在私有云方面,2016年华为公有云正式发布了容器引擎平台。2018年年初发布了Kubernetes容器实例服务,也就是Serverless container,不过它与业界其他的类似服务有些区别。
众所周知容器有三大好处,首先它提供资源隔离并且能够提升利用率;其次不用加载重型虚拟化,有秒级弹性;同时得益于容器镜像技术,解决了环境一致性问题,简化了交付流程。
Kubernetes的使用形态
在私有云自建是kubernetes的一种常见使用形态。开发者通常很享受DIY过程中的乐趣和成就感。并且全套的私有化下,数据的请求和处理都在本地,不会有隐私上的顾虑。资源规划、安装部署、升级都由自己亲自主导,有更清晰的端到端的控制。
但这种方式的缺点也很明显。构建的过程中大多数开发者主要关注的还是kubernetes,在其他方面没有深入的研究,因此在面临网络或存储选型时会非常头疼。另外自己要投入100%的运维成本,资源的投入也是一次性或阶段性的,随着扩容需求的出现成本会逐步增加。同时自建的模式往往资源量是有限的,集群规模达到一定体量后,弹性伸缩就会受限于底层资源规模。
第二种使用形态是在公有云上半托管kubernetes集群。这种情况下用户可以独占集群,不会被其他人干扰。公有云的kubernetes专属集群都经过大量了的测试和调优,所以现有的集群配置在该云平台上是最佳实践。当社区有新版本的时候云平台也会跟进测试,然后再推送给用户。同时kubernetes出现问题后,可以直接提工单,相当于和云平台共享运维成本。
公有云半托管模式的缺点,首先在于价格门槛上,一般是flavor的单价乘以N。且由于是一个用户独占一套kubernetes集群,因此整体的资源利用率仍然比较低。另外扩容的主体是虚机,耗费时间更多,会达到分钟级。
容器服务的另一种形态是容器实例服务。它有着诸多优点,比如免运维开箱即用、细粒度的资源定价、秒级的扩容和计费。
然而业界很多的容器实例服务都是私有API,会导致厂商绑定,且最大的问题在于不兼容kubernetes,Azure ACI就是典型的代表。
为此这些厂商使用Virtual kubelet项目作为兼容方案,把容器实例服务转化成k8s集群中的虚拟node,然后接到已部署好的k8s控制面中。这样就可以通过k8s API来创建应用,且能够正常接收各种pod的调度。
但本质上接管的pod未受k8s调度,比如在集群只有一个Virtualkubelet的时候,最终还是要到容器实例服务中去,没法通过一些pod API的规则来做。
Virtual kubelet数据面的方案也不完整,kube-proxy位置难以确定,容器存储方案尚不明朗。
基于k8s多租构建Serverless Container
其实最理想的情况是使用kubernetes多租户方案构建Serverless container服务,这样不仅能支持k8s原生API和命令行,价格门槛也更低,用户零运维,开箱即用。这方面目前最主要的挑战在于k8s只支持软多租。
我们先来回顾下常见的k8s多租场景。首先是SaaS平台,在k8s之上封装多租,控制面隔离要求低,不过由于应用来自外部最终客户,所以数据面隔离要求高。
第二是小公司内部平台,这种整体隔离要求低,基本满足k8s原生能力。第三是大型企业内部平台,为避免部门间管理干扰,在控制面上隔离要求高,应用来自企业内部,相对可信,因此数据不用做太多的隔离。
最后是公有云平台,这种控制面和数据面的隔离要求都比较高,一方面k8s API是暴露的,另一方面最终客户来自外部。
总的来说要想在公有云场景下提供serverless container服务,先得引入租户概念以及实现访问控制,然后是节点隔离和Runtime安全,最后是网络层面的隔离。
华为云的探索与实践
以上是我们基于kubernetes的容器实例服务全貌,它对最终用户暴露k8s API,底层是直接在物理机上跑容器,所以资源利用率上有很大提升。
大家都知道k8s的开源版本在google云上最大节点数可跑到5000,而其他的云平台一般很难到达这一量级,主要是受限于底层的网络和存储系统。
我们的目标是要做一个非常大的资源池,即使单集群能够达到5000也还远远不够,所以要在k8s上封装了一层API的同时,再引入federation,以此来实现多集群,通过多集群来提升整体服务的规模。
同时由于K8s多租能力有限,我们还会将额外的基于租户的验证和多租限流也放到这层。但是k8s原生这块只是做一些透层和请求合法性的校验。
图中右边的容器网络和容器存储目前开源的项目还无法满足需求,所以这块我们完全采用的是自研的方案。
众所周知Kubernetes中没有租户的概念,只有一层namespace,提供资源限制和pod资源配置功能。
在公有云上一个租户可以有多个项目,而我们另外还提供了针对单个项目创建多个namespace的能力,通过不同的namespace来区分开发或生产环境。网络最终也是映射到namespace,以此来实现隔离。
在我们的容器实例平台中,由于底层是直接跑在物理机上,造成不同用户的pod会运行在同一节点上,因此针对这块我们引入了kata安全容器。
Kata其实就是在传统pod外围包裹的一层轻量虚拟机,它仅用来拉起pod中的容器,所以可以做到定向的极致的裁剪,最后的启动时间基本上和单个容器处于同一水平。
Kubernetes为了和docker解耦以支持更多的容器引擎,另外封装了层container runtime interface。我们在这块接口上是follow的CRI,但是前面用了一层docker engine。
我们是直接在Docker的API下做分支,在传统的容器引擎模式下会走runc。在容器实例服务中,由于不同租户应用在同一节点上,所以要通过一层虚拟化来做,在这里就是kata了。
这种方式的好处在于解决了安全上的问题,计算资源通过虚机计得到了非常好的隔离。
最终效果
最后我们来总结下容器实例服务所达到的效果。
首先基于kubernetes的构建和上层封装的多租隔离,带来了原生的k8s API和命令行支持,使得用户无需感知集群和底层资源,也不需要在使用前创建集群。我们也针对多租的场景提供了图形化的控制界面,实现了良好的端到端的体验。
计算资源隔离方面,使用原生的kata container,和docker生态完全兼容。同时还有自有硬件虚拟化加速技术,底层接入了异构服务器支持和GPU、FPGA。存储方面,封装了原来的容器存储,实现了存储的多租。