【人物简介】

丁琦诗,前游族网络MOB云平台运维负责人、前趣医网运维总监,中科大自动化系软件工程专业硕士,8年互联网运维工作经验,4年运维管理经验,带过20人的团队,也负责过近万台设备,擅长云原生技术、容器云建设、监控体系、持续集成等领域。

【内容提要】

Jenkins在K8s下的有几种部署模式? 如何使用原生控制器和资源? 包管理器是什么?怎么使用? 自定义控制器如何部署?

【讲座整理】

一般来说,Jenkins在K8s上部署,无论是有状态运维也好,或者无状态运维也好,无非通过以下三个手段进行:第一,原生控制器和资源,即利用已有的K8s基础架构和技术控制器,如deployment、service、基础资源来做部署;第二,包管理器Helm2来做部署;第三,自定义控制器,这部分主要是指Operator。下面就这三种模式分别展开论述。

一、原生控制器和资源

**1. 传统架构无论是云上还是云下,最外层有一个负载均衡器SLB,下面是LVS,到后面就直接到nginx这一层,之后到app和应用,到最后的DB。**总体来说,传统架构的服务自愈性是比较欠缺的。为什么要用K8s?正是因为可以解决这一问题:它可以让我们把单体应用自己拉起来,让用户期望的状态和运行状态保持一致。

正如下面右图所示,外面一层是不变的,让SLB/LVS流量进来,下面开始就发生了变化,到了nginx的一个模块后,这个模块会把流量分发到nginx pod上面去,通过nginx pod controller让用户期望的状态和运行状态保持一致,这样一来,即使在nginx pod少一个的情况下,在nginx pod controller的作用下也能保持始终有两个nginx pod的运行。App service也是类似的,先到app service的一个模块,再把流量分发到app pod上面去,具体到app pod的运行数量和运行状态也都由K8s 的组件app pod controller来负责,再到最后的DB。 2. K8s下面有很多基础资源,无论是跑Jenkins还是Grafana或者其他,如何选用这些基础资源是部署时首先要考虑的问题。 2.1 service。service有五种,比较常见的有Headless、NodePort、ClusterIP。以NodePort型service为例,它提供了一种外部流量访问K8s集群中service入口的方式。当外部用户要访问K8s集群中的一个web应用时,那么我们可以配置对应service类型为NodePort。 2.2 Volume。对于K8s来说,有两大类的存储:通过PVC+PV来定义静态的存储,通过PVC+SC(存储类)+PV来定义动态的存储。初学者容易混淆的是,PVC是用户期望定义的一个东西,PV才是它的存储时限。 2.3 RBAC。即基于角色的访问控制,首先会定义一个服务账号ServiceAccount。这个服务账号是给应用用的,比如Jenkins肯定也要用到一个ServiceAccount。规则是Role、ClusterRole。我们可以把ServiceAccount通过RoleBinding绑定到Role上,则该用户拥有role定义的权限;通过Clusterrolebinding绑定到Clusterrole,则该用户拥有Clusterrole定义的权限。对于namespace内的授权使用RoleBinding,集群范围内使用ClusterRoleBinding。 2.4 ConfigMap&Secret。这两个都是用来保存K8s上面应用的配置。它们两个唯一的区别就是ConfigMap是保存非敏感的信息,Secret可以保存敏感信息。这两个组件的申请、使用、加载其实都是大同小异的。 2.5其他需要注意的点。其一,调度期望和调度机制。机器很多时要管理好运维类pod和业务pod的分离。同时要努力实现真正的业务高可用;其二,网络策略。当有很多pod在运行时,网络策略是必须考虑的;其三,安全。在K8s上面把一个pod跑起来其实很简单,难的是如何跑好,安全也是需要考虑的一大因素。 3. 如何用原生控制器和资源区部署一个Jenkins应用。 3.1划分所需的名称空间。这一条不是非必选的,我们对于一个应用会划分一个专门的namespace,可以按环境来分,也可以按业务组来分。 3.2基础控制器选型。基础控制器有哪些?Deployment控制器、SatefulSet控制器、DaemonSet控制器、job控制器、cronjob控制器。对于Jenkins来说,无疑选择Deployment来做基础控制器。 3.3基础资源选型。应用跑起来之后,怎么让K8s集群外的人能访问到我们的应用?简单来说,可以直接用NodePort来做,在存储上选的是PVC+PV,PV是使用NFS来实现。 3.4服务暴露(可选)。因为解决Jenkins是南北流量的问题,而不是集群内访问东西流量的问题,所以服务暴露是我们需要考虑的。对于Jenkins来说可以用NodePort直接来做,也可以后面去接Ingress nginx做服务暴露,也可以挂到SLB后面做服务暴露。 3.5网络策略、安全、调度机制。要让一个Jenkins应用跑起来、能用之外还能跑的好,就需要考虑全面周到一点。

  • 4. 小结。关于现状和挑战。 4.1无状态业务、微服务,没有别的选择,肯定是选原生控制器,可能只是会在服务暴露时有一个基础选型,这是传统架构转型至K8S的必经之路。 4.2挑战在于:依样画葫芦看似简单,举一反三却举步维艰;用原生控制器需要充分了解k8s原生组件和资源细分的应用场景和区别;对所涉及配置文件的生命周期,缺少完善的管理手段;只解决了部署刚需,却没有完善复杂应用的部署逻辑和本土化用户的使用习惯。

