如何藉助Tekton實現微服務的Pipeline

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服務架構中,應用程序是由多個相互連接的服務組成的,這些服務協同工作以實現所需的業務功能。所以,一個典型的企業級微服務架構如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/13figure-1-1626181667065.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最初,我們可能認爲使用微服務架構實現一個應用程序是很容易的事情。但是,要恰當地完成這一點並不容易,因爲我們會面臨一些新的挑戰,而這些挑戰是單體架構所未曾遇到的。舉例來講,這樣的挑戰包括容錯、服務發現、擴展性、日誌和跟蹤等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了應對這些挑戰,每個微服務都需要實現在Red Hat被稱爲“微服務特性(microservicility)”的內容。這個術語指的是除了業務邏輯之外,服務必須要實現的一個橫切性關注點的列表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些關注點總結起來如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/8figure-2-1626181667065.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本系列的"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MBAYoWrjCOAJK5QuqbcQ","title":"","type":null},"content":[{"type":"text","text":"第一部分"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MAdzPKPBJBDibipO9daS","title":"","type":null},"content":[{"type":"text","text":"第二部分"}]},{"type":"text","text":"中,我們分別討論瞭如何使用"},{"type":"link","attrs":{"href":"https:\/\/quarkus.io\/","title":"","type":null},"content":[{"type":"text","text":"Quarkus"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/istio.io\/","title":"","type":null},"content":[{"type":"text","text":"Istio"}]},{"type":"text","text":"實現這些微服務特性。但是,還有一個微服務特性是我們在這兩篇文章中都沒有涉及到的,那就是"},{"type":"text","marks":[{"type":"strong"}],"text":"Pipeline"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服務架構中,我們應該能夠獨立地部署服務,而不需要任何的部署編排(orchestration)。在服務間沒有任何的編排意味着沒有必要在每次部署的時候都部署和發佈整個應用程序,只需要其中的很小一部分就可以了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們能夠發佈應用中各個小的組成部分的話,那麼這會帶來一些好處:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"減少在應用中引入破壞性變更的機率。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果出現錯誤的話,更容易部署和回滾。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以增加部署至生產環境的頻率。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"出於這樣的原因,每個服務應該都有自己部署Pipeline,這樣的話,我們就可以隨時部署它,只需要遵循它自己的規則或流程即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/6figure-3-1626181667065.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,爲每個服務都創建一個部署Pipeline會帶來一些挑戰,這是我們需要解決的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何實現和管理多個Pipeline。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何爲所有的服務實現自動部署。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何跨服務共享Pipeline中的某些組成部分,同時又保持這些Pipeline的獨立性。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何在雲環境中執行它們。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數問題的答案就是_Pipeline即代碼(pipeline-as-code)_。這種技術能夠允許我們以代碼或可視爲代碼的文件(YAML)的形式創建持續交付Pipeline。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲Pipeline是以代碼的形式定義的,所以它們應該置於源碼控制之下,這意味着它們是可以重用、可以建立分支也可以創建tag。更重要的是,我們能夠將服務代碼和交付Pipeline添加到相同的倉庫中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes正在成爲部署微服務的事實標準工具。它是一個開源的系統,用來自動化、編排、擴展和管理容器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我們在前面的兩篇文章中所講到的那樣,在我們提到的十個微服務特性中,通過使用 Kubernetes 能夠覆蓋其中的三個。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/4figure-4-1626181667065.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們使用Istio,可以實現另外五個微服務特性,即服務發現、回彈性、認證、監控和跟蹤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/3figure-5-1626181667065.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用Kubernetes和Istio是個好主意,但是Pipeline該怎麼實現呢?我們該如何實現一個Kubernetes原生的持續交付Pipeline呢?引入"},{"type":"link","attrs":{"href":"https:\/\/tekton.dev\/","title":"","type":null},"content":[{"type":"text","text":"Tekton"}]},{"type":"text","text":"是一個可行的解決方案。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Tekton"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Tekton是一個Kubernetes原生的構建CI\/CD Pipeline的解決方案,能夠以Kubernetes擴展的方式安裝和運行。它提供了一組Kubernetes自定義資源(custom resource),藉助這些自定義資源,我們可以爲Pipeline創建和重用構建塊。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"實體"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Tekton定義瞭如下的基本Kubernetes自定義資源定義(Kubernetes Custom Resource Definition,CRD)來構建Pipeline:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"能夠定義可引用的資源,比如源碼倉庫或容器鏡像。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Task"}]},{"type":"text","text":"定義了一個按順序執行的step列表。每個step會在容器中執行命令。每個task都是一個Kubernetes Pod,Pod中包含了與step同等數量的容器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"會實例化一個要執行的"},{"type":"codeinline","content":[{"type":"text","text":"Task"}]},{"type":"text","text":",並且會帶有具體的輸入、輸出和參數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Pipeline"}]},{"type":"text","text":"會定義一個task的列表,這些task會按照特定的順序來執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"PipelineRun"}]},{"type":"text","text":"會實例化一個要執行的"},{"type":"codeinline","content":[{"type":"text","text":"Pipeline"}]},{"type":"text","text":",並且會帶有具體的輸入、輸出和參數。它會自動爲每個"},{"type":"codeinline","content":[{"type":"text","text":"Task"}]},{"type":"text","text":"創建"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"實例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Task"}]},{"type":"text","text":"可以通過創建"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"對象單獨運行,也可以作爲"},{"type":"codeinline","content":[{"type":"text","text":"Pipeline"}]},{"type":"text","text":"的一部分運行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/2figure-6-1626182560203.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"安裝"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"執行如下的命令來啓動集羣:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"minikube start -p tekton --kubernetes-version='v1.19.0' --vm-driver='virtualbox' --memory=4096\n\n [istio] minikube v1.17.1 on Darwin 11.3\n Kubernetes 1.20.2 is now available. If you would like to upgrade, specify: --kubernetes-version=v1.20.2\n minikube 1.19.0 is available! Download it: https:\/\/github.com\/kubernetes\/minikube\/releases\/tag\/v1.19.0\n To disable this notice, run: 'minikube config set WantUpdateNotification false'\n\n Using the virtualbox driver based on existing profile\n You cannot change the memory size for an exiting minikube cluster. Please first delete the cluster.\n Starting control plane node istio in cluster istio\n Restarting existing virtualbox VM for \"istio\" ...\n Preparing Kubernetes v1.19.0 on Docker 19.03.12 ...\n Verifying Kubernetes components...\n Enabled addons: storage-provisioner, default-storageclass\n Done! kubectl is now configured to use \"tekton\" cluster and \"\" namespace by default\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes啓動就緒之後,我們可以下載"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":" CLI工具來與Tekton Pipeline進行交互。在本例中,我們從"},{"type":"link","attrs":{"href":"https:\/\/github.com\/tektoncd\/cli\/releases\/tag\/v0.18.0","title":"","type":null},"content":[{"type":"text","text":"發佈頁面"}]},{"type":"text","text":"下載"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":" 0.18.0。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們通過執行如下的命令安裝Tekton控制器:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f \nhttps:\/\/storage.googleapis.com\/tekton-releases\/pipeline\/previous\/v0.24.0\/release.yaml\nnamespace\/tekton-pipelines created\npodsecuritypolicy.policy\/tekton-pipelines created\nclusterrole.rbac.authorization.k8s.io\/tekton-pipelines-controller-cluster-access created\nclusterrole.rbac.authorization.k8s.io\/tekton-pipelines-controller-tenant-access created\nclusterrole.rbac.authorization.k8s.io\/tekton-pipelines-webhook-cluster-access created\nrole.rbac.authorization.k8s.io\/tekton-pipelines-controller created\n…\ndeployment.apps\/tekton-pipelines-controller created\nservice\/tekton-pipelines-controller created\nhorizontalpodautoscaler.autoscaling\/tekton-pipelines-webhook created\ndeployment.apps\/tekton-pipelines-webhook created\nservice\/tekton-pipelines-webhook created\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"定義Pipeline"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們看一下該如何在Tekton中定義持續交付的Pipeline。這個pipeline由兩個task組成。第一個task從"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lordofthejars\/hello-world-tekton.git","title":"","type":null},"content":[{"type":"text","text":"GitHub"}]},{"type":"text","text":"上clone項目,使用Maven(可以是其他任意的構建工具甚至是不同的語言)構建Java項目,創建容器鏡像並將其推送至一個容器registry。第二個任務會將服務部署至一個Kubernetes集羣。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:qualit(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/2figure-7-1626182560203.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,在開發pipeline之前,我們先通過一個簡單的“Hello World”來理解Tekton的概念。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第一個Task"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們創建一個只包含單個step的task,該task會啓動一個"},{"type":"codeinline","content":[{"type":"text","text":"busybox"}]},{"type":"text","text":"容器並在容器中執行"},{"type":"codeinline","content":[{"type":"text","text":"echo"}]},{"type":"text","text":"命令。創建名爲"},{"type":"codeinline","content":[{"type":"text","text":"hello-world-task.yml"}]},{"type":"text","text":"的文件:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: Task\nmetadata:\n name: helloworld\nspec:\n steps:\n - name: sayhello\n image: busybox\n command:\n - echo\n args: ['Hello World']\n\nkubectl apply -f src\/main\/tekton\/hello-world-task.yml -n default\ntask.tekton.dev\/helloworld created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":"列出當前註冊的所有task:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn task list\nNAME DESCRIPTION AGE\nhelloworld 1 minute ago\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此時只是註冊了這個task,現在我們需要初始化一個task來執行它。我們可以通過使用"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":" CLI或應用一個"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"來實現這一點。在這裏,我們創建一個名爲 "},{"type":"codeinline","content":[{"type":"text","text":"hello-world-taskrun.yml"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"文件,它會在"},{"type":"codeinline","content":[{"type":"text","text":"name"}]},{"type":"text","text":"字段中註冊前文所述的"},{"type":"codeinline","content":[{"type":"text","text":"task"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: TaskRun\nmetadata:\n name: helloworld-run\nspec:\n taskRef:\n name: helloworld\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,我們應用這個文件,task就會被觸發。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/tekton\/hello-world-taskrun.yml\ntaskrun.tekton.dev\/helloworld-run created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以使用"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":"列出當前所有的task:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn tr list\nNAME STARTED DURATION STATUS\nhelloworld-run 8 seconds ago --- Running(Pending)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歸根到底,"},{"type":"codeinline","content":[{"type":"text","text":"Task"}]},{"type":"text","text":"只是運行在Kubernetes集羣中的一個Kubernetes Pod,而每個step則是Pod中的一個容器。執行如下的命令獲取當前的Pod:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods\nNAME READY STATUS RESTARTS AGE\nhelloworld-run-pod-75dwt 0\/1 Completed 0 45s\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pod的狀態是已完成,因爲task已經執行完畢了。它會運行一個容器,容器的名字會符合"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"中"},{"type":"codeinline","content":[{"type":"text","text":"metadata"}]},{"type":"text","text":"部分name字段所定義的值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以描述一下Pod,請關注"},{"type":"codeinline","content":[{"type":"text","text":"containers"}]},{"type":"text","text":"區域,以獲取task啓動的容器的概況:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl describe pod helloworld-run-pod-75dwt\n\n…\nContainers:\n step-sayhello:\n Container ID: docker:\/\/e3bb6b747e6cbb76829e7658b7bf2976f3f09861775a4584eccfba0c143996a6\n Image: busybox\n\n...\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/2figure-8-1626182804262.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,我們可以使用"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":"的logs命令查看TaskRun的日誌:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn tr logs helloworld-run\n[sayhello] Hello World\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們對Tekton的task已經有了基本的瞭解,接下來我們更進一步,實現一個具備了所需step的真正pipeline。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Pipeline資源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"定義了輸入\/輸出資源的位置,這些資源會被Task中的step所用到。這些參數通常會遵循Git URL或者容器鏡像全限定名的形式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們創建一個新的"},{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"文件來配置項目的倉庫:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1alpha1\nkind: PipelineResource\nmetadata:\n name: git-source\nspec:\n type: git\n params:\n - name: url\n value: https:\/\/github.com\/lordofthejars\/hello-world-tekton.git\n\nkubectl apply -f src\/main\/tekton\/git-pipeline-resource.yml -n default\npipelineresource.tekton.dev\/git-source created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,創建另外一個"},{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"來設置容器鏡像:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1alpha1\nkind: PipelineResource\nmetadata:\n name: hello-world-image\nspec:\n type: image\n params:\n - name: url\n value: quay.io\/lordofthejars\/hello-world-quarkus-tekton:1.0.0\n\nkubectl apply -f src\/main\/tekton\/container-image-resource.yml\npipelineresource.tekton.dev\/hello-world-image created\n\ntkn resource list\nNAME TYPE DETAILS\ngit-source git url: https:\/\/github.com\/lordofthejars\/hello-world-tekton.git\nhello-world-image image url: quay.io\/lordofthejars\/hello-world-quarkus-tekton:1.0.0\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Task"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在創建task之前,我們先創建一個Kubernetes "},{"type":"codeinline","content":[{"type":"text","text":"Secret"}]},{"type":"text","text":",它包含了兩個用於"},{"type":"link","attrs":{"href":"https:\/\/quay.io\/","title":"","type":null},"content":[{"type":"text","text":"Quay"}]},{"type":"text","text":"訪問憑證的鍵\/值對,分別是Quay的用戶名和Quay的密碼。請將username和password的值替換成正確的值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl create secret generic quay-credentials --from-literal=quay-username='yyy' --from-literal=quay-password='xxx'\nsecret\/quay-credentials created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們創建一個"},{"type":"codeinline","content":[{"type":"text","text":"Task"}]},{"type":"text","text":"來構建項目,創建Linux容器鏡像並將其推送至容器registry上(在本例中,我們使用"},{"type":"link","attrs":{"href":"https:\/\/quay.io\/","title":"","type":null},"content":[{"type":"text","text":"Quay"}]},{"type":"text","text":",但是可以切換成任意的其他方案)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲這是一個使用Quarkus實現的Java項目,所以我們會通過集成"},{"type":"link","attrs":{"href":"https:\/\/github.com\/GoogleContainerTools\/jib","title":"","type":null},"content":[{"type":"text","text":"Jib"}]},{"type":"text","text":"實現以Dockerless的方式創建容器鏡像。在一個容器運行時(Tekton就是這種情況)中構建容器鏡像時,我們可能會遇到一些在容器中運行task容器的問題(構建新的容器)。這也是爲何採用Dockerless技術創建容器的重要原因。對於Java項目來說,Jib是一個可行的方案,但是也有其他通用的、不針對特定語言的方案,比如"},{"type":"link","attrs":{"href":"https:\/\/buildah.io\/","title":"","type":null},"content":[{"type":"text","text":"Buildah"}]},{"type":"text","text":"或"},{"type":"link","attrs":{"href":"https:\/\/github.com\/GoogleContainerTools\/kaniko","title":"","type":null},"content":[{"type":"text","text":"Kaniko"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們創建一個"},{"type":"codeinline","content":[{"type":"text","text":"task"}]},{"type":"text","text":",它會執行執行Maven package goal,設置構建所需的Quarkus選項並將容器鏡像推送至Quay。當然,我們還需要以輸入和輸出("},{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":")的方式設置Git倉庫和容器鏡像名,並以參數(Kubernetes "},{"type":"codeinline","content":[{"type":"text","text":"Secrets"}]},{"type":"text","text":")的形式設置Quay的用戶名和密碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: Task\nmetadata:\n name: build-app\nspec:\n params:\n - name: quay-credentials-secret\n type: string\n description: name of the secret holding the quay credentials\n default: quay-credentials\n resources:\n inputs:\n - name: source\n type: git\n outputs:\n - name: builtImage\n type: image\n steps:\n - name: maven-build\n image: docker.io\/maven:3.6-jdk-11-slim\n command:\n - mvn\n args:\n - clean\n - package\n - -Dquarkus.container-image.push=true\n env:\n - name: QUARKUS_CONTAINER_IMAGE_IMAGE\n value: $(outputs.resources.builtImage.url)\n - name: QUARKUS_CONTAINER_IMAGE_USERNAME\n valueFrom:\n secretKeyRef:\n name: $(params.quay-credentials-secret)\n key: quay-username\n - name: QUARKUS_CONTAINER_IMAGE_PASSWORD\n valueFrom:\n secretKeyRef:\n name: $(params.quay-credentials-secret)\n key: quay-password\n workingDir: \"\/workspace\/source\/\"\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面的文件中,我們看到Quay憑證的secret名被設置爲參數。參數"},{"type":"codeinline","content":[{"type":"text","text":"quay-credentials"}]},{"type":"text","text":"有一個默認值,它的值與"},{"type":"codeinline","content":[{"type":"text","text":"kubectl creates secret"}]},{"type":"text","text":"命令中所用的值是相同的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸入參數被命名爲"},{"type":"codeinline","content":[{"type":"text","text":"source"}]},{"type":"text","text":",類型爲"},{"type":"codeinline","content":[{"type":"text","text":"git"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸出參數是容器鏡像的名稱。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"env"}]},{"type":"text","text":"部分中,我們定義了一些環境變量,用來配置Quarkus容器鏡像擴展如何構建和推送容器鏡像:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器鏡像名是在輸出資源中定義的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quay憑證會從Kubernetes "},{"type":"codeinline","content":[{"type":"text","text":"Secret"}]},{"type":"text","text":"中注入進來。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Kubernetes集羣中註冊task:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/tekton\/build-push-task.yml\n\ntkn task list\nNAME DESCRIPTION AGE\nbuild-app 3 hours ago\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我們通過創建一個"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"來初始化這個task,此時我們需要鏈接在前文中通過"},{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"創建的輸入\/輸出資源並將secret名稱設置爲參數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: TaskRun\nmetadata:\n name: build-app-run\nspec:\n params:\n - name: quay-credentials-secret\n value: quay-credentials\n resources:\n inputs:\n - name: source\n resourceRef:\n name: git-source\n outputs:\n - name: builtImage\n resourceRef:\n name: hello-world-image\n taskRef:\n name: build-app\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,"},{"type":"codeinline","content":[{"type":"text","text":"resourceRef"}]},{"type":"text","text":"字段指向了在前文中定義的"},{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"的名稱。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"被應用的時候,構建過程就開始執行了,它會clone項目,使用Maven進行構建,然後創建並推送鏡像。通過使用"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":" CLI,我們可以看到當前task的實時日誌:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn tr logs -f\n\n? Select taskrun: [Use arrows to move, type to filter]\n> build-app-run started 38 minutes ago\n helloworld-run started 1 day ago\n\n[git-source-source-w2ck5] {\"level\":\"info\",\"ts\":1620922474.2565205,\"caller\":\"git\/git.go:169\",\"msg\":\"Successfully cloned https:\/\/github.com\/lordofthejars\/hello-world-tekton.git @ ee3edc414c47f2bdeda9cc7c47ac54427d35a9dc (grafted, HEAD) in path \/workspace\/source\"}\n[git-source-source-w2ck5] {\"level\":\"info\",\"ts\":1620922474.276228,\"caller\":\"git\/git.go:207\",\"msg\":\"Successfully initialized and updated submodules in path \/workspace\/source\"}\n\n...\n\n[maven-build] Downloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-plugin-parameter-documenter\/2.0.6\/maven-plugin-parameter-documenter-2.0.6.pom\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-plugin-parameter-documenter\/2.0.6\/maven-plugin-parameter-documenter-2.0.6.pom (1.9 kB at 58 kB\/s)\n[maven-build] Downloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/reporting\/maven-reporting-api\/2.0.6\/maven-reporting-api-2.0.6.pom\n\n…\n\n[maven-build] [INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Pushed container image quay.io\/lordofthejars\/hello-world-quarkus-tekton:1.0.0 (sha256:e71b0808af36ce3b9b980b2fb83886be2e06439ee454813052a115829e1e727c)\n[maven-build] [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 24202ms\n[maven-build] [INFO] ------------------------------------------------------------------------\n[maven-build] [INFO] BUILD SUCCESS\n[maven-build] [INFO] ------------------------------------------------------------------------\n[maven-build] [INFO] Total time: 01:16 min\n[maven-build] [INFO] Finished at: 2021-05-13T16:15:52Z\n[maven-build] [INFO] ------------------------------------------------------------------------\n\n[image-digest-exporter-88mpr] {\"severity\":\"INFO\",\"timestamp\":\"2021-05-13T16:15:53.025263494Z\",\"caller\":\"logging\/config.go:116\",\"message\":\"Successfully created the logger.\"}\n[image-digest-exporter-88mpr] {\"severity\":\"INFO\",\"timestamp\":\"2021-05-13T16:15:53.025374882Z\",\"caller\":\"logging\/config.go:117\",\"message\":\"Logging level set to: info\"}\n[image-digest-exporter-88mpr] {\"severity\":\"INFO\",\"timestamp\":\"2021-05-13T16:15:53.025508459Z\",\"caller\":\"imagedigestexporter\/main.go:59\",\"message\":\"No index.json found for: builtImage\",\"commit\":\"b86a9a2\"}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-tekton\/en\/resources\/2figure-9-1626183766241.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Pipeline"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們已經創建了一個簡單的task。但是,實際的持續交付\/部署pipeline應該是由多個task組成的:構建並推送容器鏡像,然後將其部署到Kubernetes集羣中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們創建具有兩個step的task:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一個step是使用"},{"type":"codeinline","content":[{"type":"text","text":"PipelineResource"}]},{"type":"text","text":"所設置的容器鏡像更新Kubernetes "},{"type":"codeinline","content":[{"type":"text","text":"Deployment"}]},{"type":"text","text":"。在編輯deployment YAML文件時,我們可以使用"},{"type":"link","attrs":{"href":"https:\/\/github.com\/mikefarah\/yq","title":"","type":null},"content":[{"type":"text","text":"yq"}]},{"type":"text","text":"工具。同時,我們使用"},{"type":"codeinline","content":[{"type":"text","text":"script"}]},{"type":"text","text":"代替"},{"type":"codeinline","content":[{"type":"text","text":"command"}]},{"type":"text","text":"來展示在容器中運行命令的另外一種方式。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二個step執行"},{"type":"codeinline","content":[{"type":"text","text":"kubectl"}]},{"type":"text","text":"命令以部署服務。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於這個task來講,我們需要三個參數:deployment文件、存儲deployment文件的Git倉庫以及前面task中所構建的容器鏡像的名稱。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建名爲"},{"type":"codeinline","content":[{"type":"text","text":"deploy-task.yml"}]},{"type":"text","text":"的文件,內容如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: Task\nmetadata:\n name: kubectl-deploy\nspec:\n params:\n - name: deploymentFile\n type: string\n description: deployment file location\n resources:\n inputs:\n - name: source\n type: git\n - name: builtImage\n type: image\n steps:\n - name: update-deployment-file\n image: quay.io\/lordofthejars\/image-updater:1.0.0\n script: |\n #!\/usr\/bin\/env ash\n yq eval -i '.spec.template.spec.containers[0].image = env(DESTINATION_IMAGE)' $DEPLOYMENT_FILE\n env:\n - name: DESTINATION_IMAGE\n value: \"$(inputs.resources.builtImage.url)\"\n - name: DEPLOYMENT_FILE\n value: \"\/workspace\/source\/$(inputs.params.deploymentFile)\"\n\n - name: kubeconfig\n image: quay.io\/rhdevelopers\/tutorial-tools:0.0.3\n command: [\"kubectl\"]\n args:\n - apply\n - -f\n - \/workspace\/source\/$(inputs.params.deploymentFile)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,在第一個step中,我們使用"},{"type":"codeinline","content":[{"type":"text","text":"script"}]},{"type":"text","text":"來代替"},{"type":"codeinline","content":[{"type":"text","text":"command"}]},{"type":"text","text":"。要傳遞Tekton參數到"},{"type":"codeinline","content":[{"type":"text","text":"script"}]},{"type":"text","text":"區域,我們需要通過環境變量來實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/tekton\/deploy-task.yml\ntask.tekton.dev\/kubectl-deploy created\n\ntkn task ls\nNAME DESCRIPTION AGE\nbuild-app 18 hours ago\nhelloworld 1 day ago\nkubectl-deploy 11 minutes ago\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要部署服務,我們需要在Kubernetes集羣中執行"},{"type":"codeinline","content":[{"type":"text","text":"kubectl"}]},{"type":"text","text":"命令。爲了實現這一點,我們需要設置一個Kubernetes "},{"type":"codeinline","content":[{"type":"text","text":"Role"}]},{"type":"text","text":"以允許"},{"type":"codeinline","content":[{"type":"text","text":"default"}]},{"type":"text","text":"服務賬號(因爲這是在我們的樣例中運行Tekton Pipeline所使用的服務賬號)具備相應的權限。這個角色必須要允許在運行的容器中應用Kubernetes資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建名爲"},{"type":"codeinline","content":[{"type":"text","text":"pipeline-sa-role.yml"}]},{"type":"text","text":"的文件,內容如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: rbac.authorization.k8s.io\/v1\nkind: Role\nmetadata:\n name: pipeline-extra-role\nrules:\n - apiGroups:\n - \"\"\n resources:\n - pods\n - services\n - endpoints\n - configmaps\n - secrets\n verbs:\n - \"*\"\n - apiGroups:\n - apps\n resources:\n - deployments\n - daemonsets\n - replicasets\n - statefulsets\n verbs:\n - \"*\"\n - apiGroups:\n - \"\"\n resources:\n - pods\n verbs:\n - get\n - apiGroups:\n - apps\n resources:\n - replicasets\n verbs:\n - get\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: RoleBinding\nmetadata:\n name: pipeline-exta-role-binding\nroleRef:\n kind: Role\n name: pipeline-extra-role\n apiGroup: rbac.authorization.k8s.io\nsubjects:\n - kind: ServiceAccount\n name: default\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"註冊角色:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/tekton\/pipeline-sa-role.yml\nrole.rbac.authorization.k8s.io\/pipeline-extra-role created\nrolebinding.rbac.authorization.k8s.io\/pipeline-exta-role-binding created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們已經可以創建Tekton Pipeline將這兩個task組合在一起了。在這裏,定義pipeline參數至關重要,因爲在pipeline定義中引用Tekton Task的時候,要設置這些參數。在這兩個task中,我們都定義了"},{"type":"codeinline","content":[{"type":"text","text":"params"}]},{"type":"text","text":"值("},{"type":"codeinline","content":[{"type":"text","text":"quay-credentials-secret"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"deploymentFile"}]},{"type":"text","text":"),並引用"},{"type":"codeinline","content":[{"type":"text","text":"pipelineresource"}]},{"type":"text","text":"作爲輸入\/輸出。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建"},{"type":"codeinline","content":[{"type":"text","text":"pipeline.yml"}]},{"type":"text","text":"文件:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: Pipeline\nmetadata:\n name: hello-world-pipeline\nspec:\n resources:\n - name: appSource\n type: git\n - name: containerImage\n type: image\n tasks:\n - name: build-app\n taskRef:\n name: build-app\n params:\n - name: quay-credentials-secret\n value: quay-credentials\n resources:\n inputs:\n - name: source\n resource: appSource\n outputs:\n - name: builtImage\n resource: containerImage\n - name: kube-deploy\n taskRef:\n name: kubectl-deploy\n params:\n - name: deploymentFile\n value: src\/main\/kubernetes\/kubernetes.yml\n runAfter:\n - build-app\n resources:\n inputs:\n - name: source\n resource: appSource\n - name: builtImage\n resource: containerImage\n\nkubectl apply -f src\/main\/tekton\/pipeline.yml\npipeline.tekton.dev\/hello-world-pipeline created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以使用"},{"type":"codeinline","content":[{"type":"text","text":"tkn"}]},{"type":"text","text":" CLI列出pipeline:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn pipeline list\nNAME AGE LAST RUN STARTED DURATION STATUS\nhello-world-pipeline 5 seconds ago --- --- --- ---\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過命令描述pipeline,能夠讓我們看到pipeline的概述以及在運行它的時候,必須要定義哪些內容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn pipeline describe hello-world-pipeline\nName: hello-world-pipeline\nNamespace: default\n\n Resources\n\n NAME TYPE\n ∙ appSource git\n ∙ containerImage image\n\n Params\n\n No params\n\n Results\n\n No results\n\n Workspaces\n\n No workspaces\n\n Tasks\n\n NAME TASKREF RUNAFTER TIMEOUT CONDITIONS PARAMS\n ∙ build-app build-app --- --- quay-credentials-secret: quay-credentials\n ∙ kube-deploy kubectl-deploy build-app --- --- deploymentFile: src\/main\/kubernetes\/deployment.yml\n\n PipelineRuns\n\n No pipelineruns\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們不需要任何的"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"了,因爲它們會在應用"},{"type":"codeinline","content":[{"type":"text","text":"PipelineRun"}]},{"type":"text","text":"的時候自動創建:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: tekton.dev\/v1beta1\nkind: PipelineRun\nmetadata:\n name: hello-world-pipeline-run\nspec:\n resources:\n - name: appSource\n resourceRef:\n name: git-source\n - name: containerImage\n resourceRef:\n name: hello-world-image\n pipelineRef:\n name: hello-world-pipeline\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"PipelineRun"}]},{"type":"text","text":"中,我們設置了"},{"type":"codeinline","content":[{"type":"text","text":"PipelineResources"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"Pipeline"}]},{"type":"text","text":"之間的鏈接,因爲它是由"},{"type":"codeinline","content":[{"type":"text","text":"TaskRun"}]},{"type":"text","text":"執行的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/tekton\/pipeline-run.yml\npipelinerun.tekton.dev\/hello-world-pipeline-run created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與TaskRun類似,我們可以通過如下的命令將一個pipeline的日誌以流的方式鏈接起來:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"tkn pipeline logs -f\n\n…\n\n[kube-deploy : git-source-source-sdgld] {\"level\":\"info\",\"ts\":1620978300.0659564,\"caller\":\"git\/git.go:169\",\"msg\":\"Successfully cloned https:\/\/github.com\/lordofthejars\/hello-world-tekton.git @ b954dbc68e0aa7e4cfb6defeff00b1e4ded2889c (grafted, HEAD) in path \/workspace\/source\"}\n[kube-deploy : git-source-source-sdgld] {\"level\":\"info\",\"ts\":1620978300.0972755,\"caller\":\"git\/git.go:207\",\"msg\":\"Successfully initialized and updated submodules in path \/workspace\/source\"}\n\n[kube-deploy : kubeconfig] deployment.apps\/hello-world created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果構建成功的話,服務將會部署到當前的Kubernetes集羣中。我們可以獲取所有的Pod以查看集羣中發生了什麼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods\nNAME READY STATUS RESTARTS AGE\nhello-world-59d47597c-qm9nr 1\/1 Running 0 36m\nhello-world-pipeline-run-build-app-xmvg2-pod-v4vps 0\/4 Completed 0 37m\nhello-world-pipeline-run-kube-deploy-bfqhd-pod-x68xh 0\/3 Completed 0 36m\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到有兩個已完成的Pod。"},{"type":"codeinline","content":[{"type":"text","text":"hello-world-pipeline-run-build-app"}]},{"type":"text","text":" Pod對應的是"},{"type":"codeinline","content":[{"type":"text","text":"build-app"}]},{"type":"text","text":" task,它會clone、構建和測試服務,並且最終會創建和推送容器鏡像。"},{"type":"codeinline","content":[{"type":"text","text":"hello-world-pipeline-run-kube-deploy"}]},{"type":"text","text":" Pod對應"},{"type":"codeinline","content":[{"type":"text","text":"kube-deploy"}]},{"type":"text","text":" task,它負責部署應用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了task之外,這裏還有一個正在運行的Pod。這就是pipeline執行期間所部署的應用Pod。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"結論"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開發和實現微服務架構要比開發單體應用更具挑戰性。我們相信,微服務特性能夠促使你在應用基礎設施方面正確地開發服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本系列的"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MBAYoWrjCOAJK5QuqbcQ","title":"","type":null},"content":[{"type":"text","text":"第一篇"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MAdzPKPBJBDibipO9daS","title":"","type":null},"content":[{"type":"text","text":"第二篇"}]},{"type":"text","text":"文章中,我們分別學習瞭如何使用Quarkus或Istio實現微服務特性,但是我們還沒有介紹Pipeline這項微服務特性。本文闡述瞭如何使用Tekton實現一個基本的持續交付pipeline,Tekton是一個Kubernetes原生的解決方案,用於構建CI\/CD pipeline。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Tekton一個很重要的優勢是能夠在容器最終要部署的同一個集羣中創建容器鏡像。這減少了容器在某些機器上構建而在其他機器上部署時可能出現的差異。另一個優勢是使用YAML文件來定義pipeline的方式。通過這種方式,pipeline能夠與源代碼一起存儲,使其可創建分支、可創建tag或版本化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時候,沒有必要從頭開始定義task,在"},{"type":"link","attrs":{"href":"https:\/\/github.com\/tektoncd\/catalog","title":"","type":null},"content":[{"type":"text","text":"Tekton Catalog"}]},{"type":"text","text":"上,我們可以看到很多可以直接使用的task。除此之外,如果你需要開發自定義的task的話,要藉助參數和輸入\/輸出資源將它們設計的儘可能比較開放。通過這種方式,task有可能實現在你的組織內的重用,從而解決類似的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文只是Tekton的一個簡介。我們可以看到當一個"},{"type":"codeinline","content":[{"type":"text","text":"PipelineRun"}]},{"type":"text","text":"對象在集羣中創建的時候,pipeline就會運行,但是觸發器\/事件也可以觸發Pipeline。舉例來講,事件可能是對GitHub倉庫中特性分支的推送。觸發器是一個更高級的話題,你可以通過"},{"type":"link","attrs":{"href":"https:\/\/github.com\/tektoncd\/triggers","title":"","type":null},"content":[{"type":"text","text":"該地址"}]},{"type":"text","text":"學習關於觸發器的更多知識。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用於闡述本文的源碼可以在GitHub"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lordofthejars\/hello-world-tekton","title":"","type":null},"content":[{"type":"text","text":"倉庫"}]},{"type":"text","text":"中找到。"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MBAYoWrjCOAJK5QuqbcQ","title":"","type":null},"content":[{"type":"text","text":"第一篇"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MAdzPKPBJBDibipO9daS","title":"","type":null},"content":[{"type":"text","text":"第二篇"}]},{"type":"text","text":"文章的源碼也分別可以在"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lordofthejars\/microservicilities-quarkus","title":"","type":null},"content":[{"type":"text","text":"這裏"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lordofthejars\/rating-service\/tree\/istio","title":"","type":null},"content":[{"type":"text","text":"這裏"}]},{"type":"text","text":"獲取。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者簡介"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Alex Soto"},{"type":"text","text":"是紅帽公司的開發者體驗總監。他對Java領域、軟件自動化充滿熱情,他相信開源軟件模式。Soto是"},{"type":"link","attrs":{"href":"https:\/\/www.manning.com\/books\/testing-java-microservices","title":"","type":null},"content":[{"type":"text","text":"Manning的《Testing Java Microservices》"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.oreilly.com\/library\/view\/quarkus-cookbook\/9781492062646\/","title":"","type":null},"content":[{"type":"text","text":"O’Reilly的《Quarkus Cookbook》"}]},{"type":"text","text":"兩本書的共同作者,他還是多個開源項目的貢獻者。自2017年以來,他一直是Java Champion,是國際演講者和Salle URL大學的教師。你可以在Twitter上關注他("},{"type":"link","attrs":{"href":"https:\/\/twitter.com\/alexsotob","title":"","type":null},"content":[{"type":"text","text":"Alex Soto ⚛"}]},{"type":"text","text":"️),隨時瞭解Kubernetes和Java領域的動態。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/articles\/microservicilities-tekton\/","title":"","type":null},"content":[{"type":"text","text":"Implementing Pipeline Microservicilities with Tekton"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章