天天看點

Tekton 如何接入實體機進行建構

1. 為什麼需要實體建構機

在文章《如何接入遠端 macOS 實體機進行 Jenkins 流水線建構》中,我描述了在 Jenkins 中添加實體建構機的方法。這并不是我拍腦袋想的需求,而是當時真的有 ToB 的商業客戶在咨詢方案。

對于多端開發商來說,建構 Android、IOS、macOS、Arm 、Windows、X86 應用是常見的需求。

好的方面是 GitHub Actions 提供了 macOS 建構環境、AWS 提供了 macOS 虛拟機,而華為提供了 ARM 主機。在雲原生背景下,更多使用的是 Kubernetes 運作時,在 Kubernetes 不支援的處理器架構和作業系統面前,持續內建 (CI) 顯得很無力。持續內建需要支援實體建構機運作時。

本文希望讨論的問題是在 Kubernetes 下,如何接入實體機進行 CI 的建構。本文以 Tekton 為例,其他引擎在處理邏輯上類似。

2. Tekton 如何與實體機互動

Tekton 如何接入實體機進行建構

Kuberntes 對實體機或者虛拟機的管理,實際上是一個典型的 Operator 場景。我們可以定義一個 CRD 用來描述相關字段,通過寫 Controller 處理 Pod 與建構機之間的邏輯。

也可以寫 Tekton 的 Task 封裝,本文将使用這種方式。由此也給我帶來另一個疑問,Tekton 能否代替部分 Operator 的場景,在後續的文章中我會給出思考。

這裡僅做原型驗證,不會太關注産品化的細節。

在 Tekton 中,每個流水線由很多個 Task 構成,Task 可以并行。一個 Task 包含很多個串行的 step 步驟,對應着一個 Pod 包含很多個容器。

這裡的關鍵是要将 Pod 與建構機關聯起來。我選擇的是使用 rsync 同步 Pod 與建構機之間的檔案,在 Pod 中使用 sshpass 執行實體機的建構指令。

主要分為如下步驟 (以下指令都是在容器中執行):

  1. 克隆代碼
  2. 執行 rsync 将代碼同步到建構機
  3. 執行 sshpass 在建構機上執行建構指令
  4. 執行 rsync 将建構機中的建構産物同步到容器
  5. 歸檔建構産物(示例中, 這一步會被省略,僅驗證能拿到建構産物)

可以看到整個過程其實和 Tekton 沒有直接關系,對于任意容器與建構機直連的環境都是可行的。下面以 Tekton 為例進行示範。

3. 資源準備清單

  • 一個 Kubernetes 叢集。用來運作 Tekton,最新的 Tekton 0.23 要求 Kubernetes 不低于 1.17
  • 一台實體機或虛拟機。用于建構應用

3.1 檢視 Kubernetes 版本

1

2

3

4

kubectl version

Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:23:52Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"darwin/amd64"}

Server Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.2", GitCommit:"faecb196815e248d3ecfb03c680a4507229c2a56", GitTreeState:"clean", BuildDate:"2021-01-21T01:11:42Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}

3.2 實體機準備

  • 作業系統是 CentOS 7.6

1

2

3

uname -a

Linux test 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

  • 預裝 Golang 的編譯環境

原計劃是選擇一個 macOS 的建構示例,但是無法提供直通的網絡環境,是以換成 Golang 的建構示例。

1

2

3

go version

go version go1.13 linux/amd64

4. 準備 Tekton 以及 Pipeline 資源

4.1 部署 Tekton Pipeline

  • 建立負載

Tekton 預設使用的是 gcr.io 鏡像,如果是國内環境可以替換為 gcr.azk8s.cn 鏡像。

1 kubectl apply -f https://github.com/tektoncd/pipeline/releases/download/v0.23.0/release.notags.yaml
  • 檢視資源

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

kubectl -n tekton-pipelines get all

NAME READY STATUS RESTARTS AGE

