自动化运维|云原生架构下的产品自动化发布、快速部署和持续交付实战之路。

1.背景介绍
CI/CD是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。作为一种面向开发和运维团队的解决方案,CI/CD 主要针对在集成新代码时所引发的问题(亦称:“集成地狱”)。CI/CD创建了一个可重复的、可靠的且可预见的发布流程,从而大大缩短了发布周期,不仅节省下了巨大的金钱成本,还节省了包括建立和维护这样一个发布系统所需要的时间投入。
在引入CI/CD技术之前,公司测试人员自动化打包主要依赖Jenkins实现,在配置任务的源码、构建触发器、构建环境、构建、构建后操作等步骤后,可以触发构建任务。相比传统的拉代码、运行命令打包、整理上传更新文件,重启服务等繁琐的操作,Jenkins任务构建这种方式在一定程度上节省了测试和研发人员的时间。但随着代码仓库数量的日益增长,以及Jenkins在公司内部使用程度越来越深,一些问题也逐渐暴露。例如:

每一套代码或服务都需要繁琐的job配置工作,且大部分是重复劳动。

研发人员和测试人员都在使用Jenkins,需要一定的学习成本。

缺少打包结果通知,不能及时获取打包结果。

构建完成后需要手动下载归档文件,并整理后创建更新任务。

公用测试环境,频繁更新重启服务(特别是封版期间)导致服务中断的问题。

为了解决以上问题,我们使用了Kubernetes 原生 CI/CD 构建框架Tekton和基于Kubernetes的声明式持续交付工具Argo CD,并将流水线接入项目管理平台Redmine,简化了代码打包步骤、降低了使用人员的学习成本、提供稳定的测试环境、自动粘贴归档文件下载链接、自动创建更新任务等,进一步提高了持续集成的效率。

  1. 系统设计
    流水线系统设计以项目管理平台为入口,Kubernetes为环境基础,使用Tekton完成CI部分功能,在构建完成后更新服务编排文件。Argo CD则负责持续监控应用状态,并更新应用。


2.1Kubernetes简介
Kubernetes是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署、规划、更新、维护的一种机制。

Kubernetes一个核心的特点就是能够自主的管理容器来保证云平台中的容器按照用户的期望状态运行着(比如用户想让apache一直运行,用户不需要关心怎么去做,Kubernetes会自动去监控,然后去重启,新建,总之,让apache一直提供服务),管理员可以加载一个微型服务,让规划器来找到合适的位置,同时,Kubernetes也系统提升工具以及人性化方面,让用户能够方便的部署自己的应用(就像canary deployments)。

2.2Tekton简介
Tekton 是一个基于 Kubernetes 的云原生 CI/CD 开源框架,属于 CD 基金会的项目之一。Tekton 通过定义 CRD 的方式,让用户以灵活的自定义流水线以满足自身 CI/CD 需求。

Tekton 最主要的四个概念为:Task、TaskRun、Pipeline 以及 PipelineRun。

Task: Task 为构建任务,是 Tekton 中不可分割的最小单位,正如同 Pod 在 Kubernetes 中的概念一样。在 Task 中,可以有多个 Step,每个 Step 由一个 Container 来执行。

Pipeline: Pipeline 由一个或多个 Task 组成。在 Pipeline 中,用户可以定义这些 Task 的执行顺序以及依赖关系来组成 DAG(有向无环图)。

PipelineRun: PipelineRun 是 Pipeline 的实际执行产物,当用户定义好 Pipeline 后,可以通过创建 PipelineRun 的方式来执行流水线,并生成一条流水线记录。

TaskRun: PipelineRun 被创建出来后,会对应 Pipeline 里面的 Task 创建各自的 TaskRun。一个 TaskRun 控制一个 Pod,Task 中的 Step 对应 Pod 中的 Container。当然,TaskRun 也可以单独被创建。

综上:Pipeline 由多个 Task 组成,每次执行对应生成一条 PipelineRun,其控制的 TaskRun 将创建实际运行的 Pod。


2.3Argo CD 简介
Argo CD 是一个为 Kubernetes 而生的,遵循声明式 GitOps 理念的持续部署(CD)工具,它的配置和使用非常简单,并且自带一个简单易用的 Dashboard 页面,并且支持多种配置管理/模板工具(例如 Kustomize、Helm、Ksonnet、Jsonnet、plain-YAML)。