二、包管理器:Helm

前述提到,原生控制器缺什么?配置文件的生命周期管理是原生控制器所欠缺的。哪些文件该是我考虑的,这个文件变更了怎么办?怎么做版本管理?这就是Helm要解决的问题。 **1. Helm2的拓扑架构。**如下图所示,charts是基础配置模板,config是charts配置参数,helm是命令行客户端,通过grpc协议,向tiller发送请求,用来做版本发布、回滚、打包、升级。tiller存储在CM里面的,然后在Helm3里已经移除了,因为它有一些局限性。要注意的是,Helm和Docker是一样的,有一个仓库的概念。对于一个成熟的软件来说,第三方的支持是很少的,不可能让所有的helm都自己写,所以对于helm来说,它有自己成熟的远程仓库,我是写在集群外的。事实上还有一个集群内的,版本管理之后会有一个local仓库,可以把自己的发版应用打包,然后发布到local仓库,做内部的版本管理。

2. 部署流程。 2.1工具的部署安装。对于基础选型来说并没有很多选择,无非是选Helm2或Helm3。 2.2 charts制作。Helm有两种类型的repo,一个是已有chart使用,直接在repo中搜索和安装,不用自己造轮子;第二个是自定义chart制作。 以Jenkins为例,可以通过Helm create Jenkins帮我们生成一个树状结构的整个东西(如下图)。再看外面两个文件,这里templates是K8S内置的,就是它自己的基础控制器的文件。templates之外的这两个charts、values是Helm自己定义的东西。

我们把前面参数定义好之后,可以通过Helm install -n “namespace” Jenkins,把Jenkins目录下的项目装起来。第一步,初始deployment;第二步,相关pod在创建;下一步,service,pod去跑,所有运行完成之后,跟你说“你是通过这些命令去拿到你对应的URL”。最后可以通过helm list看到刚才发布的应用,Jenkins版本发了1版,发布的时间点、状态是已部署,chart是Jenkins-0.1.0,namespace是default。 2.3 release迭代。对于自定义好的create之后的charts怎么玩?不可能发了一版后就完全不动了,更新(upgrade)怎么做?如果不符合预期,可能是产品预期、业务预期、技术预期,需要回滚(rollback),又要怎么做? 2.3.1更新。看下图中的chart.yaml文件,先要去变更helm的描述清单,appVersion变更,version也要变更。除了之前变更之外,把镜像仓库版本也进行变更。 2.3.2回滚。回滚是用命令helm rollback,后面跟位置参数。Jenkins需要回滚哪个应用?第一个位置参数是业务名,第二个字段是有一个default值,是默认后帮你回滚到1版本,写一个1。回滚之后,前面进行第一次初始化发版,第二次更新,第三次回滚,为什么这个版本还是3呢?这里是让我们每次发版都有记录可查,所以记录是递增的。 2.4打包和发布。当这两个状态结束之后,要进行打包和发布,打包到哪里?打包到我们定义好的local repo里来。第一步就是用Helm package打包项目,然后创建项目存放路径,把打包好的项目丢过来,再通过helm.serve的命令指定好本地仓库的地址是哪里,最后就可以在本地仓库里找到刚才的项目了。 3. 小结。关于现状和挑战。 3.1 Helm完善l k8s资源生命周期的管理,如:升级、回滚、下线、发布等;解决了单体应用创建时的配置依赖问题;提供了更上层的编排能力,交付的标准颗粒度是应用,而不是k8s基础对象或组件;Helm的生态圈和官方提供的众多包含有最佳实践的chart包。 3.2 挑战在于:需要充分了解k8s原生组件和资源细分的应用场景和区别;Helm工具本身架构设计和用户体验的不足:如Tiller权限控制、数据存储(CM)、发布体验等。