pod/tekton-pipelines-controller-86c487c965-p6s5t 1/1 Running 0 51s

pod/tekton-pipelines-webhook-7b775d9cd8-fzdrq 1/1 Running 0 51s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

service/tekton-pipelines-controller ClusterIP 10.233.61.46 <none> 9090/TCP,8080/TCP 51s

service/tekton-pipelines-webhook ClusterIP 10.233.46.233 <none> 9090/TCP,8008/TCP,443/TCP,8080/TCP 51s

NAME READY UP-TO-DATE AVAILABLE AGE

deployment.apps/tekton-pipelines-controller 1/1 1 1 51s

deployment.apps/tekton-pipelines-webhook 1/1 1 1 51s

NAME DESIRED CURRENT READY AGE

replicaset.apps/tekton-pipelines-controller-86c487c965 1 1 1 51s

replicaset.apps/tekton-pipelines-webhook-7b775d9cd8 1 1 1 51s

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE

horizontalpodautoscaler.autoscaling/tekton-pipelines-webhook Deployment/tekton-pipelines-webhook <unknown>/100% 1 5 1 51s

4.2 資源規劃

需要的流水線資源清單:

  • 一個 task, 用于克隆代碼
  • 一個 pv, 用于共享 task 之間的檔案
  • 一個自定義的 task, 用于将代碼同步到建構機,建構完成之後,再同步回來
  • 一個 pipeline, 用于描述流水線,編排 task
  • 一個 pipelinerun, 用于執行個體化 pipeline, 提供建構時必要的參數

4.2 編寫同步檔案、執行腳本的 Task

Tekton 如何接入實體機進行建構

如上圖,這裡的 Task 就是用于打通 container 和 vm 之間的檔案和程序,實作類似交叉編譯的效果。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

---

apiVersion: tekton.dev/v1beta1

kind: Task

metadata:

name: remote-shell

labels:

app.kubernetes.io/version: "0.1"

annotations:

tekton.dev/pipelines.minVersion: "0.12.1"

tekton.dev/tags: git

tekton.dev/displayName: "remote shell"

spec:

description: >-

This task can be used to run shell in remote machine

workspaces:

- name: source

params:

- name: remote-ip

type: string

- name: remote-port

type: string

- name: remote-username

type: string

- name: remote-password

type: string

- name: remote-workspace

type: string

- name: remote-script

type: string

steps:

- name: remote-shell

image: shaowenchen/rsync-sshpass:v1

workingDir: $(workspaces.source.path)

script: |

sshpass -p "$(params.remote-password)" ssh -o StrictHostKeyChecking=no "$(params.remote-username)"@"$(params.remote-ip)" -p "$(params.remote-port)" "mkdir -p $(params.remote-workspace)"

rsync -ratlz --progress --rsh="sshpass -p $(params.remote-password) ssh -o StrictHostKeyChecking=no -l $(params.remote-username)" ./ "$(params.remote-ip)":"$(params.remote-workspace)"

sshpass -p "$(params.remote-password)" ssh -o StrictHostKeyChecking=no "$(params.remote-username)"@"$(params.remote-ip)" -p "$(params.remote-port)" "$(params.remote-script)"

rsync -ratlz --progress --rsh="sshpass -p $(params.remote-password) ssh -o StrictHostKeyChecking=no -l $(params.remote-username)" "$(params.remote-ip)":"$(params.remote-workspace)"/ .

在寫法上,可以參考 Tekton 提供的示例。主要分為幾步:

  • 定義參數
  • 編寫 step 流程
  • 寫 script

這就是一個串腳本的過程,隻不過借助容器鏡像,省去了安裝各種工具的步驟。

4.3 準備 Tekton 的 pipeline 描述

  • 克隆代碼 Task

Tekton 已經正式上線 Hub 服務,用于共享 Task,這裡直接使用 https://hub.tekton.dev/tekton/task/git-clone

