柴宗三 分布式实验室
亚信公司有一些多年来在大数据方面的积累,比如数据,计算工具,算法等。现在要把这些能力结合起来做一个PaaS平台。经过一些前期选型与评估,我们决定用Kubernetes+外部服务形式的架构来建立PaaS平台。
关于Kubetnetes的架构大家应该都有所了解,主要的组件比如 API serve、etcd、Controller Manager、Scheduler以及cluster node上的Kubelet跟Proxy。但是我们今晚的主题,关注点主要在API Server和Controller。
API serve是Kubernetes集群管理的入口,我们在做集群部署,诸如起pod、rolling update、建rc之类都是通过kubectl这个CLI工具。
kubectl接收命令行的输入,再发请求给API serve,API serve在接收到请求后会做一些验证,比如请求体是否合法、格式是否规范等。校验后,格式化的请求数据便会被写入到etcd。
Controller Manager则一直在监控etcd的数据变化,Kubernetes里的各种资源对应到自己相应的Controller,比如pod会有pod的Controller,service有service的Controller,等等。当这些资源在etcd的存储数据发生变化时,Controller Manager则会监控到这些变化。
当我们在master node上观察Kubernetes的网络连接时,我们会看到有在API server与etcd服务端口之间有许多tcp长连接。这些长连接就是在通过etcd的watch接口监控etcd中的数据变化。我们甚至可以用curl来直接调用这些接口,便会发现这是一个不会断开的长连接,当etcd里的数据发生变化时,这个长连接会不断地收到全量数据。
Scheduler的任务则是调度具体的节点来拉取镜像,起pod等调度工作。并且把结果写入到etcd。当我们使用 kubectl get xx命令时,便会从etcd中取出相应资源的存储值并格式化输出到终端。
Kubernetes整体上采用了优美的异步机制设计,从kubectl发起一个命令后,API server本身不会执行命令要求的动作,只是把请求数据写入到etcd中,然后由Conrtoller Manager从etcd观察到数据发生变化,再调用Controller去执行具体的动作。如果是一个get操作,则是简单的从etcd返回相应资源数据。这里要注意的是,Controller或者Scheduler在完成任务,要写回数据至etcd时,也是通过API server来操作etcd。
关于Kubernetes的内容我们简单回顾到这。下面我们来看看Backing Service,即后端服务。
Backing Service指需要通过网络调用来完成的服务。比方数据库啊,消息队列啊,以及API访问的服务比如Twitter API等等。目前大家开发应用都比较讲究应用与服务分离,开发者只需要关注应用业务本身的开发,对依赖的服务只需通过外部接入,直接使用。
外部服务有诸如持久化、插件化,服务化等特性。拿MySQL来做比方,如果要把MySQL跟应用打包在一起跑在集群容器里,需考虑把数据存储挂一个Volume出来。而这一点在集群中会表现的更加复杂,因为涉及到跨主机数据同步的问题,开发者也许并不熟悉这些细节原理,如果总是纠结于应用之外的问题,那毫无疑问,开发效率会明显降低。目前主流的方式是把MySQL的连接信息注入到应用的环境变量中,如果应用不再使用这个数据库,那么可以随时卸载这个数据库,或者接入另一个数据库,只要简单地清除或修改环境变量就可以,而不用再关心怎样优雅地去停掉那个不再被使用的数据库。
在PaaS上,应用开发者依赖的服务都会产生交集,这也是我们为什么以后端服务的形式来提供这些服务的原因之一,我们把这些后端服务作为一个支撑服务体系,给开发者提供服务。还以MySQL来比方,如果没有后端服务,那每一个依赖MySQL的应用都要起一个MySQL的容器。对于集群管理者,意味着集群里会跑许多MySQL容器。
另一个原因是,有很多后端服务比如大数据算法和分析工具,只以API或者独立服务的形式提供出来,如此一来,开发者也没有能力在自己的应用里打包一个这样的容器/镜像。
最后一个主要原因是,这些后端服务对开发者是热插拔的。使用的时候只要通过命令/接口来创建一个服务实例,然后绑定实例到自己的应用上就可以了,非常便捷。
那么再具体地,开发者如何使用这些后端服务,与自己的应用对接呢?我们采用了Service Broker协议规范。
Service Broker是CloudFoundry中的概念,目标就是把应用与服务分离。Service Broker实现7个REST API,通过API,我们可以知道一个Broker会提供何种服务,并且轻松地实现对服务实例的创建、绑定、解绑,以及销毁。采用ServiceBroker规范还有一个好处就是,所有实现了Service Broker协议的服务,都可以以后端服务的方式无缝接入我们的PaaS平台。这样,一些第三方服务就可以轻松地为开发者提供诸多服务。
我们来看一看Service Broker API。
Service Broker API
上面7个API便是Service Broker协议中需要实现的接口。获取服务,创建实例,绑定应用,这一系列操作都通过这几个api完成。关于Service Broker API的更多信息,我们从CloudFoundry网站可以看到更详细的介绍,想进一步了解的同学可以到这个链接查看: docs.cloudfoundry.org/services/api.html
下面分享如何扩展Kubernetes。
Kubernetes的代码架构设计使得扩展其功能变得容易。正如前面所述,Kubernetes里的概念诸如pod、service,在Kubernetes里都被当作一种资源(Resource)来对待。所以我们的扩展首先是增加新的Resource类型。
针对Backing Service,我们增加了三个新的Resource,分别是:ServiceBroker、Backing Service、Backing ServiceInstance。相应地,我们增加了这三种Resource的Controller、API以及CLI。这样一来,我们就可以用命令行工具或者API,像操作其他资源一样去操作后端服务资源,来完成对应的功能。
下面我们以MongoDB的Service Broker来举例说明,开发者如何在应用里同一个经由后端服务提供的MongoDB实例交互。我们用oc命令来展示。oc是OpenShift客户端,也是由Kubernetes扩展而来,完全兼容Kubernetes。
首先是一个MongoDB的Service Broker的YAML定义,内容如下:
mongo.yaml
我们用oc create -f mongo.yaml 把这个Broker接入到平台。
接入mongoServicerBroker,并创建一个MongoDB实例
上面是几个命令执行的截图,分别解释一下。
用oc create命令创建一个broker后,我们用oc get servicebroker mongodbldp来查看刚刚创建的broker。
然后用oc get backingservice来查看刚刚接入的broker都提供什么服务。我们从图中看到,有mongodb_aws这个服务处于激活状态,也就是说,我们可以用它来创建MongoDB实例。oc describe backingservice会列出MongoDB这个后端服务更详细的信息,例如提供哪些套餐计划,套餐价格等等。其中,planid我们会在创建实例时用到。
oc new-backingserviceinstance mongo-demo –backingservice_name=mongodb_aws –planid xxxxxxxx 这个命令会创建一个MongoDB实例。这个命令需要两个参数。一个是后端服务名,和planid。这两个参数将对应到所有后端服务中一个唯一的plan。
到这里,我们的实例就创建好了,通过oc get bsi可以看到刚刚创建的一个实例,处于未绑定状态。
下面把这个实例绑定到我们的应用中。我们继续看一张截图。
绑定MongoDB实例
首先我们有一个应用叫docker-2048,用oc describe dc docker-2048来观察,可以看到输出的ENV项,是我们注入到pod/container的环境变量。只有一个值TEST=test。
我们绑定刚刚创建的实例到这个应用。用命令oc bind mongo-demo docker-2048。bind命令也需要两个参数,就是服务实例和要绑定的应用。这里是dc的名字。dc是deploymentconfig,是OpenShift里的概念。在Kubernetes中,则用rc。
绑定实例到应用后,我们用oc get bsi mongo-demo可以看到,这个实例上已经绑定了一个应用。我们再用oc describe bsi来观察,可以看到,连接MongoDB数据库的必要信息已经以环境变量的方式注入到了应用docker-2048。在kubernetes中,当rc(openshift中是dc)发生改变时,会触发pod重启。
继续来看一张命令截图:
解绑实例前后环境变量变化
从这张截图看到,刚刚与mongo-demo绑定的docker-2048,现在已经有了几个新的变量,BSI_开头的,就是我们新注入的环境变量,此时pod已经被重启了。这时候,我们的应用docker-2048就可以通过这几个环境变量来访问MongoDB。至此,外部服务和应用已经完成了对接。
当我们的应用不再使用MongoDB这个实例,我们只需简单的命令oc unbind把实例与应用解绑即可,也很方便。上面的截图显示了当实例与应用解绑后,应用的环境变量再次发生了变化。之前注入的环境变量被清除了。只剩注入变量之前的那个环境变量TEST。
从上面几张截图,我们可以依次看到一个MongoDB的后端服务实例被创建的过程,并且演示了实例如何被绑定到我们的应用docker-2048。我们观察到,在绑定了实例后,docker-2048的容器里多了一些mongodb的环境变量。这些环境变量随着解绑操作,被从应用容器里清除。上面的绑定与解绑过程,dc的改变会自动触发应用pod被重启。