Argo CD 被实现为一个 Kubernetes 控制器,它持续监控应用的实际状态,周期性地拉取 Git 仓库中的配置清单,并将实际状态与期望状态进行比较,如果实际状态不符合期望状态,就会更新应用的实际状态以匹配期望状态。

Argo CD的主要功能:

可搭配使用各种配置管理工具(如 ksonnet/jsonnet、Helm 和 kustomize)使应用程序与 Git 中定义的保持一致。

将应用程序自动部署到指定的目标环境。

持续监控已部署的应用程序。

基于 Web 和 CLI 的操作,以及应用程序可视化。

部署或回滚到 Git 仓库中提交的应用程序的任何状态(这也是使用 Git 进行版本管理的一大好处。


2.4项目管理平台Redmine
政通项目管理平台是流水线使用的入口系统,在同步保存Gitlab仓库、分支、CI/CD参数后,新增流水线任务时将参数传给Tekton。流水线任务执行时,会将当前流水线信息推送到项目管理平台。

流水线打包成功后会将归档路径推送到项目管理平台。


新增流水线时如果勾选了创建更新任务,也会在流水线打包成功后自动创建更新任务。

新增流水线时选择运行测试环境,则会通过Argo CD更新测试服务。

流水线运行异常时,会将相关信息推送到项目管理平台,方便排查问题。

  1. 流水线的使用
    下面以wizdom-urban-v14代码为例,介绍流水线打包如何使用。

(1)步骤一:Gitlab参数配置。

tekton_pipeline_enable:是否启用流水线,如true。

tekton_pipeline_name:流水线名称,如“执法-vue前端”。

tekton_pipeline_extra_params:新增流水线页面额外显示的参数,多个参数逗号分隔。可选值:npm_build_params(插件名)。

tekton_pipeline_before_hook:仅用于多app前端仓库,提交记录中src/pages/后面一级目录作为打包参数。如:hook_plugins_by_src_pages、hook_plugins_by_src_views。流水线构建中如果需要使用更多参数,可以配置以“tekton_hook_param_”为前缀的参数名,流水线任务预处理会判断并截取这样的参数。

(2)步骤二:Redmine同步配置管理–项目管理–gitlab同步,同步仓库、分支、变量。代码仓库会在代码提交后自动同步。变量只需要在修改后同步1次即可。

(3)步骤三:新增提测单项目管理平台任务处理完成以后,点击任务右上角“生成提测”功能按钮,从列出的根据提交记录查询的代码仓库和分支列表中,选择需要使用的流水线打包的代码仓库和分支。


(4)步骤四:新增流水线。以上步骤完成后,就来到了最关键最常用的新增流水线环节,在任务界面点击右上角“新增流水线”功能按钮,填写相关信息。

后台会根据提测单自动填充部分内容,如有调整,可手动修改。

参数说明如下:

选择流水线:根据提测单代码仓库自动选择流水线类型。

开始打包时发送通知:根据钉钉token给测试人员(成功或失败时)和对应的开发人员发送通知(失败时),一般默认都勾选。

是否生成更新任务:用于生成当前任务的子系统,提供给测试人员发更新使用。面向研发人员是隐藏状态。任务名称和内容自动生成,可以自行编辑。

是否运行测试环境:用于自动更新测试环境,默认不勾选,测试人员需要时勾选,结合“产品线”来判断更新哪一套环境。

测试环境目标版本(不填写时将在后端自动计算):运行测试环境的目标版本。

产品线:勾选运行测试环境后生效,用来判断更新哪一套测试环境。

归档类型:默认zip主代码分支:根据提测单自动生成。

项目代码分支:根据提测单自动生成。

数据库类型:默认为mysql。如果有oracle和dm时自行切换。

插件名:根据提测单自动生成,也可以手动填写,以逗号分隔。

归档的文件和路径(不含插件名对应的jar):主要是前端的文件,多个以逗号分隔。根据提测单自动生成,也可以手动修改。

额外归档的文件或路径:填写其他还需要打包时包含的文件或路径,多个以逗号分隔。

操作人:当前操作人,自动生成。

操作人钉钉token:当前操作人的钉钉token,自动生成,用来发送消息通知。

研发负责人(处理代码异常问题):根据“实际研发处理人”自动生成。为空时不显示此项。在流水线因代码问题打包失败时通知研发负责人。

大部分参数都不需要手动填写,只需要确认无误后点击创建,流水线任务即创建成功,等待流水线自动打包完成即可。打包完成或者因代码问题打包失败会通知操作人。

  1. 流水线任务处理流程
    我们从流水线的任务处理流程、流水线任务和步骤来说明流水线中的关键技术。

4.1任务接收
流水线任务是通过TektonTrigger接收的,Tekton Trigger是Tekton的一个组件,它可以从各种来源的事件中检测并提取需要信息,然后根据这些信息来运行TaskRun和PipelineRun,还可以将提取出来的信息传递给它们以满足不同的运行要求。其核心组件如下:

EventListener:事件监听器,是外部事件的入口 ,通常需要通过HTTP方式暴露,以便于外部事件推送,例如项目管理平台的新建流水线操作。

Trigger:指定当EventListener检测到事件发生时会发生什么,它会定义TriggerBinding、TriggerTemplate以及可选的Interceptor。

TriggerTemplate:用于模板化资源,根据传入的参数实例化Tekton对象资源,比如TaskRun、PipelineRun等。

TriggerBinding:用于捕获事件中的字段并将其存储为参数,然后会将参数传递给TriggerTemplate。

ClusterTriggerBinding:和TriggerBinding相似,用于提取事件字段,不过它是集群级别的对象。

Interceptor:拦截器,在TriggerBinding之前运行,用于负载过滤、验证、转换等处理,只有通过拦截器的数据才会传递给TriggerBinding。


Tekton Trigger在接收Redmine提交的流水线请求时,使用Interceptor验证请求、解析参数,包含了新增流水线请求RequestBody的值和根据代码仓库id查询的Gitlab代码仓库的CI/CD参数,根据参数值启动对应的PipelineRun,如智信h5打包的build-mobile-h5或build-mobile-h5-npm。

4.2任务执行
PipelineRun通过pipelineRef指定要运行的Pipeline,Pipeline中定义了多个Task,每个Task又包含一个或多个Step。 以wizdom-urban-v14代码的流水线为例,主要Task及其执行顺序如下:


copy-all-code:复制缓存的代码。

fetch-main-code:拉取主代码。

fetch-project-code:拉取项目定制代码。

profiles-resolve:解决profile定义问题,包括合并主框架代码和项目定制代码、解析要打包的profile、解决依赖定义问题。

maven-run:运行打包。

archive:打包结果归档。

dockerfile:处理war打包结构,生成DockerFile内容

docker-build:构建docker镜像,并将镜像推送到镜像仓库

fetch-argocd-app-code:拉取Argo CD配置文件代码仓库

branch-to-argocd-version:更新部署文件的镜像版本

mis-argocd-app:

push-argocd-app-code:将修改后的代码提交推送到Gitlab仓库。

argocd-task-sync-and-wait:触发Argo CD同步,并等待验证服务启动成功。

dingtalk-app-start-success:通知服务更新成功。

redmine-new-update-task:项目管理平台新建更新更新任务。

delete-files:删除临时文件。

message-dingtalk:构建结果通知钉钉通知。

message-dingtalk-without-task: 构建结果通知钉钉通知。

message-redmine:打包成功的推送redmine。

除了以上流水线打包的任务外,还在finally部分指定了多个最终任务(finally task),无论Tasks部分声明的常规任务执行成功还是报错,最终任务都会在常规任务执行完成后并行执行。


set-pipeline-run-status:更新项目管理平台任务状态。

notify-admin-onerror:固定错误通知管理员。

notify-pom-group-onerror:mvn打包失败发送给pom修正群。

notify-dev-onerror:mvn打包失败发送给研发通知。

redmine-comment-onerror:带有任务号的流水线推送打包错误内容到任务上。

redmine-comment-on-mvnerror:打包错误内容推送到任务上。

message-op-user-on-npmerror:打包错误内容消息给操作人。

message-op-user:消息通知操作人。

任务的执行顺序是根据runAfter来决定的,未声明runAfter的任务可以和其他任务并行执行。另外,只有when中声明的条件都满足时任务才会执行,否则会跳过。例如redmine-new-update-task任务,需要等待归档任务(archive)完成后才可能执行,并且只有当项目管理平台任务号不为空,且创建流水线任务时勾选了创建更新任务,才会在归档后创建项目管理平台更新任务。

name: redmine-new-update-taskretries: 1taskRef:name: redminerunAfter:- archivewhen:- input: "$(params.issue_id)"operator: notinvalues: [ "" ]- input: "$(params.new_update_issue_flag)"operator: invalues: [ "true" ]

4.3典型任务和步骤
Pipeline中的任务虽然看起来非常多,但是大部分是可复用的。另外,与Pipeline中声明的任务不同,Task中声明的步骤(step)是按照声明的顺序依次执行的。我们以典型的任务profiles-resolve为例来说明,任务步骤执行顺序如下:

各个步骤完成的内容为:

merge-code:合并主框架代码和项目定制代码。

profiles-resolve:解决profile定义不规范问题。

check-need-mvn:判断是否需要maven构建,后续任务需要使用,例如判断是否运行打包。

add-profiles-by-product:根据产品线追加profiles。

dependencies-fix:修复pom文件中传递依赖定义不完整问题。

war-exclude:排查war打包,只需要更新部分jar时,排斥war打包步骤能够加快打包速度。

fix-git-commit-id-plugin:解决git-commit-id-plugin插件问题。

fix-repository:由于内网maven镜像仓库地址有调整,修改pom中定义的maven镜像源。

fix-pom-commons-collections4:修复部分版本缺少commons-collections4。

profiles-resolve任务的很多额外步骤是为了解决代码代码不规范的问题,例如代码中profile定义不完整,直接执行maven clean package -P pluginName1,pluginName2,…时,往往不能成功。在profiles-resolve步骤中,通过多次循环补充当前profiles依赖的其他profiles。虽然直接修改代码也能解决这个问题,但是由于wizdom-urban-v14代码插件非常多,分支也非常多,改起来会相当耗时。并且由于插件之间依赖关系比较复杂,开发过程中难以维持正确的依赖关系定义。

4.4发布更新
由上文任务列表可知,服务更新其实也是包含在任务执行中的。我们借助Argo CD的自动同步和部署应用程序的优势,在打包完成后,制作docker镜像,并推送到镜像仓库。随后更新Argo CD部署文件代码,触发Argo CD同步,Argo CD检测到部署文件更新后,将自动与K8S交互,更新服务。K8S滚动更新的特性,能够在保证服务不中断的情况下完成升级。

总结起来,流水线的使用流程如下图:

5. 成果和计划
相比传统的Jenkins打包,流水线的优势在于:

省去了繁琐的构建任务配置,简化了启动流程的步骤。研发和测试只需要在任务平台上新增提测单,新增流水线即可,降低了学习成本。

利用了K8S滚动更新的特性,可以在服务不中断的情况下进行升级。

任务中灵活的脚本处理,能够在不改动代码的情况下,完成依赖修复,镜像仓库地址替换等。

任务中增加了异常处理,能够在打包失败时第一时间通知流水线操作人、研发处理人。并在项目管理平台任务重附上错误日志,便于即使排查解决问题。

流水线完成后自动发布归档文件下载链接,省去测试人员整理结果上传百度云的步骤,提高了工作效率。

自动创建更新任务,测试人员无需手动创建更新任务并粘贴更新文件下载链接。

截止目前,流水线平台已归纳支持12种类型的代码结构打包,主要包含了智云主框架、智云前端、智信前端、微服务、智云拆分代码、智信拆分代码等。项目管理平台接入流水线仅半年就完成了打包任务数3987,流水线任务总运行8140次。通过Argo CD管理一网统管、信息采集、城市大脑、基础平台、市政、执法、星桥、灵珑、社会治理、运管服10条产品线,共67套运行环境。


后续计划整合其它地区的服务器,全部虚拟化后,将各产品线测试环境全面接入流水线,研发、测试环境隔离互不干扰,做到可按需开启,定时关闭。利用流水线流程的易扩展性,接入单元测试、自动测试流程。