首页 云计算

Tekton 与 Argo CD 结合实现 GitOps

前面我们使用 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  

官方微博/微信

每日头条、业界资讯、热点资讯、八卦爆料,全天跟踪微博播报。各种爆料、内幕、花边、资讯一网打尽。百万互联网粉丝互动参与,TechWeb官方微博期待您的关注。

↑扫描二维码

想在手机上看科技资讯和科技八卦吗?

想第一时间看独家爆料和深度报道吗?

请关注TechWeb官方微信公众帐号:

1.用手机扫左侧二维码;

2.在添加朋友里,搜索关注TechWeb。

手机游戏更多