您當前的位置:首頁 > 書法

kubernetes原生CICD工具:Tekton探秘與上手實踐

作者:由 ethfoo 發表于 書法時間:2019-08-25

引子

如果有關注過Knative社群動態的同學,可能會知道最近發生了一件比較大的新聞,三大元件之一的build專案被人提了一個很殘忍的Proposal(

https://github。com/knative/build/issues/614

),並且專門在專案Readme的開頭加了個NOTE:

NOTE: There is an open proposal to deprecate this component in favor of Tekton Pipelines。 If you are a new user, consider using Tekton Pipelines, or another tool, to build and release。 If you use Knative Build today, please give feedback on the deprecation proposal。

這個Proposal的目的是想要廢棄Knative的build模組,Knative只專注做serverless,而將build模組代表的CI/CD功能全盤交出,讓使用者自己選擇合適的CI/CD工具。Knative只負責將映象執行,同時提供serverless相關的事件驅動等功能,不再關心映象的構建過程。

雖然目前為止,該Proposal還在開放徵求社群的意見,不過,從留言來看,build模組未來還是大機率會被deprecate。因為Knative build的替代者Tekton已經展露頭腳,表現出更強大的基於kubernetes的CI/CD能力,Tekton的設計思路其實也是來源於Knative build的,現有使用者也可以很方便的從build遷移至Tekton。

Tekton是什麼

Tekton是一個谷歌開源的kubernetes原生CI/CD系統,功能強大且靈活,開源社群也正在快速的迭代和發展壯大。google cloud已經推出了基於Tekton的服務(

https://cloud。google。com/Tekton/

)。

其實Tekton的前身是Knative的build-pipeline專案,從名字可以看出這個專案是為了給build模組增加pipeline的功能,但是大家發現隨著不同的功能加入到Knative build模組中,build模組越來越變得像一個通用的CI/CD系統,這已經脫離了Knative build設計的初衷,於是,索性將build-pipeline剝離出Knative,搖身一變成為Tekton,而Tekton也從此致力於提供全功能、標準化的原生kubernetesCI/CD解決方案。

Tekton雖然還是一個挺新的專案,但是已經成為 Continuous Delivery Foundation (CDF) 的四個初始專案之一,另外三個則是大名鼎鼎的Jenkins、Jenkins X、Spinnaker,實際上Tekton還可以作為外掛整合到JenkinsX中。所以,如果你覺得Jenkins太重,沒必要用Spinnaker這種專注於多雲平臺的CD,為了避免和Gitlab耦合不想用gitlab-ci,那麼Tekton值得一試。

Tekton的特點是kubernetes原生,什麼是kubernetes原生呢?簡單的理解,就是all in kubernetes,所以用容器化的方式構建容器映象是必然,另外,基於kubernetes CRD定義的pipeline流水線也是Tekton最重要的特徵。

那Tekton都提供了哪些CRD呢?

Task:顧名思義,task表示一個構建任務,task裡可以定義一系列的steps,例如編譯程式碼、構建映象、推送映象等,每個step實際由一個Pod執行。

TaskRun:task只是定義了一個模版,taskRun才真正代表了一次實際的執行,當然你也可以自己手動建立一個taskRun,taskRun創建出來之後,就會自動觸發task描述的構建任務。

Pipeline:一個或多個task、PipelineResource以及各種定義引數的集合。

PipelineRun:類似task和taskRun的關係,pipelineRun也表示某一次實際執行的pipeline,下發一個pipelineRun CRD例項到kubernetes後,同樣也會觸發一次pipeline的構建。

PipelineResource:表示pipeline input資源,比如github上的原始碼,或者pipeline output資源,例如一個容器映象或者構建生成的jar包等。

他們大概有如下圖所示的關係:

kubernetes原生CICD工具:Tekton探秘與上手實踐

上手實踐

部署

Tekton部署很簡單,理論上只需下載官方的yaml檔案,然後執行kubectl create -f 一條命令就可以搞定。但是由於在國內,我們無法訪問gcr。io映象倉庫,所以需要自行替換官方部署yaml檔案中的映象。

