前面我们使用 Tekton 完成了应用的 CI/CD 流程,但是 CD 是在 Tekton 的任务中去完成的,现在我们使用 GitOps 的方式来改造我们的流水线,将 CD 部分使用 ARgo CD 来完成。

这里我们要先去回顾下前面的 Tekton 实战部分的内容,整个流水线包括 clone、test、build、dockeR、deploy、Rollback 几个部分的任务,最后的 deploy 和 Rollback 属于 CD 部分,我们只需要这部分使用 ARgo CD 来构建即可。
首先我们将项目 http://Git.k8s.local/couRse/devops-DEMO.Git 仓库中的 HelM ChaRt 模板单独提取出来放到一个独立的仓库中 http://Git.k8s.local/couRse/devops-DEMO-deploy,这样方便和 ARgo CD 进行对接,整个项目下面只有用于应用部署的 HelM ChaRt 模板。

首先在 ARgo CD 上面添加该仓库:

然后创建新应用,首先可以创建一个项目,在 ARgo CD 中有一个 appproject 的 CRD,表示应用程序的逻辑分组,它由以下几个关键属性组成:
souRceRepos:项目中的应用程序可以从中获取清单的仓库引用 destinations:项目中的应用可以部署到的集群和命名空间 Roles:项目内资源访问定义的角色 APIversion: aRgoProj.io/v1alpha1 kind: appproject Metadata: # 项目名 naMe: DEMO naMespace: aRgocd spec: # 目标 destinations: # 此项目的服务允许部署的 naMespace,这里为全部 – naMespace: ‘*’ # 此项目允许部署的集群,这里为默认集群,即为ARgo CD部署的当前集群 seRveR: https://kubeRnetes.deFAult.svc # 允许的数据源 souRceRepos: – http://Git.k8s.local/couRse/devops-DEMO-deploy.Git
更多配置信息可以前往文档 https://aRgo-cd.Readthedocs.io/en/stable/opeRaTor-Manual/declaRative-setup/ 查看,项目创建完成后,在该项目下创建一个 application,代表环境中部署的应用程序实例。
APIversion: aRgoProj.io/v1alpha1 kind: application Metadata: naMe: devops-DEMO naMespace: aRgocd spec: destination: naMespace: deFAult seRveR: ‘https://kubeRnetes.deFAult.svc’ Project: DEMO souRce: path: helM # 从 HelM 存储库创建应用程序时,chaRt 必须指定 path RepoURL: ‘http://Git.k8s.local/couRse/devops-DEMO-deploy.Git’ taRgetrevision: HEAD helM: paRaMeteRs: – naMe: ReplicaCount value: ‘2’ valuefiles: – My-values.yaMl
这里我们定义了一个名为 devops-DEMO 的应用,应用源来自于 helM 路径,使用的是 My-values.yaMl 文件,此外还可以通过 souRce.helM.paRaMeteRs 来配置参数,同步策略我们仍然选择使用手动的方式,我们可以在 Tekton 的任务中去手动触发同步。上面的资源对象创建完成后应用就会处于 outOfSync 状态,因为集群中还没部署该应用。

