【编者的话】亚信公司有一些多年来在大数据方面的积累,比如数据,计算工具,算法等。现在要把这些能力结合起来做一个PaaS平台。经过一些前期选型与评估,我们决定用kubernetes+外部服务形式的架构来建立PaaS平台。
@Container容器技术大会将于6月4日在上海光大会展中心国际大酒店举办,来自携程、PPTV、蚂蚁金服、京东、浙江移动、海尔电器、唯品会、eBay、道富银行、麻袋理财、土豆网、阿里百川、腾讯游戏、点融网等公司的技术负责人将带来实践经验分享,3月21日之前购票只需238元,欢迎感兴趣的同学抢购。
首先我们简单回顾一下Kubernetes
关于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
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的代码架构设计使得扩展其功能变得容易。正如前面所述,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被重启。
Q&A:
Q:请问在举的MySQL服务的例子中,讲到不是采用应用与MySQL一起打包成容器运行的方式,而是只需要把应用链接mysql服务的环境变量注册到PaaS平台上面就可以使用MySQL的服务了是吗?那么你们平台上面不是运行多个MySQL镜像吗?Q:如果我把MySQL环境变量注册到PaaS平台后,我应用链接你们平台上面的MySQL服务后,如果后期分库分表可以吗?还需要重新配置环境变量信息吗?
A:后端服务与PaaS在逻辑上是分开的,可能独立运行在平台集群外部,也可能运行在集群里,以service的形式服务。所以是否运行多个MySQL容器,取决于servicebroker的设计实现。每一种servicebroker的设计都不必相同。
Q:1. Kubernetes自定义扩展具体怎么做的?2. 后端服务你弹性伸缩直接用Kubernetes自带的,还是有什么改造?
A:这个取决于后端服务提供的套餐。不同套餐计划提供不同程度的服务。
Q:请问容器里的环境变量是Kubernetes的Backing Service自动生成的,还是OpenShift特有的?是不是可以说因为Backing Service会自动在容器里注册环境变量,所以某些场景下使用起来比起直接使用MongoDB地址来得方便?
A:1. 扩展Kubernetes是我们参考Kubernetes的API server、Controller这种异步机制直接在Kubernetes中增加代码,Kubernetes在易扩展性上做的挺好。2. 后端服务可以运行在Kubernetes集群上,或独立运行,其弹性伸缩由后端服务自身的设计来决定。
Q:Kubernetes的Volume,你们是如何使用的,当pod发生迁移后,如何保证Volume还挂在pod上?
A:是实例在绑定的时候由我们的扩展生成,并更新到pod里,不是OpenShift特有的。第二个问题的话,确实是这样,多数情况下,用环境变量都会很方便。
Q:现在生产上用什么监控,监控有报警吗,什么粒度的?还有就是日志收集怎么处理有什么成熟的方案吗?
A:Kubernetes本身提供了persistent volume功能,并且支持多种存储机制。pod迁移时,Kubernetes会维护其对应的Volume,根据不同的存储,Kubernetes有不同的调度实现。
Q:Kubernetes的部署可否做到跨数据中心吗?如何实现?谢谢。
A:我们采用了多种监控机制。主机、容器、和Kubernetes服务本身,我们都采取不同的监控策略/工具。至于粒度,也跟监控的对象有关。日志我们用ELK。
以上内容根据2016年3月15日晚微信群分享内容整理。分享人柴宗三,资深架构师,10年的IT从业经验,在云计算、大数据方面及大型企业级应用拥有丰富技术架构经验。负责北京亚信智慧数据科技有限公司大数据云平台的架构设计及开发管理。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesz,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。
A:Kubernetes是分布式的异步系统,理论上是可以跨数据中心的。但考虑到网络环境的复杂性,我们当前还没有作这方面的尝试。
有疑问加站长微信联系(非本文作者)