執行起來後可以在Tekton-pipelines namespace下看到兩個deployment:

# kubectl -n Tekton-pipelines get deploy

NAME READY UP-TO-DATE AVAILABLE AGE

Tekton-pipelines-controller 1/1 1 1 10d

Tekton-pipelines-webhook 1/1 1 1 10d

這就是執行Tekton所需的所有服務,一個控制器controller用來監聽上述CRD的事件,執行Tekton的各種CI/CD邏輯,一個webhook用於校驗建立的CRD資源。

webhook使用了kubernetes的admissionwebhook機制,所以,在我們kubectl create一個taskRun或者pipelineRun時,apiserver會回撥這裡部署的Tekton webhook服務,用於校驗這些CRD欄位等的正確性。

構建一個Java應用

部署完Tekton之後,我們就可以開始動手實踐了,下面以構建一個springboot工程為例。

假設我們新開發了一個名為ncs的springboot專案,為了將該專案構建成映象並上傳至映象倉庫,我們可以梳理一個最簡單的CI流程如下: 1。 從git倉庫拉取程式碼 2。 maven編譯打包 3。 構建映象 4。 推送映象

當然在CI流程之前,我們先需要在專案中增加dockerfile,否則構建映象無從談起。

0。 新增dockerfile

FROM hub。c。163。com/qingzhou/tomcat:7-oracle-jdk-rev4

ENV TZ=Asia/Shanghai LANG=C。UTF-8 LANGUAGE=C。UTF-8 LC_ALL=C。UTF-8

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

WORKDIR /usr/local/tomcat