1 kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.3/git-clone.yaml
  • 建構一個工具箱鏡像 shaowenchen/rsync-sshpass:v1

Dockerfile 為:

1

2

3

4

5

6

7

8

9

10

11

12

13

ARG alpine_ver=3.13

FROM alpine:${alpine_ver}.5

RUN apk update \

&& apk upgrade \

&& apk add --no-cache \

rsync \

openssh-client \

openssh \

sshpass \

ca-certificates \

&& update-ca-certificates \

&& rm -rf /var/cache/apk/*

  • pipeline

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

apiVersion: tekton.dev/v1beta1

kind: Pipeline

metadata:

name: remote-build-pipeline

spec:

params:

- name: repo-url

type: string

- name: branch-name

type: string

- name: remote-ip

type: string

- name: remote-port

type: string

- name: remote-username

type: string

- name: remote-password

type: string

- name: remote-workspace

type: string

- name: remote-script

type: string

workspaces:

- name: shared-data

tasks:

- name: fetch-repo

taskRef:

name: git-clone

workspaces:

- name: output

workspace: shared-data

params:

- name: url

value: $(params.repo-url)

- name: revision

value: $(params.branch-name)

- name: remote-build

taskRef:

name: remote-shell

runAfter: ["fetch-repo"]

workspaces:

- name: source

workspace: shared-data

params:

- name: remote-ip

value: $(params.remote-ip)

- name: remote-port

value: $(params.remote-port)

- name: remote-username

value: $(params.remote-username)

- name: remote-password

value: $(params.remote-password)

- name: remote-workspace

value: $(params.remote-workspace)

- name: remote-script

value: $(params.remote-script)

pipeline 包含兩個 task,一個 task 克隆代碼,一個 task 執行遠端建構。

  • pipelinerun

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

---

apiVersion: tekton.dev/v1beta1

kind: PipelineRun

metadata:

name: remote-build-pipelinerun-1

spec:

pipelineRef:

name: remote-build-pipeline

workspaces:

- name: shared-data

volumeClaimTemplate:

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 10Gi

params:

- name: repo-url

value: https://github.com/shaowenchen/terraform-provider-qingcloud.git

- name: branch-name

value: master

- name: subdirectory

value: terraform-provider-qingcloud-001

- name: remote-ip

value: 0.0.0.0

- name: remote-port

value: "22"

- name: remote-username

value: root

- name: remote-password

value: YourPassword

- name: remote-workspace

value: ~/workspaces/terraform-provider-qingcloud-001

- name: remote-script

value: |

cd ~/workspaces/terraform-provider-qingcloud-001

make

這裡将克隆代碼到 pv 的 terraform-provider-qingcloud-001 目錄,同步到建構機的 ~/workspaces/terraform-provider-qingcloud-001 目錄。也就是說,這兩個目錄最終的檔案會保持一緻,而建構的二進制是在建構機上生成的。

  • 檢視 Tekton 資源定義

以上資源全部 apply 之後,就可以檢視相關的資源和流水線狀态了。

1

2

3

4

5

kubectl get tasks.tekton.dev

NAME AGE

git-clone 18m

remote-shell 5m47s

1

2

3

4

kubectl get pipelines.tekton.dev

NAME AGE

remote-build-pipeline 4m21s

1

2

3

4

kubectl get pipelineruns.tekton.dev

NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME

remote-build-pipelinerun-1 True Succeeded 6m15s 5m42s

在 pipelineruns 中可以通過 describe 拿到整個流水線執行的記錄,用于展示執行步驟,查詢建構日志。下面是截取的部分内容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

Task Runs:

remote-build-pipelinerun-1-fetch-repo-56ws8:

Pipeline Task Name: fetch-repo

Status:

Completion Time: 2021-04-27T13:22:08Z

Conditions:

Last Transition Time: 2021-04-27T13:22:08Z

Message: All Steps have completed executing

Reason: Succeeded

Status: True

Type: Succeeded

Pod Name: remote-build-pipelinerun-1-fetch-repo-56ws8-pod-mgx77

Start Time: 2021-04-27T13:21:54Z

Steps:

Container: step-clone

Image ID: docker-pullable://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init@sha256:db18a9c1607c8cbbcd72f61d0c4d795b9ff528669deacd5f8a1672e4ef198ffd

Name: clone

Terminated:

Container ID: docker://e5258bc7b0770e0333a93395eda13514abbd293652c0c0494352407a3fbc1a7f

Exit Code: 0

Finished At: 2021-04-27T13:22:07Z

Message: [{"key":"commit","value":"d024b4deeb2f328098fed88eb702cb19dac8452f","type":"TaskRunResult"},{"key":"url","value":"https://github.com/shaowenchen/terraform-provider-qingcloud.git","type":"TaskRunResult"}]

Reason: Completed

Started At: 2021-04-27T13:22:04Z

Task Results:

Name: commit

Value: d024b4deeb2f328098fed88eb702cb19dac8452f

Name: url

Value: https://github.com/shaowenchen/terraform-provider-qingcloud.git

5. 功能驗證

  • 檢視相關負載

1

2

3

4

5

kubectl get pod

NAME READY STATUS RESTARTS AGE

remote-build-pipelinerun-1-fetch-repo-56ws8-pod-mgx77 0/1 Completed 0 8m49s

remote-build-pipelinerun-1-remote-build-wxtms-pod-bcn6r 0/1 Completed 0 8m35s

  • 在實體建構機上,檢視建構目錄

1

2

3

4

5

6

7

8

pwd

/root/workspaces/terraform-provider-qingcloud-001

ls

CHANGELOG.md glide.yaml go.sum main.go qingcloud scripts terraform-provider-qingcloud website

dev.md go.mod LICENSE Makefile README.md terraform vendor

  • 檢視 Kubernetes PV 的建構目錄

1

2

3

4

kubectl get pv

NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS

pvc-860016bb-14b6-414a-9c5a-1a71d7290ba8 10Gi RWO Delete Bound default/pvc-e7ceb0582a openebs-hostpath 2m12s

查找 PV 存儲路徑

1

2

3

kubectl describe pv pvc-860016bb-14b6-414a-9c5a-1a71d7290ba8 |grep Path

Path: /var/openebs/local/pvc-860016bb-14b6-414a-9c5a-1a71d7290ba8

檢視 PV 目錄檔案結構

1

2

3

4

ls /var/openebs/local/pvc-860016bb-14b6-414a-9c5a-1a71d7290ba8

CHANGELOG.md glide.yaml go.sum main.go qingcloud scripts terraform-provider-qingcloud website

dev.md go.mod LICENSE Makefile README.md terraform vendor

在兩個目錄中,都存在建構産物 terraform-provider-qingcloud,符合預期,也說明我們達成了目标。

6. 總結

傳統的 CICD 引擎通常是一個 C/S 架構。它需要一個 S 端,用于解析流程,對流水線進行排程; 需要很多個 C 端,用于執行高負載的建構任務。這種方式的擴充性并不是線性的,在雲原生下、業務量大時很容易遇到瓶頸。是以,我們需要更加雲原生的建構引擎。在新的引擎下我們需要解決一些老的問題,支援實體機建構就是其中之一。

本文主要以 Tekton 為例,提供了一種利用 rsync 和 sshpass 接入實體機進行建構的思路。其中的關鍵點如下:

  • 使用 rsync\sshpass 的目的主要是将容器與實體機綁定,檔案雙向同步,程序空間互通。
  • 不限于 Tekton, 任意的引擎都可以使用這種方式。
  • 這裡僅是作為方案驗證,如果落地到産品,還需要考慮緩存、秘鑰安全、資料安全、租戶隔離等問題。

原文 https://www.chenshaowen.com/blog/how-to-add-physical-machines-to-tekton.html