Tekton 如何接入物理機進行構建
本文轉(zhuǎn)載自微信公眾號「問其」,作者陳少文 。轉(zhuǎn)載本文請聯(lián)系問其公眾號。
1. 為什么需要物理構建機
在文章《如何接入遠程 macOS 物理機進行 Jenkins 流水線構建》中,我描述了在 Jenkins 中添加物理構建機的方法。這并不是我拍腦袋想的需求,而是當時真的有 ToB 的商業(yè)客戶在咨詢方案。
對于多端開發(fā)商來說,構建 Android、IOS、macOS、Arm 、Windows、X86 應用是常見的需求。
好的方面是 GitHub Actions 提供了 macOS 構建環(huán)境、AWS 提供了 macOS 虛擬機,而華為提供了 ARM 主機。在云原生背景下,更多使用的是 Kubernetes 運行時,在 Kubernetes 不支持的處理器架構和操作系統(tǒng)面前,持續(xù)集成 (CI) 顯得很無力。持續(xù)集成需要支持物理構建機。
本文希望討論的問題是在 Kubernetes 下,如何接入物理機進行 CI 的構建。本文以 Tekton 為例,其他引擎在處理邏輯上類似。
2. Tekton 如何與物理機交互
Kuberntes 對物理機或者虛擬機的管理,實際上是一個典型的 Operator 場景。我們可以定義一個 CRD 用來描述相關字段,通過寫 Controller 處理 Pod 與構建機之間的邏輯。
也可以寫 Tekton 的 Task 封裝,本文將使用這種方式。由此也給我?guī)砹硪粋€疑問,Tekton 能否代替部分 Operator 的場景,在后續(xù)的文章中我會給出思考。
這里僅做原型驗證,不會太關注產(chǎn)品化的細節(jié)。
在 Tekton 中,每個流水線由很多個 Task 構成,Task 可以并行。一個 Task 包含很多個串行的 step 步驟,對應著一個 Pod 包含很多個容器。
這里的關鍵是要將 Pod 與構建機關聯(lián)起來。我選擇的是使用 rsync 同步 Pod 與構建機之間的文件,在 Pod 中使用 sshpass 執(zhí)行物理機的構建命令。
主要分為如下步驟 (以下命令都是在容器中執(zhí)行):
- 克隆代碼
- 執(zhí)行 rsync 將代碼同步到構建機
- 執(zhí)行 sshpass 在構建機上執(zhí)行構建命令
- 執(zhí)行 rsync 將構建機中的構建產(chǎn)物同步到容器
- 歸檔構建產(chǎn)物(示例中, 這一步會被省略,僅驗證能拿到構建產(chǎn)物)
可以看到整個過程其實和 Tekton 沒有直接關系,對于任意容器與構建機直連的環(huán)境都是可行的。下面以 Tekton 為例進行演示。
3. 資源準備清單
- 一個 Kubernetes 集群。用來運行 Tekton,最新的 Tekton 0.23 要求 Kubernetes 不低于 1.17
- 一臺物理機或虛擬機。用于構建應用
3.1 查看 Kubernetes 版本
- 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 物理機準備
- 操作系統(tǒng)是 CentOS 7.6
- 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 的編譯環(huán)境
原計劃是選擇一個 macOS 的構建示例,但是無法提供直通的網(wǎng)絡環(huán)境,因此換成 Golang 的構建示例。
- go version
- go version go1.13 linux/amd64
4. 準備 Tekton 以及 Pipeline 資源
4.1 部署 Tekton Pipeline
- 創(chuàng)建負載
Tekton 默認使用的是 gcr.io 鏡像,如果是國內(nèi)環(huán)境可以替換為 gcr.azk8s.cn 鏡像。
- kubectl apply -f https://github.com/tektoncd/pipeline/releases/download/v0.23.0/release.notags.yaml
- 查看資源
4.2 資源規(guī)劃
- 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
需要的流水線資源清單:
- 一個 task, 用于克隆代碼
- 一個 pv, 用于共享 task 之間的文件
- 一個自定義的 task, 用于將代碼同步到構建機,構建完成之后,再同步回來
- 一個 pipeline, 用于描述流水線,編排 task
- 一個 pipelinerun, 用于實例化 pipeline, 提供構建時必要的參數(shù)
4.2 編寫同步文件、執(zhí)行腳本的 Task
如上圖,這里的 Task 就是用于打通 container 和 vm 直接的文件和進程,實現(xiàn)類似交叉編譯的效果。
- ---
- 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 提供的示例。主要分為幾步:
- 定義參數(shù)
- 編寫 step 流程
- 寫 script
這就是一個串腳本的過程,只不過借助容器鏡像,省去了安裝各種工具的步驟。
4.3 準備 Tekton 的 pipeline 描述
- 克隆代碼 Task
Tekton 已經(jīng)正式上線 Hub 服務,用于共享 Task,這里直接使用 https://hub.tekton.dev/tekton/task/git-clone
- kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.3/git-clone.yaml
- 構建一個工具箱鏡像 shaowenchen/rsync-sshpass:v1
Dockerfile 為:
- 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
- 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 執(zhí)行遠程構建。
- pipelinerun
- ---
- 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 之后,就可以查看相關的資源和流水線狀態(tài)了。
- kubectl get task
- NAME AGE
- git-clone 18m
- remote-shell 5m47s
- kubectl get pipelinerun
- NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
- remote-build-pipelinerun-1 True Succeeded 6m15s 5m42s
執(zhí)行成功,接著繼續(xù)驗證功能是否符合預期。
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
- 在物理構建機上,查看構建目錄
- 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 的構建目錄
- 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 存儲路徑
- kubectl describe pv pvc-860016bb-14b6-414a-9c5a-1a71d7290ba8 |grep Path
- Path: /var/openebs/local/pvc-860016bb-14b6-414a-9c5a-1a71d7290ba8
- 查看 PV 目錄文件結構
- 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
在兩個目錄中,都存在構建產(chǎn)物 terraform-provider-qingcloud,符合預期,也說明我們達成了目標。
6. 總結
傳統(tǒng)的 CICD 引擎通常是一個 C/S 架構。它需要一個 S 端,用于解析流程,對流水線進行調(diào)度; 需要很多個 C 端,用于執(zhí)行高負載的構建任務。這種方式的擴展性并不是線性的,在云原生下、業(yè)務量大時很容易遇到瓶頸。因此,我們需要更加云原生的構建引擎。在新的引擎下我們需要解決一些老的問題,支持物理機構建就是其中之一。
本文主要以 Tekton 為例,提供了一種利用 rsync 和 sshpass 接入物理機進行構建的思路。其中的關鍵點如下:
- 使用 rsync\sshpass 的目的主要是將容器與物理機綁定,文件雙向同步,進程空間互通。
- 不限于 Tekton, 任意的引擎都可以使用這種方式。
- 這里僅是作為方案驗證,如果落地到產(chǎn)品,還需要考慮緩存、秘鑰安全、數(shù)據(jù)安全、租戶隔離等問題。