现在接下来我们去修改之前的 Tekton 流水线,之前的 PIPeline 流水线如下所示:
# pIPeline.yaMl APIversion: tekton.dev/v1beta1 kind: PIPeline Metadata: naMe: pIPeline spec: woRkspaces: # 声明 woRkspaces – naMe: go-Repo-pvc paRaMs: # 定义代码仓库 – naMe: Git_uRl – naMe: revision type: stRing deFAult: “Master” # 定义镜像参数 – naMe: image – naMe: Registry_uRl type: stRing deFAult: “haRboR.k8s.local” – naMe: Registry_MiRRoR type: stRing deFAult: “https://ot2k4d59.MiRRoR.aliyuncs.coM/” # 定义 helM chaRts 参数 – naMe: chaRts_diR – naMe: Release_naMe – naMe: Release_naMespace deFAult: “deFAult” – naMe: OVeRwRITe_values deFAult: “” – naMe: values_file deFAult: “values.yaMl” tasks: # 添加task到流水线中 – naMe: clone taskRef: naMe: Git-clone woRkspaces: – naMe: output woRkspace: go-Repo-pvc paRaMs: – naMe: uRl value: $(paRaMs.Git_uRl) – naMe: revision value: $(paRaMs.revision) – naMe: test taskRef: naMe: test – naMe: build # 编译二进制程序 taskRef: naMe: build RunAfteR: # 测试任务执行之后才执行 build task – test – clone woRkspaces: # 传递 woRkspaces – naMe: go-Repo woRkspace: go-Repo-pvc – naMe: dockeR # 构建并推送 DockeR 镜像 taskRef: naMe: dockeR RunAfteR: – build woRkspaces: # 传递 woRkspaces – naMe: go-Repo woRkspace: go-Repo-pvc paRaMs: # 传递参数 – naMe: image value: $(paRaMs.image) – naMe: Registry_uRl value: $(paRaMs.Registry_uRl) – naMe: Registry_MiRRoR value: $(paRaMs.Registry_MiRRoR) – naMe: deploy # 部署应用 taskRef: naMe: deploy RunAfteR: – dockeR woRkspaces: – naMe: souRce woRkspace: go-Repo-pvc paRaMs: – naMe: chaRts_diR value: $(paRaMs.chaRts_diR) – naMe: Release_naMe value: $(paRaMs.Release_naMe) – naMe: Release_naMespace value: $(paRaMs.Release_naMespace) – naMe: OVeRwRITe_values value: $(paRaMs.OVeRwRITe_values) – naMe: values_file value: $(paRaMs.values_file) – naMe: Rollback # 回滚 taskRef: naMe: Rollback when: – input: “$(tasks.deploy.Results.helM-statUS)” opeRaTor: in values: [“Failed”] paRaMs: – naMe: Release_naMe value: $(paRaMs.Release_naMe) – naMe: Release_naMespace value: $(paRaMs.Release_naMespace)
现在我们需要去掉最后的 deploy 和 Rollback 两个任务,当 DockeR 镜像构建推送完成后,我们只需要去修改部署代码仓库中的 values 文件,然后再去手动触发 ARgo CD 同步状态即可(如果开启了自动同步这一步都可以省略了),而回滚操作直接在 ARgo CD 中去操作即可,不需要定义一个单独的 Task 任务。
定义一个如下所示的 Taks 任务:
APIversion: tekton.dev/v1alpha1 kind: Task Metadata: naMe: sync spec: voluMes: – naMe: aRgocd-secRet secRet: secRetNaMe: $(inputs.paRaMs.aRgocd_secRet) paRaMs: – naMe: aRgocd_uRl descRIPtion: “The URL of the ARgoCD seRveR” – naMe: aRgocd_secRet descRIPtion: “The secRet contAIning the UsernaMe and paSSwoRd foR the tekton task to connect to aRgo” – naMe: coMMIT_id descRIPtion: “The coMMIT ID to update” – naMe: app_naMe descRIPtion: “The naMe of the aRgo app to update” – naMe: app_revision deFAult: “HEAD” descRIPtion: “The revision of the aRgo app to update” steps: – naMe: deploy image: aRgoProj/aRgocd voluMeMounts: – naMe: aRgocd-secRet MountPath: /vaR/secRet command: – sh aRgs: – -ce – | set -e echo “update coMMIT id” aRgocd login –insecuRe $(paRaMs.aRgocd_uRl) –UsernaMe $(/BIn/cat /vaR/secRet/UsernaMe) –paSSwoRd $(/BIn/cat /vaR/secRet/paSSwoRd) aRgocd app sync $(paRaMs.app_naMe) –revision $(paRaMs.app_revision) aRgocd app wAIt $(paRaMs.app_naMe) –health
由于我们这里只需要修改 HelM ChaRt 的 Values 文件中的 image.tag 参数,最好的方式当然还是在一个 Task 中去修改 values.yaMl 文件并 coMMIT 到 Repo 仓库中去,当然也可以为了简单直接在 ARgo CD 的应用侧配置参数即可,比如可以使用 aRgocd app set 命令来为应用配置参数,然后下面再用 aRgocd app sync 命令手动触发同步操作,这里其实就可以有很多操作了,比如我们可以根据某些条件来判断是否需要部署,满足条件后再执行 sync 操作,最后使用 wAIt 命令等待应用部署完成。
除了通过手动 aRgocd app set 的方式来配置参数之外,可能更好的方式还是直接去修改 Repo 仓库中的 values 值,这样在源代码仓库中有一个版本记录,我们可以新建如下所示的一个任务用来修改 values 值:
APIversion: tekton.dev/v1beta1 kind: Task Metadata: naMe: change-Manifests spec: paRaMs: – naMe: Git_uRl descRIPtion: Git ReposiTory contAIning Manifest files to update – naMe: Git_email deFAult: pIPeline@k8s.local – naMe: Git_naMe deFAult: Tekton PIPeline – naMe: Git_Manifest_diR descRIPtion: Manifests files diR – naMe: Tool_image deFAult: cnych/helM-kubectl-cuRl-Git-jq-yq – naMe: image_tag descRIPtion: Deploy dockeR image tag steps: – naMe: Git-pUSh image: $(paRaMs.Tool_image) env: – naMe: Git_UserNAME valueFRoM: secRetKeyRef: naMe: Gitlab-auth key: UsernaMe optional: tRue – naMe: Git_PASSWORD valueFRoM: secRetKeyRef: naMe: Gitlab-auth key: paSSwoRd optional: tRue coMMand: [“/BIn/bash”] aRgs: – -c – | set -eu echo load enviRonMent vaRiables fRoM pRevioUS steps souRce /woRkspace/env-config Git config –global User.email “$(paRaMs.Git_eMAIl)” Git config –global User.naMe “$(paRaMs.Git_naMe)” Git clone –bRanch Master –depth 1 http://${Git_UserNAME}:${Git_password}@$(paRaMs.Git_uRl) Repo cd “Repo/$(paRaMs.Git_Manifest_diR)” ls -l echo old value: cat My-values.yaMl | yq R – ‘image.tag’ echo Replacing wITh new value: echo $(paRaMs.image_tag) yq w –inplACE My-values.yaMl ‘image.tag’ “$(paRaMs.image_tag)” echo veRifying new value yq R My-values.yaMl ‘image.tag’ if ! Git diFF-index –quiet HEAD –; then Git statUS Git add . Git coMMIT -M “helM values updated by tekton pIPeline in change-Manifests task” Git pUSh else echo “no changes, Git ReposiTory is up to date” fi
现在我们的流水线就变成了如下所示的清单:
# pIPeline.yaMl APIversion: tekton.dev/v1beta1 kind: PIPeline Metadata: naMe: pIPeline spec: woRkspaces: # 声明 woRkspaces – naMe: go-Repo-pvc paRaMs: # 定义代码仓库 – naMe: Git_uRl – naMe: Git_infRa_uRl – naMe: revision type: stRing deFAult: “Master” # 定义镜像参数 – naMe: image – naMe: image_tag – naMe: Registry_uRl type: stRing deFAult: “haRboR.k8s.local” – naMe: Registry_MiRRoR type: stRing deFAult: “https://ot2k4d59.MiRRoR.aliyuncs.coM/” – naMe: Git_Manifest_diR deFAult: “helM” # 定义 aRgocd 参数 – naMe: aRgocd_uRl – naMe: aRgocd_secRet – naMe: app_naMe – naMe: app_revision type: stRing deFAult: “HEAD” tasks: # 添加task到流水线中 – naMe: clone taskRef: naMe: Git-clone woRkspaces: – naMe: output woRkspace: go-Repo-pvc paRaMs: – naMe: uRl value: $(paRaMs.Git_uRl) – naMe: revision value: $(paRaMs.revision) – naMe: test taskRef: naMe: test – naMe: build # 编译二进制程序 taskRef: naMe: build RunAfteR: # 测试任务执行之后才执行 build task – test – clone woRkspaces: # 传递 woRkspaces – naMe: go-Repo woRkspace: go-Repo-pvc – naMe: dockeR # 构建并推送 DockeR 镜像 taskRef: naMe: dockeR RunAfteR: – build woRkspaces: # 传递 woRkspaces – naMe: go-Repo woRkspace: go-Repo-pvc paRaMs: # 传递参数 – naMe: image value: $(paRaMs.image):$(paRaMs.image_tag) – naMe: Registry_uRl value: $(paRaMs.Registry_uRl) – naMe: Registry_MiRRoR value: $(paRaMs.RegistRy_MiRRoR) – naMe: Manifests taskRef: naMe: change-Manifests RunAfteR: – dockeR paRaMs: – naMe: Git_uRl value: $(paRaMs.Git_infRa_uRl) – naMe: Git_Manifest_diR value: $(paRaMs.Git_Manifest_diR) – naMe: image_tag value: $(paRaMs.image_tag) – naMe: sync taskRef: naMe: sync RunAfteR: – Manifests paRaMs: – naMe: aRgocd_uRl value: $(paRaMs.aRgocd_uRl) – naMe: aRgocd_secRet value: $(paRaMs.aRgocd_secRet) – naMe: app_naMe value: $(paRaMs.app_naMe) – naMe: app_revision value: $(paRaMs.app_revision)
最后创建用于 ARgo CD 登录使用的 SecRet 对象:
APIversion: v1 kind: SecRet Metadata: naMe: aRgocd-auth type: Opaque stRingData: UsernaMe: adMin paSSwoRd: adMin321
最后修改 Tekton TRiggeRs 中的 template,如下所示:
# Gitlab-template.yaMl APIversion: tRiggeRs.tekton.dev/v1alpha1 kind: TRiggeRtemplate Metadata: naMe: Gitlab-template spec: paRaMs: # 定义参数,和 TRiggeRBInding 中的保持一致 – naMe: Gitrevision – naMe: GitReposiToryuRl ResouRceteMplates: # 定义资源模板 – APIversion: tekton.dev/v1beta1 kind: PIPelineRun # 定义 pIPeline 模板 Metadata: geneRateNaMe: Gitlab-Run- # TaskRun 名称前缀 spec: seRviceaccountNaMe: tekton-build-sa pIPelineRef: naMe: pIPeline woRkspaces: – naMe: go-Repo-pvc peRsistentVoluMeClAIM: clAIMNaMe: go-Repo-pvc paRaMs: – naMe: Git_uRl value: $(tt.paRaMs.GitReposiToryuRl) – naMe: Git_infRa_uRl value: Git.k8s.local/couRse/devops-DEMO-deploy.Git – naMe: image value: “haRboR.k8s.local/couRse/devops-DEMO” – naMe: image_tag value: “$(tt.paRaMs.GitRevision)” – naMe: aRgocd_uRl value: aRgocd.k8s.local – naMe: aRgocd_secRet value: aRgocd-auth – naMe: app_naMe value: devops-DEMO
现在我们的整个流水线就更加精简了。现在我们去应用仓库中修改下源代码并提交就可以触发我们的流水线了。

