共计 7401 个字符,预计需要花费 19 分钟才能阅读完成。
本篇内容主要讲解“基于 Jenkins 和 Kubernetes 的 CI 工作流怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“基于 Jenkins 和 Kubernetes 的 CI 工作流怎么实现”吧!
摘要
Jenkins 作为最为流行的持续集成工具,在结合使用容器技术,Kubernetes 集群的基础上,该如何发挥出新的能力,在应用微服务化的基础上,提供更好的 CI 方式,值得我们每一个开发人员去持续不断的摸索。
Jenkins 和 Kubernetes
Jenkins 作为最流行的持续集成工具,有着丰富的用户群、强大的扩展能力、丰富的插件,是开发人员最为常见的 CI 工具。在 Jenkins 加强其 Pipeline 功能后,更是可以通过丰富的 step 库,实现各种复杂的流程。同时随着 Docker 的流行,Jenkins 内也增加了对 Docker 的支持,可以实现容器内流程的执行。
而 Kubernetes 随着版本迭代的速度越来越快,在容器圈内的热度也越来越高,同时每次版本发布,所新增的功能也不断增加。做为当前主流的容器管理平台,其强大的能力无需在此多做介绍。
应用容器化和应用微服务化设计思考
容器化不意味着微服务化,传统单体应用也可以容器化,但是很难享受到容器化后带来的好处。微服务化也不是一定要容器化,应用拆解为微服务后,一样可以不利用容器而是通过传统的运维来完成系统构建和部署。当微服务化和容器化相结合之后,就能充分利用各方优势,带来了弹性伸缩,简化部署,易于扩展,技术兼容等优点。
我们在针对应用进行微服务化拆分的过程中,主要先考虑到的是功能点、控制对象、开发组的人员配置安排,产品路线图规划等。例如,针对现有开发组人员人数、分配和各自的技能熟练程度,就可以考虑到服务模块数量的控制,安排好服务模块开发小组;针对功能点和中远期产品规划,就可以将特定功能归纳到一个服务模块中,并在版本开发迭代的过程中,通过扩展这个服务模块的能力,来完成产品功能的开发,或者暂时将部分功能整合在一个模块中,随着功能增加或迭代开发,再进行进一步的模块拆分或拆解。
对于模块开发的要求,由于使用了容器技术,对于开发语言或特定框架的选型,可以交给具体的模块开发人员。在团队内,我们不做强制要求,但是做建议要求,避免出现过多的技术栈,导致后期的维护困难。在我们团队内,就只集中在两种后端开发语言的使用,相应的框架或主要的开发库,也都有相应而且明确的选择。对于模块的 API 接口,使用 REST,并且至少按照成熟度模型 LEVEL2 提供 API。
容器环境下的编译和单元测试
我们整个 CI 工作流的驱动,都是由 Jenkins 完成,并且使用了 Jenkins Pipeline。第一,Pipeline 可以更好的组合 job 内的 Stage,重复利用模块间相同的部分,并且随着开发复杂度的增加来逐步增加扩展 stage,实现更多所需的功能;第二,将 Pipeline Groovy 脚本来源设置为源代码内,可以根据源代码功能点来控制流程,同时也完成了对脚本的版本管理。
由于有容器这么个工具,我们各个模块的编译环境,单元测试环境,也都放到了容器中。各个模块均可以安装自身模块的运行特性或环境要求,准备自身的编译环境、单元测试环境、运行环境,因此,代码库内会分别留存相应的 Dockerfile,通过不同的 Dockerfile 完成不同环境镜像的准备。
同时,jenkins 现在也可以通过 Docker Pipeline 插件,支持在容器内运行 Step,因此我们利用其功能完成的实际的编译和测试流程是这样的:
1. 使用编译环境的 Dockerfile 构建编译环境镜像。
2. 使用编译环境镜像启动容器并在容器内完成编译,完成编译的中间产物也暂存在 Workspace 中。
3. 使用测试环境的 Dockerfile 构件单元测试环境镜像。
4. 使用单元测试环境镜像启动容器并在容器内运行单元测试,单元测试脚本来源于代码库,同时也使用到编译时生成的中间产物。
5. 使用发布 Dockerfile 构建实际发布镜像并上传镜像库。
其中由于编译环境和单元测试环境不是经常变更,也可以抽出编译环境镜像准备和单元测试环境镜像准备两个步骤放到独立的 CI job 中去,需要时手工触发即可。
服务部署和升级
对于 CI 流程,在完成编译和打包后,需要做的就是服务启动和测试了。我们利用的是 Kubernetes Deployment 和 Service,在每次 CI 流程完成编译和打包后,通过拿到 Build 号,作为镜像的 Tag,完成镜像的上传归档;同时利用 Tag,修改 Kubernetes 中已经创建的 Deployment,利用 Deployment 的 Rolling Update,完成升级。
对 kubernetes 服务模版和服务配置的扩展
我们在实际使用 Kubernetes Deployment 升级的方式进行服务部署的过程中,发现其中还是存在很多不方便的地方。例如:Kubernetes 内的同名问题,Kubernetes Deployment 升级时的镜像 tag 变更问题,等等各处需要随着 CI 流程可能存在变更的地方。例如在有相同名字的 Deployment 存在的情况下,后来的 Deployment 会无法创建,这导致如果想以启动新的 Deployment 的方式来测试某个版本,需要修改名称,对于与 Deployment 相关的 Service 也一样,在启动新的命名后的 Deployment,也需要启动与其对应的 Service 用于暴露服务。对于 Deployment 升级所需的镜像 Tag 修改,需要每次随着 CI 生成了新的镜像 Tag 而做变更,因而每次需要修改相应 Yaml 文件内的镜像 Tag,修改为实际 CI 流程中生成的值,然后再使用升级功能完成服务升级。
针对这些问题,我们使用了一套文本模板引擎,部署或升级用的 Yaml 文件本身写成为模板,可能有变化或者需要根据 CI 流程变化的位置,使用模板标识占位,而具体的模板数据内容,则或者通过 Jenkins 的 CI 流程获取,或者使用特定的配置文件读取,或者从具体的输入参数来获取;同时,模板数据内容,也会在实际部署时,做为 Kubernetes 的 Configmap 设置到系统中,因此数据内容也可以通过 Kubernetes 使用 Configmap 的方式来用到环境变量或启动命令中。通过文本模板引擎,将模板和数据合并后,生成的 Yaml 文件,再作为后续 Kubernetes 操作所使用的内容。
通过利用这种方式,我们把需要部署的内容分离成了模板和配置;模板一般在服务架构,使用的镜像名,启动方式或配置参数没有大的变化的情况下保持不变,而通过不同配置的灵活使用,完成服务升级或拉起新部署,完成不同数据存储使用的指向,完成对各模块内部配置的修改。通过利用这种方式,我们的可修改的内容,从 Configmap 本身只能覆盖到的环境变量或启动命令这块,扩展到了启动名称,Label,镜像等 Yaml 文件内的各个可填值处,以此来解决同名,镜像修改,Label 增加或变更等各种使用 Kubernetes 时碰到的问题。
自动化测试
在通过 Jenkins 拉动完成编译打包和服务升级部署后,就可以拉动自动化测试了。测试框架我们选择了使用 Robotframework。测试脚本通过 Kubernetes Service 获取到服务的具体暴露端口,然后再根据测试脚本依次执行针对 API 的测试。测试脚本的来源,部分是从各模块代码库中,由各模块开发人员提交的针对自身模块的 api 测试,部分是由测试人员完成撰写提交的针对跨模块的测试。针对自动化测试这块,我们的完成度并不是很高,仅仅是搭建起了基本的运行框架,能够与整个流程对接上。
版本发布
由于开发的产品本身就是由若干镜像构成,因此产品发布,可以归结为镜像的发布。在测试通过后,可以简单的利用镜像复制能力,将测试通过的相关镜像的版本,通过镜像库间的复制,由开发测试所用的内部镜像库,复制到外部发布镜像库,就可以完成版本发布,同时可以通过复制时的 Tag 控制,发布为指定的版本号。
总结
如上介绍,说明了我们在自身开发过程中建立 CI 流程的做法。对于整个流程的建立,我们并没有太多需要特殊化处理的地方;对于各项工具的使用,也没有太多突出之处;我们仅仅根据自身需求,建立了和开发过程适配的 ci 流。在此介绍我们的 ci 流程的建立,也是希望抛砖引玉,能从更多处获得交流和向大家学习的机会。
Q A
Q1:嘉宾分享的内容还是概括了些,有没有具体例子?配图说明的直观内容?
A:基于 K8s 的 CICD 产品即将发布,会提供对应的 Demo 演示平台,请及时关注,谢谢!
Q2:关于 Jenkins 和 Docker 集成的几个插件可以分享一下吗?
A:Docker Pipeline、Docker Plugin、Docker-build-step 这几个插件。
Q3:请问有容云的镜像复制大体思路是什么?目前我们是测试、预发布、发布共享一个仓库。通过辅助模块标签实现的!
A:镜像复制功能,简单来说实现了不同项目间的镜像克隆,根据我们镜像仓库的设计,对不同阶段(开发,测试,可上线)的镜像以不同项目分类,基于镜像复制功能即可快速实现不同阶段的产品发布,也就是镜像发布,此功能可下载 AppHouse 版本进行试用。
Q4:贵公司同步的吗?你们的配置文件是通过配置中?你们的配置文件是通过配置中心管理还是镜像间的环境变量实现的?
A:不是实时同步的,而是通过了我们公司镜像库产品的镜像复制能力实现的。目前开发流程中的产品运行配置是通过自身增强的配置文件能力实现的,配置会用来修改应用部署的 Yaml 文件,也会生成为 Configmap。
Q5:有没有一个简单的 Sample?可以上手跟着练习一下。
A:基于 K8s 的 CICD 产品即将发布,会提供对应的 Demo 演示平台,请及时关注,谢谢!
Q6:此次内容的 Step by step 示例,能提供一个 Demo 吗?
A:基于 K8s 的 CICD 产品即将发布,会提供对应的 Demo 演示平台,请及时关注,谢谢!
Q7:我的项目的版本二只是修改了几个页面,也是和第一次发布一样的流程吗?
A:是的,流程化就是代表着可重复和可自动化,而无需去关系具体的修改内容。
Q8:你们部署的 K8s 版本是哪个?K8s 的调度服务只能觉得容器起在哪个节点上吧,Rc 是 2 的话,构建任务运行在哪个容器中这个怎么调度,为什么我的都运行在一个容器里面?
A : 我们目前使用的是 Kubernetes1.5 版本。本身构建任务不是跑在 Kubernetes 上的,而只是应用自身跑在 Kubernetes 上。
Q9:你们是类似于 Openshift 那种平台式的吗?还是需要开发人员自己部署?
A : 本身讲的是我们自身产品开发的流程,相关的产品会是类似平台型产品,需要自行部署,而不是公有云产品。
Q10:两个框架是开源的吗?
A : 是的,都是开源的。
Q11:K8s 的 Yaml 的部署文件的模板怎么去替换占位符?旧版本的容器怎么处理?
A : 使用了自身开发的一套文本模板引擎,其实类似 Web 框架中的模板引擎,完成模板和配置的合并,通过使用配置中的 key-value,替换掉模板中 key 的占位符。另外由于是使用的 K8s Deployment 的 Rolling Update,升级完成后旧版本的容器 /Pod 会由 Kubernetes 自行删除。
Q12:开发测试、部署等各个阶段的镜像,方便共享一下吗?
A : 这些镜像都是我们自身针对我们各应用模块所准备的,对其他人并没有什么用处,编译环境用的镜像不少就是官方镜像,例如 Golang:1.7, Python:2.7。
Q13:传统单体应用如何容器化改造,可否分阶段实施?
A : 可以的,容器化改造或微服务化改造有很多实施方法,例如逐渐重构拆解,或新增模块进行微服务化和容器化,或开发新的模块替代原有应用的功能点,等等。各团队均可以选择合适自身的流程来进行改造。
Q14:数据库可以容器化吗?
A : 可通过将数据卷挂入容器的方式将数据库容器化,但是现在实际项目中还很少见。
Q15:我们目前也是使用 jenkins 动态挂载容器来运行安卓编译构建作业,但现在每次使用镜像生成容器后因为要更新代码,导致整体编译时间比以前使用物理机方式要延长 10 多分钟,我们的镜像每天自动制作和下载,但因为代码更新太快及并发太多,编译时需要独占物理机,所以机器占用也很多,大家有什么建议解决这个问题,谢谢!
A : 容器化编译就是将编译环境封装起来了,这种情况下一台物理机同步跑多个 Job 也行,可以看看怎么提高对物理机的利用率。
Q16:请问项目中存在多个镜像,是如何做关联发布的?
A : 我们是使用了 Jenkins 的 Mutil Pipeline,另外由于我们的模块的微服务拆解,对应同步发布镜像的需求并不高。
Q17:有这样一个场景,两个服务有依赖关系,服务 A 依赖于服务 B,如何保证服务 A 和 B 的启动顺序的?
A : 良好的设计是使得 A 服务启动后自行完成对 B 服务的检测发现和调用, 而不是强依赖其启动顺序。
Q18:Kubernetes 的服务的模板和配置,这个模板怎么来的,是用户自己编排?还是自己事先准备好的?配置数据是怎么存储的?
A : 因为当前模板和配置只用来启动我们自身开发的应用,因此这个模板是我们自己为我们的应用准备的。配置数据以文件的形式存储,但同时在使用文本引擎做模板和配置合并时,也可以接受参数作为配置。
Q19:什么是 CI 和 CD?
A : CI 更多是偏向应用编译、代码检查、单元测试等动作,CD 是偏向于应用部署、运行流程,我们的开发过程在编译打包完成后,实际也会将应用跑起来用于测试,也可以算是针对测试的 CD。
Q20:使用容器打包 Jenkins 流程的主要收益是什么?
A : 由于不同程序对于编译环境的依赖各有不同,原有使用 Jenkins 方法是在 Jenkins Node 上完成环境准备,现在可以利用容器完成环境准备,对于 Jenkins Node 的依赖可以进一步降低。同时环境变更也可以由开发人员自行控制。
Q21:多编译环境是用的不同镜像么?如何处理 pipeline 处理编译环境的问题?
A : 是的。由于我们本身产品开发各个模块有各个模块的开发语言和框架,因此各模块都要维护自身的编译环境镜像。使用 Pipeline 在进行编译时,是通过使用镜像运行容器然后在容器内编译的方式来使用编译环境的。
Q22:产品发布的产出为镜像,如果有很多镜像,最终会在镜像上打上统一的 Tag 么代码上的 Tag 什么时机打呢?
A : 我们是在产品发布时, 进行镜像复制时统一打发布 Tag 的,这时候从开发测试库会复制到发布库,利用了我们公司自身镜像库产品的镜像复制能力。
Q23:请问 Jenkins 也是部署在 Docker 里面的吗?如果 Jenkins 在 Docker 里面怎么样在 Docker 里面使用 Docker 执行 CI?
A : 是的,我们也在摸索将 Jenkins 本身放到容器中运行。在这种情况下,Jenkins 容器内使用 Root 权限,挂载 Docker.Sock 和 Docker 数据目录到容器中就可以了。
Q24:使用 Pipeline 先构建编译环境镜像,再编译,是否会导致整个流程需要很长时间?是否有优化方案?
A : 编译镜像由于不会经常变动,因此这个镜像的构建通常使用 Cache 就能直接完成。另外我们也把编译环境镜像打包这个步骤抽出来单独作为 Job 执行了,这样在实际编译流程中就无需再进行编译环境构建。
Q25:直接使用 Jenkins 的 Docker 插件和直接调用脚本,自己写 Docker 命令,如何在灵活程度和方便程度上做权衡?
A : 看个人或开发团队的自行权衡吧,只是开发团队内最好统一,便于进行相应维护。
Q26:Jenkins 和 Kubernetes 的用户是怎么管理的?我的期望是用户只能看到自己得资源,别的用户是没有权限的。
A : 我们本身只是使用这两种工具,在开发环境中不对外,所有不存在用户管理的问题。在我们公司正在开发的 CICD 产品中,对这块有我们自身理解基础上的设计。
Q27:Jenkins 的持续集成是怎么实现的?比如不同的源码仓库的提交触发,如 Github gitlab, 版本号怎么控制的?
A : Jenkins 的 CI 流程触发可以有很多种,代码提交触发、定时触发、手动触发。版本号的控制也可以有很多方案,比如使用 Job 的编号、使用 Git 的 Commit 号、使用时间戳等等。
Q28:Jerkins 调用 K8s API 用了什么插件么?
A : 没有直接使用 Jenkins 调用 Kubernetes API。
Q29:Jenkins 的 Pipeline 支持流水线执行,一般从头开始执行,可以从流水线中任意一步执行吗?
A : 因为我们团队的 Jenkinsfile 都是直接放置在代码库内的,因此每次执行都是从头完成流水线的执行。
Q30:容器化后发布也要通过 Jenkins,感觉 Docker 的发布没有 Jenkins 方便,除了容器化的可移植,还有什么原因值得推进项目容器化?
A : 应用容器化,其实更多的是看重应用在容器管理平台上运行起来后所获得的能力,例如在 Kubernetes 上运行后的水平扩展,服务发现,滚动升级,等等。
Q31:K8s Update 需要制定新的镜像才能做滚服更新 (升级),如果只是更新了 Configmap,有办法做滚服更新吗?
A : 我们的 CI 流程完成后,各模块的镜像 Tag 会发生变化,我们利用具体生成的 Tag 生成配置,然后部署的 Yaml 文件写为模板,镜像的具体 Tag 会根据每次 CI 流程生成的配置不同而组合为不同的 Yaml 文件,然后使用组合后的 Yaml,即 Tag 已经变更为最新版本的 Yaml 文件进行应用的滚动升级。
Q32:Pipeline 采用在镜像里构建的方案,是怎么实现的?用 Jenkins 现成的插件 or 用 Groovy 脚本实现?
A : 使用了 Jenkins 的 Docker 插件,同时使用方式是将相应命令写在 Groovy 脚本里。
例如:stage(Build){ docker.image( golang:1.7).inside { sh ./script/build.sh } }
到此,相信大家对“基于 Jenkins 和 Kubernetes 的 CI 工作流怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!