多集群下的 Tekton 流水線
1. 多集群構建 Tekton 的優勢
借助于 Kubernetes, Tekton 已經具備很好的彈性, 能夠支持大規模構建。同時, 開發 Task 主要使用 Yaml 和 Shell, 這擴大了 Tekton 的各種場景適配范圍。
上面是一張 Tekton 在多集群下的示意圖。為什么 Tekton 需要多集群執行流水線?
- 隨時可變的 Kubernetes 集群。單一的 Kubernetes 集群, 無法滿足運維的要求, 不能隨時對集群進行變更。多集群下, 可以下架部分集群進行維護。
- 更大規模的構建。CI 對 CPU、內存、IO 資源的消耗很大, 容易壓垮節點甚至集群。多集群能有效分擔負載壓力,提高可用性。
- 業務隔離。業務對代碼安全等級、構建速度、構建環境要求不一樣, 多集群能夠提供隔離的環境, 定制化的流水線服務。
2. Kubernetes Cluster Federation
Kubernetes Cluster Federation 簡稱 KubeFed。KubeFed v2 相較于 v1 最大的改變是將 API Server 移除, 并且通過 CRD 機制完成 Federated Resource 的擴展。KubeFed Controller 管理這些 CRD, 并實現同步 Resources 跨集群編排等功能,實現模塊化和定制化。下面是社區的架構圖:
KubeFed 配置了兩種類型的信息:
- Type configuration, 聲明 KubeFed 處理的 API 類型
- Cluster configuration, 聲明 KubeFed 管理哪些集群
Type configuration 有三個基本概念:
- Templates, 定義資源在集群中的模板描述
- Placement, 定義資源需要分發到哪些集群
- Overrides, 定義在集群中,需要覆蓋 Templates 的字段內容
此外,通過 Status、Policy 和 Scheduling 可以實現更高級的功能:
- Status 收集分發資源在各個集群中的狀態
- Policy 允許將資源分配給哪些集群的策略控制
- Scheduling 允許資源跨集群遷移副本
除此,KubeFed 還提供了 MultiClusterDNS,可以用于多集群之間的服務發現。
3. 聯邦化 Kubernetes 集群
3.1 準備集群并配置 Context
這里部署兩個集群: dev1 作為主集群,用來作為 Tekton 的控制面,不運行流水線任務; dev2 作為子集群,用來執行 Tekton 流水線任務。
準備兩個集群
主集群 dev1
- kubectl get node
- NAME STATUS ROLES AGE VERSION
- node1 Ready control-plane,master,worker 151m v1.20.4
- helm version
- version.BuildInfo{Version:"v3.2.1", GitCommit:"fe51cd1e31e6a202cba7dead9552a6d418ded79a", GitTreeState:"clean", GoVersion:"go1.13.10"}
子集群 dev2
- kubectl get node
- NAME STATUS ROLES AGE VERSION
- node1 Ready control-plane,master,worker 42d v1.20.4
在主集群上配置全部集群的 Context(要求集群 Apiserver 入口在一個網絡,能夠直連),用來添加子集群
這里 contexts 中的 name 不能含義 @ 等特殊字符, 否則 join 時會報錯。因為 name 會用來創建 Secret, 需要符合 Kubernetes 的命名規范。
將主集群 dev1 的 kubeconfig 放在 ~/.kube/config-1,并修改 name 等信息,格式如下:
- apiVersion: v1
- clusters:
- - cluster:
- ...
- name: dev1.cluster.local
- contexts:
- - context:
- cluster: dev1.cluster.local
- user: dev1-kubernetes-admin
- name: dev1-context
- users:
- - name: dev1-kubernetes-admin
- user:
- ...
將子集群 dev2 的 kubeconfig 放在 ~/.kube/config-2,并修改 name 等信息,格式如下:
- apiVersion: v1
- clusters:
- - cluster:
- ...
- name: dev2.cluster.local
- contexts:
- - context:
- cluster: dev2.cluster.local
- user: dev2-kubernetes-admin
- name: dev2-context
- users:
- - name: dev2-kubernetes-admin
- user:
- ...
合并 kubeconfig
- cd $HOME/.kube/
- KUBECONFIG=config-1:config-2 kubectl config view --flatten > $HOME/.kube/config
查看添加的集群 Context
- kubectl config get-contexts
- CURRENT NAME CLUSTER AUTHINFO NAMESPACE
- dev1-context dev1.cluster.local dev1-kubernetes-admin
- dev2-context dev2.cluster.local dev2-kubernetes-admin
切換到主集群 dev1
- kubectl config use-context dev1-context
- Switched to context "dev1-context".
3.2 在主集群上安裝 KubeFed
使用 Helm 安裝 KubeFed
- git clone https://github.com/kubernetes-sigs/kubefed.git
- cd kubefed/charts/
- helm install kubefed ./kubefed/ --namespace kube-federation-system --create-namespace
查看負載
- kubectl get deploy,pod -n kube-federation-system
- NAME READY UP-TO-DATE AVAILABLE AGE
- deployment.apps/kubefed-admission-webhook 1/1 1 1 95s
- deployment.apps/kubefed-controller-manager 2/2 2 2 95s
- NAME READY STATUS RESTARTS AGE
- pod/kubefed-admission-webhook-598bd776c6-gv4qh 1/1 Running 0 95s
- pod/kubefed-controller-manager-6d9bf98d74-n8kjz 1/1 Running 0 17s
- pod/kubefed-controller-manager-6d9bf98d74-nmb2j 1/1 Running 0 14s
3.3 在主集群上安裝 kubefedctl
執行命令:
- wget https://github.com/kubernetes-sigs/kubefed/releases/download/v0.8.0/kubefedctl-0.8.0-linux-amd64.tgz
- tar -zxvf kubefedctl-*.tgz
- mv kubefedctl /usr/local/bin/
3.4 添加集群
在主集群上執行命令, 將 dev1、dev2 都添加到主集群 dev1 上。
- kubefedctl join dev1-context --host-cluster-context dev1-context --kubefed-namespace=kube-federation-system --v=2
- I0625 14:32:42.969373 25920 join.go:861] Using secret named: dev1-context-dev1-context-token-2w8km
- I0625 14:32:42.972316 25920 join.go:934] Created secret in host cluster named: dev1-context-ln6vx
- I0625 14:32:42.991399 25920 join.go:299] Created federated cluster resource
- kubefedctl join dev2-context --host-cluster-context dev1-context --kubefed-namespace=kube-federation-system --v=2
- I0625 14:33:11.836472 26424 join.go:861] Using secret named: dev2-context-dev1-context-token-dcl8s
- I0625 14:33:11.840121 26424 join.go:934] Created secret in host cluster named: dev2-context-264dz
- I0625 14:33:11.898044 26424 join.go:299] Created federated cluster resource
查看集群列表:
- kubectl -n kube-federation-system get kubefedclusters
- NAME AGE READY
- dev1-context 45s True
- dev2-context 16s True
3.5 測試集群是否聯邦成功
- 查看已經聯邦化的資源
安裝 KubeFed 之后,常見的很多資源都已經聯邦化,可以在 CRD 中查看:
- kubectl get crd |grep federated
- federatedclusterroles.types.kubefed.io 2021-06-26T06:22:50Z
- federatedconfigmaps.types.kubefed.io 2021-06-26T06:22:50Z
- federateddeployments.types.kubefed.io 2021-06-26T06:22:50Z
- federatedingresses.types.kubefed.io 2021-06-26T06:22:50Z
- federatedjobs.types.kubefed.io 2021-06-26T06:22:50Z
- federatednamespaces.types.kubefed.io 2021-06-26T06:22:50Z
- federatedreplicasets.types.kubefed.io 2021-06-26T06:22:50Z
- federatedsecrets.types.kubefed.io 2021-06-26T06:22:50Z
- federatedserviceaccounts.types.kubefed.io 2021-06-26T06:22:50Z
- federatedservices.types.kubefed.io 2021-06-26T06:22:50Z
- federatedservicestatuses.core.kubefed.io 2021-06-26T06:22:50Z
- federatedtypeconfigs.core.kubefed.io 2021-06-26T06:22:50Z
在 federatedtypeconfigs 中也可以看到已經開啟聯邦的資源。
- kubectl get federatedtypeconfigs.core.kubefed.io -n kube-federation-system
- NAME AGE
- clusterroles.rbac.authorization.k8s.io 29m
- configmaps 29m
- deployments.apps 29m
- ingresses.extensions 29m
- jobs.batch 29m
- namespaces 29m
- replicasets.apps 29m
- secrets 29m
- serviceaccounts 29m
- services 29m
- 創建一個聯邦的 Namespace
Namespace 級別的資源需要放置在聯邦化的 Namespace 下,否則在進行資源分發時,Controller 會報錯。
- apiVersion: v1
- kind: Namespace
- metadata:
- name: testing-fed
- ---
- apiVersion: types.kubefed.io/v1beta1
- kind: FederatedNamespace
- metadata:
- name: testing-fed
- namespace: testing-fed
- spec:
- placement:
- clusters:
- - name: dev1-context
- - name: dev2-context
- 在主集群創建一個聯邦的 Deployment
常見的 Deployment 是這樣:
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: nginx
- namespace: default
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: nginx
- template:
- metadata:
- labels:
- app: nginx
- spec:
- containers:
- - image: nginx
- name: nginx
而聯邦的 Deployment 是這樣。
- apiVersion: types.kubefed.io/v1beta1
- kind: FederatedDeployment
- metadata:
- name: nginx-fed
- namespace: testing-fed
- spec:
- overrides:
- - clusterName: dev1-context
- clusterOverrides:
- - path: /spec/replicas
- value: 2
- - clusterName: dev2-context
- clusterOverrides:
- - path: /spec/replicas
- value: 3
- placement:
- clusters:
- - name: dev1-context
- - name: dev2-context
- template:
- metadata:
- labels:
- app: nginx
- namespace: testing-fed
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: nginx
- template:
- metadata:
- labels:
- app: nginx
- spec:
- containers:
- - image: nginx
- name: nginx
FederatedDeployment 編寫時,需要注意三個字段
- overrides, 根據不同集群, 需要覆蓋的字段屬性。這里將 dev1 上的副本數改為 2,而將 dev2 上的副本數改為 3。
- placement, 資源需要放置的集群列表。這里放置在 dev1、dev2 兩個集群。
- template, 資源的模板。這里是 Deployment 去掉 apiVersion 和 kind 的剩余部分。
- 驗證資源是否分發成功
在 dev1 集群上
- kubectl -n testing-fed get pod
- NAME READY STATUS RESTARTS AGE
- nginx-fed-6799fc88d8-7llk9 1/1 Running 0 8m2s
- nginx-fed-6799fc88d8-clc5w 1/1 Running 0 8m2s
在 dev2 集群上
- kubectl -n testing-fed get pod
- NAME READY STATUS RESTARTS AGE
- nginx-fed-6799fc88d8-2ld4k 1/1 Running 0 7m49s
- nginx-fed-6799fc88d8-6dncp 1/1 Running 0 7m49s
- nginx-fed-6799fc88d8-x64fb 1/1 Running 0 7m49s
4. 聯邦化 Tekton 的 CRD 資源
4.1 安裝 Tekton
在所有集群上都需要安裝 Tekton
- kubectl apply -f https://raw.githubusercontent.com/shaowenchen/scripts/main/image-sync/tektondev/dockerhub/release-0.24.1.yaml
由于 Tekton 社區使用的是 gcr.io 的鏡像, 有些主機環境上可能無法拉取。我在 Dockerhub 上對其進行了備份, 在這里可以找到相關的 yaml, https://github.com/shaowenchen/scripts/tree/main/image-sync/tektondev/dockerhub 。
4.2 聯邦化 Tekton 的 CRD
安裝 KubeFed 時, 會默認將常見的 Deployment、Secret 等聯邦化, 但如果是用戶自定義的 CRD 就需要手動開啟。
執行命令:
- kubefedctl enable clustertasks.tekton.dev
- kubefedctl enable conditions.tekton.dev
- kubefedctl enable pipelineresources.tekton.dev
- kubefedctl enable pipelineruns.tekton.dev
- kubefedctl enable pipelines.tekton.dev
- kubefedctl enable runs.tekton.dev
- kubefedctl enable taskruns.tekton.dev
- kubefedctl enable tasks.tekton.dev
以 taskruns 為例, kubefedctl enable taskruns.tekton.dev 會自動創建兩個資源:
- customresourcedefinition.apiextensions.k8s.io/federatedtaskruns.types.kubefed.io, 聯邦 CRD 資源 federatedtaskruns
- federatedtypeconfig.core.kubefed.io/taskruns.tekton.dev, 在 kube-federation-system 命名空間下, 創建 federatedtypeconfig 類型的資源 taskruns 開啟資源分發使能
4.3 編輯新創建的聯邦 CRD 資源添加字段
缺少這一步, 會導致同步到子集群的 CR 資源內容為空。因為 kubefedctl enable 聯邦化 CRD 資源缺少 template 字段。
執行命令:
- kubectl edit crd federatedtasks.types.kubefed.io
在與 overrides 、placement 平級的層次,添加下面示例的 template 內容即可。
- apiVersion: apiextensions.k8s.io/v1
- ...
- spec:
- versions:
- - name: v1beta1
- schema:
- openAPIV3Schema:
- properties:
- spec:
- properties:
- overrides:
- ...
- placement:
- ...
- template:
- type: object
- x-kubernetes-preserve-unknown-fields: true
- type: object
如果覺得不夠清晰,可以參考 https://github.com/shaowenchen/scripts/tree/main/image-sync/tektondev/kubefed 修改。如果你也是使用版本 0.24.1, 可以直接 kubectl apply 這些 CRD 資源。
4.4 測試多集群下分發 Tekton 對象
這里為了避免粘貼大量 yaml, 直接提前預先在子集群上創建 Task 資源, 而沒有使用 FederatedTask 進行分發。
- 在子集群上創建 Task
- kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.4/git-clone.yaml -n testing-fed
- 在主集群 dev1 上創建 FederatedTaskRun 資源分發到子集群 dev2
- apiVersion: types.kubefed.io/v1beta1
- kind: FederatedTaskRun
- metadata:
- name: git-clone-test
- namespace: testing-fed
- spec:
- placement:
- clusters:
- - name: dev2-context
- template:
- metadata:
- namespace: testing-fed
- spec:
- workspaces:
- - name: output
- emptyDir: {}
- taskRef:
- name: git-clone
- params:
- - name: url
- value: https://github.com/kelseyhightower/nocode
- 在子集群 dev2 上查看 Tekton 的 Taskrun 任務
- kubectl get taskrun -n testing-fed
- NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
- git-clone-test True Succeeded 15s 7s
5. 總結
本文主要介紹并實踐了利用 KubeFed 管理多集群,對 Tekton CRD 資源進行聯邦化。
多集群下的 Tekton,使用主集群管理資源,使用子集群執行流水線,能夠有效均衡負載,增加流水線的并發執行量,提高 CICD 系統的可維護性。
這里的 KubeFed 主要是用來存儲并分發 Tekton 對象資源。如果自研編碼,可以通過數據存儲加循環控制器完成,但是利用 KubeFed Controller 能快速實現,同時避免了很多潛在的問題。KubeFed 用于做跨集群的資源分發,非常適用。
6. 參考
https://github.com/kubernetes-sigs/kubefed