RUN rm -rf webapps/*

COPY setenv。sh $CATALINA_HOME/bin/

COPY 。/target/*。war webapps/

ENTRYPOINT [“catalina。sh”, “run”]

一個示例如上所示,dockerfile的邏輯比較簡單:引用一個tomat的基礎映象,然後把maven構建完生成的war包複製到webapps目錄中,最後用指令碼catalina。sh執行即可。

當然這裡有個很有用的細節,我們會專案中新增一個名為setenv。sh的指令碼,在dockerfile裡會COPY

$CATALINA_HOME/bin/

。setenv。sh腳本里可以做一些tomcat啟動之前的準備工作,例如可以設定一些JVM引數等:

export NCE_JAVA_OPTS=“$NCE_JAVA_OPTS -Xms${NCE_XMS} -Xmx${NCE_XMX} -XX:MaxPermSize=${NCE_PERM} -Dcom。netease。appname=${NCE_APPNAME} -Dlog。dir=${CATALINA_HOME}/logs”

如果你也研究過catalina。sh指令碼,就會發現腳本里預設會執行setenv。sh,實際上這也是官方推薦的初始化方式。

elif [ -r “$CATALINA_HOME/bin/setenv。sh” ]; then

。 “$CATALINA_HOME/bin/setenv。sh”

fi

1。 從git倉庫拉取程式碼

新增完dockerfile之後,我們可以正式開始研究如何使用Tekton構建這個ncs專案了。

首先第一步,需要將程式碼從遠端git倉庫拉下來。

Tekton中可以使用pipelineresource這個CRD表示git倉庫遠端地址和git分支,如下所示:

apiVersion

Tekton。dev/v1alpha1

kind

PipelineResource

metadata

name

ncs-git-source

spec

type

git

params

-

name

url

value

https://github。com/ethfoo/test。git

-

name

revision

value

master

其中的revision可以使用分支、tag、commit hash。 實際上git拉取程式碼這種通用的操作,只需要我們定義了input的resource,Tekton已經預設幫我們做好了,不需要在task中寫git pull之類的steps。目前我們的task可以寫成如下所示:

apiVersion

Tekton。dev/v1alpha1

kind

Task

metadata

name

ncs

spec

inputs

resources

-

name

gitssh

type

git

git拉取程式碼還存在安全和私有倉庫的許可權問題,基於kubernetes原生的Tekton當然是採用secret/serviceaccount來解決。

對於每個專案組,可以定義一個公共的私有ssh key,然後放到secret中,供serviceaccount引用即可。

apiVersion

v1

kind

ServiceAccount

metadata

name

nce-qingzhou

namespace

Tekton-test

secrets

-

name

ncs-git-ssh

——-

apiVersion

v1

kind

Secret

metadata

name

ncs-git-ssh

namespace

Tekton-test

annotations

Tekton。dev/git-0

g。hz。netease。com

type

kubernetes。io/ssh-auth

data

ssh-privatekey

LS0tLS1CRUd。。。

known_hosts

W2cuaHoub。。。

最後,這個serviceaccount要怎麼使用呢,我們接著往下看。

2。 maven編譯打包

拉下來專案程式碼之後,開始進入使用maven編譯打包階段。而這個階段就需要我們自己定義task的steps來實現各種CI/CD的步驟了。

實際的原理也很簡單,定義的一個steps實際上就是新建一個pod去執行自定義的操作。

對於maven編譯來說,我們首先需要找一個安裝有maven的映象,然後在容器的command/args里加上mvn編譯的命令。示例如下:

spec

inputs

resources

-

name

ncs-git-source

type

git

params

# These may be overridden, but provide sensible defaults。

-

name

directory

description

The directory containing the build context。

default

/workspace/ncs-git-source

steps

-

name

maven-install

image

maven:3。5。0-jdk-8-alpine

workingDir

“${inputs。params。directory}”

args

“mvn”

“clean”

“install”

“-D maven。test。skip=true”

volumeMounts

-

name

m2

mountPath

/root/。m2

由於Tekton會給每個構建的容器都掛載/workspace這個目錄,所以每一個steps步驟裡都可以在/workspace中找到上一步執行的產物。

git拉取程式碼可以認為是一個預設的steps,這個steps的邏輯裡Tekton會把程式碼放到/workspace/{resources。name}中。上面我們定義的PipelineResource名為ncs-git-resource,所以ncs這個工程的程式碼會被放在/workspace/ncs-git-resource目錄中。

所以在maven-install這個steps中,我們需要在/workspace/ncs-git-resource中執行mvn命令,這裡我們可以使用workingDir欄位表示將該目錄設定為當前的工作目錄。同時為了避免寫死,這裡我們定義為一個input的變數params,在workingDir中使用

${}

的方式引用即可。

實際的使用中,由於每次構建都是新起容器,在容器中執行maven命令,一般都是需要將maven的m2目錄掛載出來,避免每次編譯打包都需要重新下載jar包。

steps

-

name

maven-install

。。。

volumeMounts

-

name

m2

mountPath

/root/。m2

volumes

-

name

m2

hostPath

path

/root/。m2

3。 docker映象的構建和推送

Tekton標榜自己為kubernetes原生,所以想必你也意識到了其中很重要的一點是,所有的CI/CD流程都是由一個一個的pod去執行。docker映象的build和push當然也不例外,這裡又繞不開另外一個話題,即如何在容器中構建容器映象。 一般我們有兩種方式,docker in docker(dind)和docker outside of docker(dood)。實際上兩者都是在容器中構建映象,區別在於,dind方式下在容器裡有一個完整的docker構建系統,可直接在容器中完成映象的構建,而dood是透過掛載宿主機的docker。sock檔案,呼叫宿主機的docker daemon去構建映象。

dind的方式可直接使用官方的dind映象(

https://hub。docker。com/_/docker

),當然也可以採用一些其他的開源構建方式,例如kaniko,makisu等。docker in docker的方式對使用者遮蔽了宿主機,隔離和安全性更好,但是需要關心構建映象的分層快取。

dood的方式比較簡單易用,只需要掛載了docker。sock,容器裡有docker客戶端,即可直接使用宿主機上的docker daemon,所以構建的映象都會在宿主機上,宿主機上也會有相應的映象分層的快取,這樣也便於加快映象拉取構建的速度,不過同時也需要注意定時清理冗餘的映象,防止映象rootfs佔滿磁碟。

如果是在私有云等內部使用場景下,可採用dood的方式。這裡以dood的方式為例。

首先要在task中加一個input param表示映象的名稱。

spec

inputs

params

-

name

image

description

docker image

然後在task的steps中加入映象的build和push步驟。

steps

-

name

dockerfile-build

image

docker:git

workingDir

“${inputs。params。directory}”

args

“build”

“——tag”

“${inputs。params。image}”

“。”

volumeMounts

-

name

docker-socket

mountPath

/var/run/docker。sock

-

name

dockerfile-push

image

docker:git

args

“push”

“${inputs。params。image}”

volumeMounts

-

name

docker-socket

mountPath

/var/run/docker。sock

volumes

-

name

docker-socket

hostPath

path

/var/run/docker。sock

type

Socket

瞭解kubernetes的同學一定對這種yaml宣告式的表述不會陌生,實際上上面的定義和一個deployment的yaml十分類似,這也使得Tekton很容易入門和上手。

4。 構建執行

在Tekton中task只是一個模版,每次需要定義一個taskrun表示一次實際的執行,其中使用taskRef表示引用的task即可。

apiVersion

Tekton。dev/v1alpha1

kind

TaskRun

metadata

generateName

ncs-

spec

inputs

resources

-

name

gitssh

resourceRef

name

ncs-git-source

taskRef

name

ncs

這裡的taskrun需要注意的是,inputs。resources需要引用上文定義的PipelineResource,所以resourceRef。name=ncs-git-source,同時reources。name也需要和上文task中定義的resources。name一致。

這裡還有另外一種寫法,如果你不想單獨定義PipelineResource,可以將taskrun裡的resources使用resourceSpec欄位替換,如下所示。

inputs

params

-

name

image

value

hub。c。163。com/test/ncs:v1。0。0

resources

-

name

ncs-git-source

resourceSpec

params

-

name

url

value

ssh://git@netease。com/test/ncs。git

-

name

revision

value

f-dockerfile

type

git

serviceAccount

nce-qingzhou

taskRef

name

ncs

當然,別忘記把上面建立的serviceaccount放到taskrun中,否則無法拉取私有git倉庫程式碼。

最後,我們可以把上面的檔案儲存,使用kubectl create -f ncs-taskrun。yml來開始一段taskrun的構建。

還需要提醒的是,taskrun只表示一次構建任務,你無法修改taskrun中的欄位讓它重新開始,所以我們沒有在taskrun的metadata中定義name,只加了generateName,這樣kubernetes會幫我們在taskrun name中自動加上一個hash值字尾,避免每次手動改名建立。

pipeline流水線

既然Tekton是一個CI/CD工具,我們除了用它來編譯和構建映象,還可以做更多,例如,加入一些自動化測試的流程,對接其他kubernetes叢集實現容器映象的更新部署。

當然,這一切都放到task裡的steps也未嘗不可,但是這樣無法抽象出各種task進行組織和複用,所以Tekton提供了更高一級的CRD描述,Pipeline和PipelineRun,Pipeline中可以引用很多task,而PipelineRun可用來執行Pipeline。Pipeline的yaml模版和task大同小異,這裡暫不詳述,相信你看一遍官方文件也能很快上手。

總結

雖然Tekton還很年輕,我們網易雲輕舟團隊已經開始在內部嘗試實踐,使用tekton作為內部服務的映象構建推送平臺。

隨著雲原生浪潮的到來,Kubernetes已經成為事實上的標準,Tekton正脫胎於這股浪潮之中,基於CRD、controller設計思想從一出生就註定會更適合kubernetes。相比其他老牌的CI/CD專案,Tekton還沒那麼的成熟,不過套用一句現在流行的話:

一代人終將老去,但總有人正年輕

。 看著目前的趨勢,未來可期。

參考:

1。 https://kurtmadel。com/posts/cicd-with-kubernetes/Tekton-standardizing-native-kubernetes-cd/

2。 https://developer。ibm。com/tutorials/knative-build-app-development-with-Tekton/

標簽: Tekton  name  映象  docker  git