Tekton 與 Argo CD 結(jié)合實(shí)現(xiàn) GitOps
前面我們使用 Tekton 完成了應(yīng)用的 CI/CD 流程,但是 CD 是在 Tekton 的任務(wù)中去完成的,現(xiàn)在我們使用 GitOps 的方式來改造我們的流水線,將 CD 部分使用 Argo CD 來完成。

這里我們要先去回顧下前面的 Tekton 實(shí)戰(zhàn)部分的內(nèi)容,整個(gè)流水線包括 clone、test、build、docker、deploy、rollback 幾個(gè)部分的任務(wù),最后的 deploy 和 rollback 屬于 CD 部分,我們只需要這部分使用 Argo CD 來構(gòu)建即可。
首先我們將項(xiàng)目 http://git.k8s.local/course/devops-demo.git 倉庫中的 Helm Chart 模板單獨(dú)提取出來放到一個(gè)獨(dú)立的倉庫中 http://git.k8s.local/course/devops-demo-deploy,這樣方便和 Argo CD 進(jìn)行對接,整個(gè)項(xiàng)目下面只有用于應(yīng)用部署的 Helm Chart 模板。

首先在 Argo CD 上面添加該倉庫:

然后創(chuàng)建新應(yīng)用,首先可以創(chuàng)建一個(gè)項(xiàng)目,在 Argo CD 中有一個(gè) AppProject 的 CRD,表示應(yīng)用程序的邏輯分組,它由以下幾個(gè)關(guān)鍵屬性組成:
sourceRepos:項(xiàng)目中的應(yīng)用程序可以從中獲取清單的倉庫引用destinations:項(xiàng)目中的應(yīng)用可以部署到的集群和命名空間roles:項(xiàng)目內(nèi)資源訪問定義的角色
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
# 項(xiàng)目名
name: demo
namespace: argocd
spec:
# 目標(biāo)
destinations:
# 此項(xiàng)目的服務(wù)允許部署的 namespace,這里為全部
- namespace: '*'
# 此項(xiàng)目允許部署的集群,這里為默認(rèn)集群,即為Argo CD部署的當(dāng)前集群
server: https://kubernetes.default.svc
# 允許的數(shù)據(jù)源
sourceRepos:
- http://git.k8s.local/course/devops-demo-deploy.git
更多配置信息可以前往文檔 https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/ 查看,項(xiàng)目創(chuàng)建完成后,在該項(xiàng)目下創(chuàng)建一個(gè) Application,代表環(huán)境中部署的應(yīng)用程序?qū)嵗?/p>
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 存儲(chǔ)庫創(chuàng)建應(yīng)用程序時(shí),chart 必須指定 path
repoURL: 'http://git.k8s.local/course/devops-demo-deploy.git'
targetRevision: HEAD
helm:
parameters:
- name: replicaCount
value: '2'
valueFiles:
- my-values.yaml
這里我們定義了一個(gè)名為 devops-demo 的應(yīng)用,應(yīng)用源來自于 helm 路徑,使用的是 my-values.yaml 文件,此外還可以通過 source.helm.parameters 來配置參數(shù),同步策略我們?nèi)匀贿x擇使用手動(dòng)的方式,我們可以在 Tekton 的任務(wù)中去手動(dòng)觸發(fā)同步。上面的資源對象創(chuàng)建完成后應(yīng)用就會(huì)處于 OutOfSync 狀態(tài),因?yàn)榧褐羞€沒部署該應(yīng)用。