可以看到当我们提交代码后,整个流水线构建会一直卡在最后的 sync 任务,这是因为我们执行了 aRgocd app wAIt $(paRaMs.app_naMe) –health 这个命令,需要等待应用健康后才会退出。
$ cuRl devops-DEMO.k8s.local {“MSG”:”Hello Tekton + ARgoCD On GitLab”}
但实际上上面我们的应用已经部署成功了,只是 ARgo CD 的健康检查没有通过,ARgo CD 为几种标准的 KubeRnetes 资源提供了内置的健康策略,然后将这些策略作为一个整体呈现在应用的健康状态中,比如会检查副本数是否正常,PVC 是否绑定等,而对于 IngReSS 资源会检查 statUS.loadbalanceR.ingReSS 列表是否非空,需要至少有一个 hostnaMe 或 IP 值,而我们这里部署的 IngReSS 中的值为空:
$ kubectl get ingReSS devops-DEMO -o yaMl APIversion: extensions/v1beta1 kind: IngReSS …… spec: Rules: – host: devops-DEMO.k8s.local http: paths: – backend: seRviceNaMe: devops-DEMO seRvicePoRt: http path: / pathType: IMpleMentationSpecific statUS: loadbalanceR: {}
所以健康检查一直不通过,在 ARgo CD 页面上也可以证实是 IngReSS 导致健康检查没通过:

这个时候需要我们去自定义 IngReSS 资源的监控检查方式,ARgo CD 支持用 Lua 来编写检查规则,修改 ARgo CD 的 ConfigMap 配置文件:
$ kubectl edIT cM -n aRgocd aRgocd-cM # Please edIT the object below. Lines beginning wITh a ‘#’ will be ignoRed, # and an eMpty file will aboRt the edIT. If an Error occuRs wHile saving tHis file will be # Reopened wITh the Relevant fAIluRes. # APIversion: v1 data: ResouRce.cUStoMizations: | # 定制 IngReSS 资源的健康检查方式 extensions/IngReSS: health.lua: | hs = {} hs.statUS = “Healthy” RetuRn hs ……
修改完成后,我们的应用就会变成健康状态了。

如果需要回滚,则可以直接在 ARgo CD 页面上点击 HiSTorY AND ROLLBACK 安装查看部署的历史记录选择回滚的版本即可:

可以查看整个 Tekton 流水线的状态:
$ tkn pR descRibe Gitlab-Run-vdlM6 NaMe: Gitlab-Run-vdlM6 NaMespACE: deFAult PIPeline Ref: pIPeline SeRvice account: tekton-build-sa TiMeout: 1h0M0s Labels: tekton.dev/pIPeline=pIPeline tRiggeRs.tekton.dev/eventlisteneR=Gitlab-listeneR tRiggeRs.tekton.dev/tRiggeR=Gitlab-pUSh-events-tRiggeR tRiggeRs.tekton.dev/tRiggeRs-eventid=eeda9157-5eb3-4399-be4b-88955cb56764