前面我们使用 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
- 🌡️ Status
- STARTED DURATION STATUS
- 4 minutes ago 2 minutes Succeeded
- 📦 Resources
- No resources
- ⚓ Params
- NAME VALUE
- ∙ git_url http://git.k8s.local/course/devops-demo.git
- ∙ git_infra_url git.k8s.local/course/devops-demo-deploy.git
- ∙ image harbor.k8s.local/course/devops-demo
- ∙ image_tag 332798d9e28422341fd64704ab9b54b944d77084
- ∙ argocd_url argocd.k8s.local
- ∙ argocd_secret argocd-auth
- ∙ app_name devops-demo
- 📝 Results
- No results
- 📂 Workspaces
- NAME SUB PATH WORKSPACE BINDING
- ∙ go-repo-pvc --- PersistentVolumeClaim (claimName=go-repo-pvc)
- 🗂 Taskruns
- NAME TASK NAME STARTED DURATION STATUS
- ∙ gitlab-run-vdlm6-sync-svmxl sync 3 minutes ago 42 seconds Succeeded
- ∙ gitlab-run-vdlm6-manifests-d297d manifests 3 minutes ago 26 seconds Succeeded
- ∙ gitlab-run-vdlm6-docker-g2tqx docker 4 minutes ago 48 seconds Succeeded
- ∙ gitlab-run-vdlm6-build-mkcrd build 4 minutes ago 9 seconds Succeeded
- ∙ gitlab-run-vdlm6-test-gjr4c test 4 minutes ago 4 seconds Succeeded
- ∙ gitlab-run-vdlm6-clone-57vpw clone 4 minutes ago 8 seconds Succeeded
最后用一张图来总结下我们使用 Tekton 结合 Argo CD 来实现 GitOps 的工作流: