Kubernetes容器管理平台应用管理开发实践

我们通过k8s平台部署应用时,需要根据应用的具体需求创建pod、deployment、service、secret等资源对象。k8s是如何对一个应用相关的资源对象进行统一管理的,k8s是如何构建应用模型的?

 

如果我们对应用模型进行抽象化,可能会遇到什么样的问题?k8s资源是通过yaml文件来描述资源对象,由于同一资源在命名空间的命名唯一性,我们无法在不需要修改的前提下,使用同一份yaml文件构建多个资源对象。为了满足平台使用者快速部署的需求,我们在应用部署开发时可以针对这些限制做哪些扩展?

 

一、应用模型

 

假如我们要将一个javaweb应用容器化,javaweb应用的结构为一个运行在Tomate里的webapp,JSP页面通过JDBC直接访问Mysql数据库并展示数据。还有以下的需求:tomcat,mysql需要能够进行副本伸缩,mysql需要从一个配置文件中设置“MYSQL_ROOT_PASSWORD”环境变量,tomcat、mysql镜像需要从搭建好的私有镜像仓库拉取。

 

能够从集群外访问Mysql、tomcat上述的需求,我们需要创建以下K8s资源:

 

  • deployment:mysql、tomcat

  • configmap:mysql

  • registry secret

  • service:mysql、tomcat

 

在k8s中如何对这些资源进行统一的管理呢?

 

label和label selector构建了K8s系统的应用模型,对资源对象进行了划分。k8s是通过给指定资源对象捆绑一个或多个不同的label来实现资源的分组管理功能。pod通过label标记自己,service通过label selector来决定映射到哪些pod上,deployment也是通过labelselector来决定哪些pod是由它编排的。

 

开发环境的tomcat+mysql的java web应用,按label划分的思路,变成了带有"environment=dev&& app=javaweb"的tomcatdeployment和mysqldeployment组成的后端,并由带有selector为"environment=dev&& app=javaweb"的service代理了流量。

 

 

configmap以及secret时,要么直接加上特定的label进行标记,要么记录这两个资源对象名。在清理java web应用时,我们才能将应用相关的deployments,services以及configmaps,secrets或者service accounts等资源对象都统统清理掉。

 

清理应用相关资源可以通过以下命令进行:

kubectl deletepods,services configmap sercrets -l environment=dev,app=javaweb

 

如果通过标签去删除,必须注意删除的原则是只要包含指定的标签集的资源,都会被删除。假若有另外一个javaweb应用,其资源指定的标签为”environment=dev,app=javaweb,release=v1,同样被上述命令删除。

 

因为标签是用户可随意定制的,大量应用存在的情况下,如果管理不当,很容易出现label重叠的问题。除了上述资源容易误删除的问题,重叠的label也会导致service流量下发到pod时的混乱,以及deployment编排的pod时的混乱。

 

二、应用抽象

 

在我们的平台设计中,直接引入了应用栈的概念,明确地对应用内所有资源对象进行了划分。应用栈只是抽象的概念,里面记录着支撑应用的微服务群configmap、Secret等资源。

 

应用栈名在整个平台是唯一的,通过强制在k8s资源对象中绑定以应用栈名为值的Label,来解决不同应用间由于Label混淆导致的各种问题。

 

configmap、secret等资源从属于应用栈,被应用栈中所有pod共用。不直接通过labe进行标记,而是由应用栈进行记录创建的资源,这些资源以应用栈名来命名来保证唯一性。

 

微服务是一组提供特定服务的Pod,对应的是k8s中deployment/daemonset等pod的编排。微服务名是namespace唯一,在删除资源时,应用栈会请求k8s资源删除指定命名空间上的资源,不直接通过标签去删除。

 

应用抽象的一个难题是如何判定一个应用是否正常支撑业务的运行。所有微服务都能正常提供服务时,该应用才是正常的。微服务是否正常提供服务的判定根据构成的k8s资源对象的不同获取方式也不同。

 

 

比如说一个微服务是由deployment编排的pods组成DESIRED:指的是deployment应当启动多少个PODDESIRED:指的是deployment应当启动多少个PODCURRENT:指的是当前有多少个POD。如果在更新状态,则CURRENT POD的数量会多于DESIRED,CURRENT只管当前POD创建出来,并不关心当前POD有没有真正把容器起来。

 

例如Pod正处ContainerCreating状态,Current就认为其POD已经创建出来了UP-TO-DATE:用于deployment回滚更新时,用于表示最新版本ReplicaSet的Pod的数AVAILABLE:处于Running状态的POD的数量deployment是没有状态字段,我们需要根据上述Pod的数量去计算当前微服务处于什么样的状态(升级中、创建中、所有pod正常运行等等)。

 

这里比较强调的是所有pod正在运行和所有pod正常提供服务是两种概念。所有Pod正常运行,只能表示pod中容器的程序已经启动了,不能表示其能够正常提供服务。

 

以wordpress+mysql实例,当wordpress正常启动以后,如果尝试访问mysql失败的话,会不停轮询访问mysql来提取数据。从Pod的角度来看,wordpress这个pod已经正常了。

 

但从微服务的角度来看,这个pod还没有能够正常的提供服务。所以我们还需要针对Pod的状态判定微服务是否正常工作。从用户的角度来看,他们只能看到应用已经正常运行了,所以最好建议用户对pod中的容器添加readinessprobe,以保证应用状态的正确判定。

 

另外如果在namespace设置了limitrange,常常会因为pod的requset,limit的资源配置的原因,导致一个pod都创建不出来。这种情况下,必须通过获取pod的status conditions中提取"FailedCreated"信息查明pod创建失败的原因,以及deployment的事件进行分析出错原因。

 

三、应用扩展

 