三、自定义控制器

**1.自定义控制器(Operator)的诞生。**2016年秋天,CoreOS在福斯特城(Foster City)公寓结对编程。当时两个人做了一个小项目,也就是Operator。因为他们发现K8S原生的东西并不能满足用户期望。比如在传统架构之上,MySQL集群,无论是主从也好,Mango集群,分片、副本级也好,怎么做数据备份,怎么做数据迁移,怎么做回滚,怎么做备份,用K8S原生的基础组件、基础资源其实根本做不了。Operator所做的就是教我们定义日常运维的经验、操作代码化。

**2.自定义控制器(Operator)做了什么?**整体可以分为四点。其一,用户期望。K8S的维护者肯定希望用户能更简单、更方便地去用,帮助他自己的产品去上集群;其二,研发实现。除了帮助用户支撑所需要的第三方组件快速上K8S集群之外,完善和本土化这些复杂应用集群的运维功能也至关重要;其三,K8s赋能,允许用户添加 API 对象的插件能力。如果K8S没有一个暴露的API或暴露接口让研发人员去做事也是纸上谈兵;其四,社区推广。使用方可以在Operatorhub.io和awesome-Operators快速分享或找到Operator。 3.如何通过Operator部署一个Jenkins应用。简言之,先定义CRD,再定义Controller,最后定义他的用户状态,帮用户去识别化他的应用集群。 3.1定义CRD。CRD是K8s给用户能够自己写Controller的一个API,扩展K8s API的地方。 3.2定义Controller。对Jenkins来说,Jenkins Operator0.3.3是Jenkins官方已经写好的一个Operator,我们只要拿下来去用。 3.3定义用户期望的状态。 4. Jenkins operator帮我们简化了什么? 4.1自动化发现和配置。以seed job为例。和传统玩法相比,运维人员需要做的只是把这些job写成Jenkinsfile,保存在Git里面去,这样就可以做一键迁移了。这让我们有更多的视角去看到底哪些好、哪些不好。 4.2配置代码。这里全部基于配置代码的方式去做,而不是传统的基于磁盘的备份,这么做的好处显而易见。 4.3备份和还原。backup and restore内置字段,清楚地告诉你这一块是做backup的,这一块是做restore的。这样做相当于把用户习惯直接分装掉了,所见即所得。 5 小结。关于现状和挑战。 5.1完善复杂应用的部署逻辑;本土化用户的使用习惯;自由度高,用户可以可定制化开发controllers。 5.2挑战在于:需要充分了解k8s原生组件和资源细分的应用场景和区别;需要充分了解CRD和K8s API;需要一定的研发能力。

【总结】

Jenkins其实部署在K8S上三种模式,不一定说只教会大家用Jenkins,其实别的东西也一样。但这三种模式都离不开一点:都需要去了解K8s下面所有的基础控制器、基础资源怎么去使用、怎么去定义。当你把所有基础控制器和资源都摸透之后,用Helm3、Helm2也好,网上去查Operator也好,都是可以的。只有底子打好,知道哪个东西是干什么的时候,才能知道它们的应用场景。