介绍
前面的两篇文章介绍了pipeline的基本使用和一些实际使用的例子,看似很不错,但是在实际应用也会出现很多的不足和问题,随之系统的庞大、服务的增加、人员的参差不齐会导致很多的问题。届时会带来很大的维护成本和一些改动,所以我们在做事情之前就要考虑进去,一些意外事件的发生、或者是在将来即将会发生和需要改变的事情我们都要想到或者是预留口子,这样才在今后扩展、修改、引入都能有很好可塑性。
jenkins job介绍
大多数情况下我们都是使用jenkins的普通job,普通的job好处是配置简单,结构化可以复杂,也可以单一。在使用jenkins job的时候我们分为两种:一种是单一job,一种是具有耦合性的。下面对两种情况进行对比和比较。
jenkins 单一job
在jenkins的传统模式下,单一的的job可以让维护人员可以很好的查看里面的逻辑步骤,job里面所有的任务都在这个所属的空间里面执行,它里面包含了:代码pull、编译、打包、复制包、发布包(使用内置的shell模块来写shell,这种应该不存在)。种单一job服务算得上是服务周到,不影响其他人,自己管理好自己的一亩三分地。好处是当出错以后影响范围小,容易控制。如下图:
在这中模式下,维护人员前期用看似比较轻松的工作建立起了整个发布流程。但是到了后期就不行了。之前我在的这家公司前期也是这么这么做的。开发完成后提交git,然后自动触发、构建、制品库、发布,在一个job里面就完成了。后来我们准备推行更好的devops方案的时候;发现以前的这个job建立有问题,一想到几百个微服务,几百个job需要去进行改造。顿时我们运维脸线一黑,虽然我们自己写了一个快速在jenkins上建立job,但是一想到几百个还是不好。为了解决这个问题,我们使用了job之间的任务关联,然后通过参数传递完成整个流程服务。
这种模式下的弊端就如上面所说的一样,但什么时候好的服务呢?好的服务又是什么样子的呢?这里也可以嵌套一些微服务的概念理论。如果我们要做到什么时候好的服务,我们得了解了解一下: 低耦合和高内聚。了解这个东西有助于我们在接下来的pipeline 流水线的设计,包括在后期devops的设计以及撸码都有很大的帮助。
耦合性
首先我们来了解这一概念: “高内聚低耦合”。在软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则是高内聚低耦合。从模块粒度来看,高内聚:尽可能类的每个成员方法只完成一件事(最大限度的聚合);低耦合:减少类内部,一个成员方法调用另一个成员方法。从类角度来看,高内聚低耦合:减少类内部,对其他类的调用;从功能块来看,高内聚低耦合:减少模块之间的交互复杂度(接口数量,参数数据)即横向:类与类之间、模块与模块之间;纵向:层次之间;尽可能,内容内聚,数据耦合。
低耦合
不同模块相互依赖多少?模块应尽可能独立于其他模块,以使对模块的更改不会严重影响其他模块。
高耦合
高耦合将意味着您的模块对其他模块的内部运作了解太多。对其他模块了解太多的模块会使更改难以协调,并使模块能力变弱。如果模块A对模块B的了解过多,则对模块B内部的更改可能会破坏模块A的功能。
通过实现低耦合,可以轻松更改模块内部,不必担心它们对系统中其他模块的影响。低耦合还使我们的模块彼此之间不相互依赖,因此更易于设计,编写和测试代码。我们还获得了易于重用和可组合的模块的优势。问题也被隔离到小的,独立的代码单元中。
好处:
- 可维护性: 更改限制在一个模块中
- 可测试性: 单元测试中涉及的模块可以限制在最低限度
- 可读性: 需要分析的类减少
高内聚
内聚性通常是指模块的元素如何相互组合。相关代码应彼此接近,以使其具有高度的凝聚力。易于维护的代码通常具有很高的内聚性。模块中的元素与该模块要提供的功能直接相关。如果需要修改一个功能,最好是在一个地方进行修改,然后可以尽快的发布。如果很多不同的地方要进行修改,就有可能需要发布多个微服务才能交互这个功能。在很多地方进行修改,不仅修改速度很慢,同时部署多个微服务也提高了风险。所以在找到问题域的边界域后可以确保相关的行为能放在同一个地方,并且它们会和其它边界以尽量低耦合的形式进行通信。
好处:
- 可读性: 功能包含在单个模块中
- 可维护性: 调试往往包含在单个模块中
- 可重用性: 具有集中功能不会被无用的干扰
内聚性低意味着组成某些功能的代码会散布在您的整个代码库中。不仅很难发现与您的模块相关的代码,而且很难在不同的模块之间跳转并跟踪的所有代码。
通俗的来讲,内聚是从功能角度来度量模块内的联系,好的内聚模块应恰好做一件事。描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块点以及通过接口的数据。
可维护的代码
一般在编写可维护的代码有助于提高开发人员的生产力。具有高度可维护的代码使设计新功能和编写代码变得更加容易。模块化,基于组件的分层代码可提高生产率并降低进行更改时的风险。通过使代码保持松散耦合,可以在一个模块内编写代码,而不会影响其他模块。通过保持代码的内聚性,我们可以更轻松地编写易于使用的DRY代码。
问题: 当我们遇到问题时,请评估修复、修改程序的程度。是更改一个模块,还是更改分散在整个系统中?在进行更改时,它是否可以解决所有的问题,还是会产生其他一些不可预知的问题?
在编写和使用代码库时:
- 我要修复和创建的此功能模块是多少?
- 此更改是要在几个不同的地方进行?
- 我能否独立测试代码,测试整个代码有多难?
- 我们是否可以使代码更松散地耦合来改善?可以使用高内聚来改善我们的代码吗?
Jenkins 设计
有了上面的的理论与概念。根据这里理论和概念我们就可以设计出一套更好的devops流程。本文将kubernetes平台上来做这一套设计,并在实际的环境中应用。涉及的功能如下: 服务 Job、Code Job、Release、Notice四个功能任务。
每一个环境有错误,就会执行告警任务模块,告警目前使用的是企业微信。job之间需要传递JOB_NAME,env,version三个参数。在之前的devops设计里面整个job的调用设计还要多。形成了一个通用体系。在这个设计里面,当还需要增加一个任务流程,我们只需要修改pipeline,然后增加一个job,在下次构建的时候就会把我们新增加的流程给加进去,非常的方便。设计图如下:
Project Name
此job一般就是服务,job名称以服务的名称进行命名。里面包含了四个功能.
- Clone Code: clone 代码。
- Build Code: 就是对开发提交的代码进行编译。
- Env Version: 获取本次提交的hash,以hash为版本,结合环境来做一个版本记录,这里需要进行判断。uat/prod环境不需要env前缀。
- Build Docker: 把编译完成后的二进制文件,打包成一个docker镜像。
Code Test
用于测试进行对代码的自动化测试;自动化流程、性能等测试
Release
主要是进行发布服务。当接受到上游job传递来的参数信息后,结合参数信息来进行对应的发布到kubernetes中namespace中,主要包含了以下功能
- Push Docker: 把前面打包的docker镜像推送到harbor
- Edit Files: 修改发布的脚本
- Release: 执行
kubectl
进行发布- 当发布到kubernetes中,kubernetes 会执行health检测,如果启动失败,会进行通知
Notice
此job主要用于通知。当接受到规则的告警通知以后,就会进行触发通知相关的人员。