現(xiàn)在接下來我們?nèi)バ薷闹暗?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"
# 定義鏡像參數(shù)
- name: image
- name: registry_url
type: string
default: "harbor.k8s.local"
- name: registry_mirror
type: string
default: "https://ot2k4d59.mirror.aliyuncs.com/"
# 定義 helm charts 參數(shù)
- 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 # 編譯二進(jìn)制程序
taskRef:
name: build
runAfter: # 測試任務(wù)執(zhí)行之后才執(zhí)行 build task
- test
- clone
workspaces: # 傳遞 workspaces
- name: go-repo
workspace: go-repo-pvc
- name: docker # 構(gòu)建并推送 Docker 鏡像
taskRef:
name: docker
runAfter:
- build
workspaces: # 傳遞 workspaces
- name: go-repo
workspace: go-repo-pvc
params: # 傳遞參數(shù)
- name: image
value: $(params.image)
- name: registry_url
value: $(params.registry_url)
- name: registry_mirror
value: $(params.registry_mirror)
- name: deploy # 部署應(yīng)用
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)
現(xiàn)在我們需要去掉最后的 deploy 和 rollback 兩個(gè)任務(wù),當(dāng) Docker 鏡像構(gòu)建推送完成后,我們只需要去修改部署代碼倉庫中的 values 文件,然后再去手動(dòng)觸發(fā) Argo CD 同步狀態(tài)即可(如果開啟了自動(dòng)同步這一步都可以省略了),而回滾操作直接在 Argo CD 中去操作即可,不需要定義一個(gè)單獨(dú)的 Task 任務(wù)。
定義一個(gè)如下所示的 Taks 任務(wù):
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 參數(shù),最好的方式當(dāng)然還是在一個(gè) Task 中去修改 values.yaml 文件并 commit 到 Repo 倉庫中去,當(dāng)然也可以為了簡單直接在 Argo CD 的應(yīng)用側(cè)配置參數(shù)即可,比如可以使用 argocd app set 命令來為應(yīng)用配置參數(shù),然后下面再用 argocd app sync 命令手動(dòng)觸發(fā)同步操作,這里其實(shí)就可以有很多操作了,比如我們可以根據(jù)某些條件來判斷是否需要部署,滿足條件后再執(zhí)行 sync 操作,最后使用 wait 命令等待應(yīng)用部署完成。
除了通過手動(dòng) argocd app set 的方式來配置參數(shù)之外,可能更好的方式還是直接去修改 Repo 倉庫中的 values 值,這樣在源代碼倉庫中有一個(gè)版本記錄,我們可以新建如下所示的一個(gè)任務(wù)用來修改 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: [email protected]
- 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
現(xiàn)在我們的流水線就變成了如下所示的清單:
# 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"
# 定義鏡像參數(shù)
- 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 參數(shù)
- 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 # 編譯二進(jìn)制程序
taskRef:
name: build
runAfter: # 測試任務(wù)執(zhí)行之后才執(zhí)行 build task
- test
- clone
workspaces: # 傳遞 workspaces
- name: go-repo
workspace: go-repo-pvc
- name: docker # 構(gòu)建并推送 Docker 鏡像
taskRef:
name: docker
runAfter:
- build
workspaces: # 傳遞 workspaces
- name: go-repo
workspace: go-repo-pvc
params: # 傳遞參數(shù)
- 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)
最后創(chuàng)建用于 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: # 定義參數(shù),和 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
現(xiàn)在我們的整個(gè)流水線就更加精簡了。現(xiàn)在我們?nèi)?yīng)用倉庫中修改下源代碼并提交就可以觸發(fā)我們的流水線了。

可以看到當(dāng)我們提交代碼后,整個(gè)流水線構(gòu)建會(huì)一直卡在最后的 sync 任務(wù),這是因?yàn)槲覀儓?zhí)行了 argocd app wait $(params.app_name) --health 這個(gè)命令,需要等待應(yīng)用健康后才會(huì)退出。
$ curl devops-demo.k8s.local
{"msg":"Hello Tekton + ArgoCD On GitLab"}
但實(shí)際上上面我們的應(yīng)用已經(jīng)部署成功了,只是 Argo CD 的健康檢查沒有通過,Argo CD 為幾種標(biāo)準(zhǔn)的 Kubernetes 資源提供了內(nèi)置的健康策略,然后將這些策略作為一個(gè)整體呈現(xiàn)在應(yīng)用的健康狀態(tài)中,比如會(huì)檢查副本數(shù)是否正常,PVC 是否綁定等,而對于 Ingress 資源會(huì)檢查 status.loadBalancer.ingress 列表是否非空,需要至少有一個(gè) 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 頁面上也可以證實(shí)是 Ingress 導(dǎo)致健康檢查沒通過:

這個(gè)時(shí)候需要我們?nèi)プ远x Ingress 資源的監(jiān)控檢查方式,Argo CD 支持用 Lua 來編寫檢查規(guī)則,修改 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
......
修改完成后,我們的應(yīng)用就會(huì)變成健康狀態(tài)了。

如果需要回滾,則可以直接在 Argo CD 頁面上點(diǎn)擊 HISTORY AND ROLLBACK 安裝查看部署的歷史記錄選擇回滾的版本即可:

可以查看整個(gè) Tekton 流水線的狀態(tài):
$ 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

最后用一張圖來總結(jié)下我們使用 Tekton 結(jié)合 Argo CD 來實(shí)現(xiàn) GitOps 的工作流:

K8S 進(jìn)階訓(xùn)練營
點(diǎn)擊屏末 | 閱讀原文 | 即刻學(xué)習(xí)

掃描二維碼獲取
更多云原生知識(shí)
k8s 技術(shù)圈