k8s资源是通过yaml文件创建描述的资源对象,因为部分资源对象的唯一性(例如资源在namespace命名唯一性,nodeport在集群唯一性),导致一份yaml文件无法重复的创建应用。我们可以对应用部署做哪些扩展,让用户更便捷的进行应用部署呢?

 

前面提到过一个应用包含许多微服务的组件,每个组件对应k8s编排资源对象的一种,在整个namespace是名字唯一。所以我们常常在使用同一份Yaml文件去创建多个实例时,需要不断地更改对象名和标签,如果微服务中的service通过NodePort暴露主机端口,还得修改主机端口地址。

 

针对这些问题,我们可以对k8s资源对象的YAML进行扩展。我们的平台设计中,描述应用是应用模板,其由多个微服务模板组成。微服务模板是基于K8s资源YAML的扩展的YAML文件。应用模板经过解析器处理后,才会转换成描述k8s资源对象的yaml文件。

 

我们提供了一些平台定义的变量方便平台用户去定制模板,比如"UfleetRandomString"会为用户随机生成一个资源名,"UfleetRandomHostPort"会为用户随机生成一个k8s集群中未被使用的30000-32767范围的主机端口等等。

 

 

其次提供了用户自定义变量的能力。用户可以把经常会修改的资源字段用特殊变量名标记,并附值,我们称之为应用配置。在解析器解析时,将解析模板中应用配置的键替换成值。比如说前面的javaweb应用,用户可以标记mysql的镜像名为%%MysqlImage%%,mysql的镜像版本为%%MysqlVersion%%,并在应用配

 "MysqlImage=myregistry.com/database/mysql,MysqlVersion=4.4"。

 

 

用户想变更部署的镜像地址和版本时,不需要再修改原生的模板,只需要更改配置相应的参数就可以了。

 

引入应用模板和配置,同时目的也是为了结合我们平台的应用商店来使用。应用商店的存在是目的是为了实现一个应用多套模板组合各种配置,让用户很轻易地拉起所需要的应用。用户提前定制好所需要的应用,在用户部署应用时,需要不停的对应用部署的细节进行升级微调,达到更好的运行效果。我们借鉴了dockerexport的功能,让用户应用部署后进行升级微调,再把微调好的模板,导出到应用商店中。

 

 

用户以后就可以直接使用新的应用模板,也可以把应用模板发布给其他用户使用。

 

以上是我为实现用户快速应用部署这一目的,在有容云做容器云平台开发的一些经验,谢谢大家。

 

Q&A

 

Q1:YAML的定义,你们是模板化了吗?如何实现根据客户需求做自定义修改?

A:我们应用模板是在原生k8s资源对象描述YAML添加特殊的通配符,在实际部署时会替换成应用配置上的字段。我们提供UI让用户直接对K8s yaml文件修改,添加通配符,并创建应用配置设置通配符实际值。

 

应用模板:

kind:"Deployment"

spec:

template:

spec:

containers:

-

image:"%%WordpressImage%%:%%WordpressVersion%%"

name:"wordpress-%%UfleetRandomString%%"

 

 

Q2:对于应用的监控和log日志系统,6有没有好的方案?

A:监控用的是Prometheus+Grafana,日志是filebeat+ ELK。

 

Q3:paas平台有没有集成类似aws loadbalance,提供负载均衡呢?

A:后面版本会集成,现阶段还没有计划。

 

Q4:应用栈名在整个平台是唯一的,通过强制在k8s资源对象中绑定以应用栈名为值的Label,来解决不同应用间由于Label混淆导致的各种问题。这个应用栈名没听说过, 不懂,把它写成一个label的值,那不还是用label来聚合一些资源的?

A:其实就是一个label值。应用栈名是平台唯一(准确的来说是某个集群唯一),由我们平台进行保证。通过强制绑定这样一个标签,可以尽量避免混淆的问题。你从网上找一个redis deployment的例子,你的同事也找了同样的例子,恰好在同一namespace上使用。这样会出问题。通过我们的平台,redis deplyment会根据应用名的不同,添加不同标签做selector。可以大幅度减少这类问题的发生。

 

Q5:在国内,如何简便快速的安装k8s?

A:如果是为了开发测试接口的话,可以直接使用minikube。如果因为墙的原因无法使用Kubeadm,可以直接通过下载二进制包进行部署的方式,二进制包的下载没有被墙。也可以使用我们的ufleet平台进行快速部署。

 

Q6:Mesos里批量任务用chronos 这个里面批量任务怎么做?

A:mesos不大了解。K8s可以使用cronjob(1.5处于alpha特性,需要apiserver启动--runtime-config=batch/v2alpha1=true来支持)和job来完成。

 

Q7:有状态和无状态应用是怎么部署的,如何回滚升级?

A:有状态服务使用statefulset(1.5版本之前叫做petset)进行部署,无状态服务可以使用deployment部署。更新statefulset和deployment中的podtemplate即是升级。stateful不支持回滚操作,可以记录每次更新前的模板,通过删除重建的方式进行回滚。deployment可以使用rollback命令继续回滚。

 

 

分享:Kubernetes容器管理平台应用管理开发实践

有容云-构筑企业容器云 www.youruncloud.com

温馨提示

对Docker容器技术或容器生产实施感兴趣的朋友欢迎加群讨论。我们汇集了Docker容器技术落地实施团队精英及业内技术派高人,在线为您分享Docker技术干货。我们的宗旨是为了大家拥有更专业的平台交流Docker实战技术,我们将定期邀请嘉宾做各类话题分享及回顾,共同实践研究Docker容器生态圈。

加微信群方法:

1.关注【有容云】公众号

2.留言”我要加群”

QQ群号:454565480

有容云微信二维码