K8s 多集群編排平臺(tái) Karmada 入門(mén)
Karmada(Kubernetes Armada)是 CNCF 孵化的一個(gè) Kubernetes 管理系統(tǒng),使您能夠在多個(gè) Kubernetes 集群和云中運(yùn)行云原生應(yīng)用程序,而無(wú)需更改應(yīng)用程序。通過(guò)使用 Kubernetes 原生 API 并提供先進(jìn)的調(diào)度功能,Karmada 實(shí)現(xiàn)了真正的開(kāi)放式、多云 Kubernetes。

Karmada 旨在為多云和混合云場(chǎng)景下的多集群應(yīng)用程序管理提供即插即用的自動(dòng)化,具有集中式多云管理、高可用性、故障恢復(fù)和流量調(diào)度等關(guān)鍵功能。
特性
- 兼容 K8s 原生 API
 
從單集群到多集群的無(wú)侵入式升級(jí)
現(xiàn)有 K8s 工具鏈的無(wú)縫集成
- 開(kāi)箱即用
 - 針對(duì)場(chǎng)景內(nèi)置策略集,包括:Active-active、Remote DR、Geo Redundant 等。
 - 在多集群上進(jìn)行跨集群應(yīng)用程序自動(dòng)伸縮、故障轉(zhuǎn)移和負(fù)載均衡。
 - 避免供應(yīng)商鎖定
 - 與主流云提供商集成
 - 在集群之間自動(dòng)分配、遷移
 - 未綁定專有供應(yīng)商編排
 - 集中式管理
 - 位置無(wú)關(guān)的集群管理
 - 支持公有云、本地或邊緣上的集群。
 - 豐富多集群調(diào)度策略
 - 集群親和性、實(shí)例在多集群中的拆分調(diào)度/再平衡,
 - 多維 HA:區(qū)域/AZ/集群/提供商
 - 開(kāi)放和中立
 - 由互聯(lián)網(wǎng)、金融、制造業(yè)、電信、云提供商等聯(lián)合發(fā)起。
 - 目標(biāo)是與 CNCF 一起進(jìn)行開(kāi)放治理。
 
Karmada 架構(gòu)
Karmada 的架構(gòu)非常類似于單個(gè) Kubernetes 集群,他們都有一個(gè)控制平面、一個(gè) APIServer、一個(gè)調(diào)度器和一組控制器,而且 Karmada 完全兼容 K8s 的原生 API 操作,便于各種 K8s 集群的接入。

Karmada 架構(gòu)
所以同樣 Karmada 的核心是其控制平面,一個(gè)完整且可工作的 Karmada 控制平面由以下組件組成。其中 karmada-agent 可以是可選的,這取決于集群注冊(cè)模式。
karmada-apiserver
APIServer 是 Karmada 控制平面的一個(gè)組件,對(duì)外暴露 Karmada API 以及 Kubernetes 原生 API,APIServer 是 Karmada 控制平面的前端。
Karmada APIServer 是直接使用 Kubernetes 的 kube-apiserver 實(shí)現(xiàn)的,因此 Karmada 與 Kubernetes API 自然兼容。這也使得 Karmada 更容易實(shí)現(xiàn)與 Kubernetes 生態(tài)系統(tǒng)的集成,例如允許用戶使用 kubectl 來(lái)操作 Karmada、與 ArgoCD 集成、與 Flux 集成等等。
karmada-aggregated-apiserver
聚合 API 服務(wù)器是使用 Kubernetes API 聚合層技術(shù)實(shí)現(xiàn)的擴(kuò)展 API 服務(wù)器。它提供了集群 API 以及相應(yīng)的子資源,例如 cluster/status 和 cluster/proxy,實(shí)現(xiàn)了聚合 Kubernetes API Endpoint 等可以通過(guò) karmada-apiserver 訪問(wèn)成員集群的高級(jí)功能。
kube-controller-manager
kube-controller-manager 由一組控制器組成,Karmada 只是從 Kubernetes 的官方版本中挑選了一些控制器,以保持與原生控制器一致的用戶體驗(yàn)和行為。值得注意的是,并非所有的原生控制器都是 Karmada 所需要的。
注意:當(dāng)用戶向 Karmada APIServer 提交 Deployment 或其他 Kubernetes 標(biāo)準(zhǔn)資源時(shí),它們只記錄在 Karmada 控制平面的 etcd 中。隨后,這些資源會(huì)向成員集群同步。然而,這些部署資源不會(huì)在 Karmada 控制平面集群中進(jìn)行 reconcile 過(guò)程(例如創(chuàng)建 Pod)。
karmada-controller-manager
Karmada 控制器管理器運(yùn)行了各種自定義控制器進(jìn)程??刂破髫?fù)責(zé)監(jiān)視 Karmada 對(duì)象,并與底層集群的 API 服務(wù)器通信,以創(chuàng)建原生的 Kubernetes 資源。
karmada-scheduler
karmada-scheduler 負(fù)責(zé)將 Kubernetes 原生 API 資源對(duì)象(以及 CRD 資源)調(diào)度到成員集群。
調(diào)度器依據(jù)策略約束和可用資源來(lái)確定哪些集群對(duì)調(diào)度隊(duì)列中的資源是可用的,然后調(diào)度器對(duì)每個(gè)可用集群進(jìn)行打分排序,并將資源綁定到最合適的集群。
karmada-webhook
karmada-webhook 是用于接收 karmada/Kubernetes API 請(qǐng)求的 HTTP 回調(diào),并對(duì)請(qǐng)求進(jìn)行處理。你可以定義兩種類型的 karmada-webhook,即驗(yàn)證性質(zhì)的 webhook 和修改性質(zhì)的 webhook。修改性質(zhì)的準(zhǔn)入 webhook 會(huì)先被調(diào)用。它們可以更改發(fā)送到 Karmada API 服務(wù)器的對(duì)象以執(zhí)行自定義的設(shè)置默認(rèn)值操作。
在完成了所有對(duì)象修改并且 Karmada API 服務(wù)器也驗(yàn)證了所傳入的對(duì)象之后,驗(yàn)證性質(zhì)的 webhook 會(huì)被調(diào)用,并通過(guò)拒絕請(qǐng)求的方式來(lái)強(qiáng)制實(shí)施自定義的策略。
etcd
一致且高可用的鍵值存儲(chǔ),用作 Karmada 的所有 Karmada/Kubernetes 資源對(duì)象數(shù)據(jù)的后臺(tái)數(shù)據(jù)庫(kù)。
如果你的 Karmada 使用 etcd 作為其后臺(tái)數(shù)據(jù)庫(kù),請(qǐng)確保你針對(duì)這些數(shù)據(jù)有一份備份計(jì)劃。
karmada-agent
Karmada 有 Push 和 Pull 兩種集群注冊(cè)模式,karmada-agent 應(yīng)部署在每個(gè) Pull 模式的成員集群上。它可以將特定集群注冊(cè)到 Karmada 控制平面,并將工作負(fù)載清單從 Karmada 控制平面同步到成員集群。此外,它也負(fù)責(zé)將成員集群及其資源的狀態(tài)同步到 Karmada 控制平面。
插件(Addons)
- karmada-scheduler-estimator
 
Karmada 調(diào)度估計(jì)器為每個(gè)成員集群運(yùn)行精確的調(diào)度預(yù)估,它為調(diào)度器提供了更準(zhǔn)確的集群資源信息。
注意:早期的 Karmada 調(diào)度器只支持根據(jù)集群資源的總量來(lái)決策可調(diào)度副本的數(shù)量。在這種情況下,當(dāng)集群資源的總量足夠但每個(gè)節(jié)點(diǎn)資源不足時(shí),會(huì)發(fā)生調(diào)度失敗。為了解決這個(gè)問(wèn)題,引入了估計(jì)器組件,該組件根據(jù)資源請(qǐng)求計(jì)算每個(gè)節(jié)點(diǎn)的可調(diào)度副本的數(shù)量,從而計(jì)算出真正的整個(gè)集群的可調(diào)度副本的數(shù)量。
- karmada-descheduler
 
Karmada 重調(diào)度組件負(fù)責(zé)定時(shí)檢測(cè)所有副本(默認(rèn)為兩分鐘),并根據(jù)成員集群中副本實(shí)例狀態(tài)的變化觸發(fā)重新調(diào)度。
該組件是通過(guò)調(diào)用 karmada-scheduler-estimator 來(lái)感知有多少副本實(shí)例狀態(tài)發(fā)生了變化,并且只有當(dāng)副本的調(diào)度策略為動(dòng)態(tài)劃分時(shí),它才會(huì)發(fā)揮作用。
- karmada-search
 
Karmada 搜索組件以聚合服務(wù)的形式,提供了在多云環(huán)境中進(jìn)行全局搜索和資源代理等功能。
其中,全局搜索能力是用來(lái)跨多個(gè)集群緩存資源對(duì)象和事件,以及通過(guò)搜索 API 對(duì)外提供圖形化的檢索服務(wù);資源代理能力使用戶既可以訪問(wèn) Karmada 控制平面所有資源,又可以訪問(wèn)成員集群中的所有資源。
CLI 工具
- karmadactl
 
Karmada 提供了一個(gè)命令行工具 karmadactl,用于使用 Karmada API 與 Karmada 的控制平面進(jìn)行通信。
你可以使用 karmadactl 執(zhí)行成員集群的添加/剔除,將成員集群標(biāo)記/取消標(biāo)記為不可調(diào)度,等等。
- kubectl karmada
 
kubectl karmada 以 kubectl 插件的形式提供功能,但它的實(shí)現(xiàn)與 karmadactl 完全相同。
安裝
首先要注意我們使用 Karmada 管理的多集群包含兩類:
- host 集群:即由 karmada 控制面構(gòu)成的集群,接受用戶提交的工作負(fù)載部署需求,將之同步到 member 集群,并從 member 集群同步工作負(fù)載后續(xù)的運(yùn)行狀況。
 - member 集群:由一個(gè)或多個(gè) K8s 集群構(gòu)成,負(fù)責(zé)運(yùn)行用戶提交的工作負(fù)載
 
所以首先我們需要準(zhǔn)備幾個(gè) K8s 集群用于測(cè)試,其中 host 集群就是我們要安裝 Karmada 的集群,這里我們可以使用 KinD 部署一個(gè) host 集群以及兩個(gè) member 集群,用于測(cè)試 Karmada 的多集群管理功能,當(dāng)然首先需要在你的測(cè)試環(huán)境中安裝 Docker 和 KinD。
$ docker version
Client:
 Cloud integration: v1.0.29
 Version:           20.10.21
 API version:       1.41
 Go version:        go1.18.7
 Git commit:        baeda1f
 Built:             Tue Oct 25 18:01:18 2022
 OS/Arch:           darwin/arm64
 Context:           orbstack
 Experimental:      true
Server: Docker Engine - Community
 Engine:
  Version:          25.0.5
  API version:      1.44 (minimum version 1.24)
  Go version:       go1.21.8
  Git commit:       e63daec
  Built:            Tue Mar 19 15:05:27 2024
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          v1.7.13
  GitCommit:        7c3aca7a610df76212171d200ca3811ff6096eb8
 runc:
  Version:          1.1.12
  GitCommit:        51d5e94601ceffbbd85688df1c928ecccbfa4685
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
$ kind version
kind v0.20.0 go1.20.4 darwin/arm64然后,我們可以使用 Karmada 官方提供的 create-cluster.sh 腳本來(lái)創(chuàng)建兩個(gè) member 集群。
$ git clone https://github.com/karmada-io/karmada.git
$ cd karmada
# 創(chuàng)建 host 集群
$ hack/create-cluster.sh host $HOME/.kube/host.config
$ kubectl get nodes --context host --kubeconfig /Users/cnych/.kube/host.config
NAME                 STATUS   ROLES           AGE   VERSION
host-control-plane   Ready    control-plane   63s   v1.27.3
# 創(chuàng)建 member1 集群
$ hack/create-cluster.sh member1 $HOME/.kube/member1.config
$ kubectl get nodes --context member1 --kubeconfig /Users/cnych/.kube/member1.config
NAME                    STATUS   ROLES           AGE    VERSION
member1-control-plane   Ready    control-plane   115s   v1.27.3
# 創(chuàng)建 member2 集群
$ hack/create-cluster.sh member2 $HOME/.kube/member2.config
$ kubectl get nodes --context member2 --kubeconfig /Users/cnych/.kube/member2.config
NAME                    STATUS   ROLES           AGE   VERSION
member2-control-plane   Ready    control-plane   29s   v1.27.3到這里我們就準(zhǔn)備好了一個(gè) host 集群和兩個(gè) member 集群,接下來(lái)我們就可以在 host 集群上安裝 Karmada 了。安裝 Karmada 的方法有很多,可以直接使用官方的 CLI 工具,也可以使用 Helm Chart 方式,還可以使用 Operator 方式等等,如果需要定制化安裝,使用 Helm Chart 的方式會(huì)更加靈活。由于官方提供的 CLI 工具并不只是用于安裝 Karmada,還可以用于管理 Karmada 集群,所以無(wú)論如何我們都可以先安裝 CLI 工具 - karmadactl,karmadactl 是允許你控制 Karmada 控制面的 Karmada 命令行工具,此外還提供一個(gè) kubectl 插件 kubectl-karmada,盡管這兩個(gè)工具的名字不同,但其關(guān)聯(lián)的命令和選項(xiàng)完全相同,所以無(wú)論使用哪一個(gè)都是一樣的,在實(shí)際使用中,你可以根據(jù)自己的需求選擇一個(gè) CLI 工具。
直接使用下面的命令即可一鍵安裝 karmadactl:
$ sudo ./hack/install-cli.sh
[INFO]  Downloading metadata https://api.github.com/repos/karmada-io/karmada/releases/latest
[INFO]  Using 1.9.1 as release
[INFO]  Downloading hash https://github.com/karmada-io/karmada/releases/download/v1.9.1/karmadactl-darwin-arm64.tgz.sha256
[INFO]  Downloading binary https://github.com/karmada-io/karmada/releases/download/v1.9.1/karmadactl-darwin-arm64.tgz
[INFO]  Verifying binary download
[INFO]  Installing karmadactl to /usr/local/bin/karmadactl
$ karmadactl version
karmadactl version: version.Info{GitVersion:"v1.9.1", GitCommit:"b57bff17d6133deb26d9c319714170a915d4fa54", GitTreeState:"clean", BuildDate:"2024-04-30T02:03:53Z", GoVersion:"go1.20.11", Compiler:"gc", Platform:"darwin/arm64"}安裝 kubectl-karmada 與安裝 karmadactl 相同,你只需要添加一個(gè) kubectl-karmada 參數(shù)即可:
$ sudo ./hack/install-cli.sh kubectl-karmada
[INFO]  Downloading metadata https://api.github.com/repos/karmada-io/karmada/releases/latest
[INFO]  Using 1.9.1 as release
[INFO]  Downloading hash https://github.com/karmada-io/karmada/releases/download/v1.9.1/kubectl-karmada-darwin-arm64.tgz.sha256
[INFO]  Downloading binary https://github.com/karmada-io/karmada/releases/download/v1.9.1/kubectl-karmada-darwin-arm64.tgz
[INFO]  Verifying binary download
[INFO]  Installing kubectl-karmada to /usr/local/bin/kubectl-karmada
$ kubectl karmada version
kubectl karmada version: version.Info{GitVersion:"v1.9.1", GitCommit:"b57bff17d6133deb26d9c319714170a915d4fa54", GitTreeState:"clean", BuildDate:"2024-04-30T02:03:52Z", GoVersion:"go1.20.11", Compiler:"gc", Platform:"darwin/arm64"}接下來(lái)我們就可以在 host 集群上安裝 Karmada 了,我們已將 host 集群的 kubeconfig 文件放到了 $HOME/.kube/config。直接執(zhí)行以下命令即可進(jìn)行安裝:
# --kube-image-mirror-country 用于指定鏡像國(guó)內(nèi)源
# --etcd-storage-mode 用于指定 etcd 存儲(chǔ)模式,支持 emptyDir、hostPath、PVC,默認(rèn)為 hostPath
$ sudo kubectl karmada init --kube-image-mirror-country=cn --etcd-storage-mode PVC --storage-classes-name standard --kubecnotallow=$HOME/.kube/host.config
I0516 15:56:35.549617   98690 deploy.go:244] kubeconfig file: /Users/cnych/.kube/host.config, kubernetes: https://192.168.247.4:6443
I0516 15:56:35.586638   98690 deploy.go:264] karmada apiserver ip: [192.168.247.4]
I0516 15:56:36.330162   98690 cert.go:246] Generate ca certificate success.
I0516 15:56:36.368464   98690 cert.go:246] Generate karmada certificate success.
I0516 15:56:36.453671   98690 cert.go:246] Generate apiserver certificate success.
I0516 15:56:36.535924   98690 cert.go:246] Generate front-proxy-ca certificate success.
I0516 15:56:36.666694   98690 cert.go:246] Generate front-proxy-client certificate success.
I0516 15:56:36.716602   98690 cert.go:246] Generate etcd-ca certificate success.
I0516 15:56:36.772838   98690 cert.go:246] Generate etcd-server certificate success.
I0516 15:56:36.905275   98690 cert.go:246] Generate etcd-client certificate success.
I0516 15:56:36.905808   98690 deploy.go:360] download crds file:https://github.com/karmada-io/karmada/releases/download/v1.9.1/crds.tar.gz
Downloading...[ 100.00% ]
Download complete.
I0516 15:56:39.224167   98690 deploy.go:620] Create karmada kubeconfig success.
I0516 15:56:39.300133   98690 idempotency.go:267] Namespace karmada-system has been created or updated.
I0516 15:56:39.352865   98690 idempotency.go:291] Service karmada-system/etcd has been created or updated.
I0516 15:56:39.353105   98690 deploy.go:426] Create etcd StatefulSets
I0516 15:57:02.386423   98690 deploy.go:435] Create karmada ApiServer Deployment
I0516 15:57:02.412127   98690 idempotency.go:291] Service karmada-system/karmada-apiserver has been created or updated.
I0516 15:57:33.480629   98690 deploy.go:450] Create karmada aggregated apiserver Deployment
I0516 15:57:33.488145   98690 idempotency.go:291] Service karmada-system/karmada-aggregated-apiserver has been created or updated.
I0516 15:57:48.545482   98690 idempotency.go:267] Namespace karmada-system has been created or updated.
I0516 15:57:48.547067   98690 deploy.go:85] Initialize karmada bases crd resource `/etc/karmada/crds/bases`
I0516 15:57:48.549059   98690 deploy.go:240] Attempting to create CRD
I0516 15:57:48.569222   98690 deploy.go:250] Create CRD cronfederatedhpas.autoscaling.karmada.io successfully.
# ......省略部分輸出
I0516 15:57:49.963201   98690 deploy.go:96] Initialize karmada patches crd resource `/etc/karmada/crds/patches`
I0516 15:57:50.372020   98690 deploy.go:108] Create MutatingWebhookConfiguration mutating-config.
I0516 15:57:50.379939   98690 webhook_configuration.go:362] MutatingWebhookConfiguration mutating-config has been created or updated successfully.
I0516 15:57:50.379957   98690 deploy.go:113] Create ValidatingWebhookConfiguration validating-config.
I0516 15:57:50.387416   98690 webhook_configuration.go:333] ValidatingWebhookConfiguration validating-config has been created or updated successfully.
I0516 15:57:50.387434   98690 deploy.go:119] Create Service 'karmada-aggregated-apiserver' and APIService 'v1alpha1.cluster.karmada.io'.
I0516 15:57:50.390795   98690 idempotency.go:291] Service karmada-system/karmada-aggregated-apiserver has been created or updated.
I0516 15:57:50.394479   98690 check.go:42] Waiting for APIService(v1alpha1.cluster.karmada.io) condition(Available), will try
I0516 15:57:51.506085   98690 tlsbootstrap.go:49] [bootstrap-token] configured RBAC rules to allow Karmada Agent Bootstrap tokens to post CSRs in order for agent to get long term certificate credentials
I0516 15:57:51.508289   98690 tlsbootstrap.go:63] [bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Karmada Agent Bootstrap Token
I0516 15:57:51.511340   98690 tlsbootstrap.go:77] [bootstrap-token] configured RBAC rules to allow certificate rotation for all agent client certificates in the member cluster
I0516 15:57:51.635344   98690 deploy.go:143] Initialize karmada bootstrap token
I0516 15:57:51.656584   98690 deploy.go:468] Create karmada kube controller manager Deployment
I0516 15:57:51.671152   98690 idempotency.go:291] Service karmada-system/kube-controller-manager has been created or updated.
I0516 15:57:58.728859   98690 deploy.go:482] Create karmada scheduler Deployment
I0516 15:58:10.763913   98690 deploy.go:493] Create karmada controller manager Deployment
I0516 15:58:22.787659   98690 deploy.go:504] Create karmada webhook Deployment
I0516 15:58:22.798328   98690 idempotency.go:291] Service karmada-system/karmada-webhook has been created or updated.
------------------------------------------------------------------------------------------------------
 █████   ████   █████████   ███████████   ██████   ██████   █████████   ██████████     █████████
??███   ███?   ███?????███ ??███?????███ ??██████ ██████   ███?????███ ??███????███   ███?????███
 ?███  ███    ?███    ?███  ?███    ?███  ?███?█████?███  ?███    ?███  ?███   ??███ ?███    ?███
 ?███████     ?███████████  ?██████████   ?███??███ ?███  ?███████████  ?███    ?███ ?███████████
 ?███??███    ?███?????███  ?███?????███  ?███ ???  ?███  ?███?????███  ?███    ?███ ?███?????███
 ?███ ??███   ?███    ?███  ?███    ?███  ?███      ?███  ?███    ?███  ?███    ███  ?███    ?███
 █████ ??████ █████   █████ █████   █████ █████     █████ █████   █████ ██████████   █████   █████
?????   ???? ?????   ????? ?????   ????? ?????     ????? ?????   ????? ??????????   ?????   ?????
------------------------------------------------------------------------------------------------------
Karmada is installed successfully.
Register Kubernetes cluster to Karmada control plane.
Register cluster with 'Push' mode
Step 1: Use "kubectl karmada join" command to register the cluster to Karmada control plane. --cluster-kubeconfig is kubeconfig of the member cluster.
(In karmada)~# MEMBER_CLUSTER_NAME=$(cat ~/.kube/config  | grep current-context | sed 's/: /\n/g'| sed '1d')
(In karmada)~# kubectl karmada --kubeconfig /etc/karmada/karmada-apiserver.config  join ${MEMBER_CLUSTER_NAME} --cluster-kubecnotallow=$HOME/.kube/config
Step 2: Show members of karmada
(In karmada)~# kubectl --kubeconfig /etc/karmada/karmada-apiserver.config get clusters
Register cluster with 'Pull' mode
Step 1: Use "kubectl karmada register" command to register the cluster to Karmada control plane. "--cluster-name" is set to cluster of current-context by default.
(In member cluster)~# kubectl karmada register 192.168.247.4:32443 --token rflrr9.iisxtboo8dsz8jsv --discovery-token-ca-cert-hash sha256:008fb63e3b17c3e399f9688eca0978ab3a50dbe5d5b8d4f32c6bfd1fab12a1d8
Step 2: Show members of karmada
(In karmada)~# kubectl --kubeconfig /etc/karmada/karmada-apiserver.config get clusters安裝正常的話會(huì)看到如上所示的輸出信息。默認(rèn) Karmada 會(huì)安裝在 host 集群的 karmada-system 命名空間中:
$ kubectl get pods -n karmada-system --kubeconfig ~/.kube/host.config
NAME                                            READY   STATUS    RESTARTS   AGE
etcd-0                                          1/1     Running   0          35m
karmada-aggregated-apiserver-5fddf66847-nnfzv   1/1     Running   0          34m
karmada-apiserver-6b6f5b45-fkbk4                1/1     Running   0          35m
karmada-controller-manager-bbdf689db-rc67z      1/1     Running   0          34m
karmada-scheduler-78f854fbd4-m24c8              1/1     Running   0          34m
karmada-webhook-77b9945cf9-mkjrk                1/1     Running   0          33m
kube-controller-manager-5c4975bf8d-6tx5r        1/1     Running   0          34m如上所示 Karmada 控制平面相關(guān) Pod 都已經(jīng)正常運(yùn)行,接下來(lái)我們就可以將兩個(gè) member 集群注冊(cè)到 Karmada 控制平面中了,注冊(cè)集群有兩種方式,一種是 Push 模式,一種是 Pull 模式:
- Push:Karmada 控制平面將直接訪問(wèn)成員集群的 kube-apiserver 以獲取集群狀態(tài)并部署清單。
 - Pull:Karmada 控制平面不會(huì)訪問(wèn)成員集群,而是將其委托給名為 Karmada-agent 的額外組件。
 
我們這里的集群都使用的 KinD 搭建的,所以使用 Push 模式更方便,對(duì)于無(wú)法直接訪問(wèn)成員集群的環(huán)境下面可以使用 Pull 模式。
我們可以使用 kubectl karmada join 命令來(lái)注冊(cè)集群到 Karmada 控制平面。
sudo kubectl karmada --kubeconfig /etc/karmada/karmada-apiserver.config join member1 --cluster-kubecnotallow=$HOME/.kube/member1.config
sudo kubectl karmada --kubeconfig /etc/karmada/karmada-apiserver.config join member2 --cluster-kubecnotallow=$HOME/.kube/member2.config注冊(cè)成功后可以查看注冊(cè)的集群列表:
$ sudo kubectl --kubeconfig /etc/karmada/karmada-apiserver.config get clusters
NAME      VERSION   MODE   READY   AGE
member1   v1.27.3   Push   True    12m
member2   v1.27.3   Push   True    2s到這里我們就完成了 Karmada 的安裝和集群注冊(cè),接下來(lái)我們就可以使用 Karmada 來(lái)管理多集群了。
資源分發(fā)
接下來(lái)我們創(chuàng)建一個(gè) Deployment 資源,然后使用 Karmada 將其分發(fā)到 member1 和 member2 集群中。首先創(chuàng)建如下所示的 Deployment 資源:
# nginx-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9要注意我們需要使用 Karmada 控制平面的 kubeconfig 文件來(lái)創(chuàng)建資源對(duì)象,因?yàn)?Karmada 控制平面會(huì)將資源對(duì)象分發(fā)到成員集群中,所以在應(yīng)用資源對(duì)象時(shí)需要使用 --kubeconfig /etc/karmada/karmada-apiserver.config 參數(shù)。
# karmada-apiserver 是與 Karmada 控制面交互時(shí)要使用的主要 kubeconfig
$ kubectl apply -f nginx-demo.yaml --kubeconfig /etc/karmada/karmada-apiserver.config
$ kubectl get pods --kubeconfig ~/.kube/member1.config
No resources found in default namespace.
$ kubectl get pods --kubeconfig ~/.kube/member2.config
No resources found in default namespace.現(xiàn)在成員集群 member1 和 member2 下面并沒(méi)有對(duì)應(yīng)的對(duì)象。要進(jìn)行資源分發(fā)我們需要使用一個(gè)名為 PropagationPolicy(或者 ClusterPropagationPolicy)的資源對(duì)象,該資源對(duì)象定義了如何將資源分發(fā)到成員集群中。比如我們要將上面的 Deployment 對(duì)象分發(fā)到 member1 和 member2 集群中,我們可以創(chuàng)建如下所示的 PropagationPolicy 對(duì)象:
# nginx-propagation.yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: nginx-propagation
spec:
  resourceSelectors:
    - apiVersion: apps/v1
      kind: Deployment
      name: nginx
  placement:
    clusterAffinity:
      clusterNames:
        - member1
        - member2
    replicaScheduling:
      replicaDivisionPreference: Weighted
      replicaSchedulingType: Divided
      weightPreference:
        staticWeightList:
          - targetCluster:
              clusterNames:
                - member1
            weight: 1
          - targetCluster:
              clusterNames:
                - member2
            weight: 1在上面的 PropagationPolicy 對(duì)象中,首先我們通過(guò) resourceSelectors 屬性指定了要分發(fā)的資源對(duì)象,然后通過(guò) placement 字段,指定了資源對(duì)象的分發(fā)策略。
其中 .spec.placement.clusterAffinity 字段表示對(duì)特定集群集合的調(diào)度限制,沒(méi)有該限制,任何集群都可以成為調(diào)度候選者,該字段包含以下幾個(gè)屬性:
- LabelSelector:用于選擇集群的標(biāo)簽,matchLabels 和 matchExpressions 兩種方式都支持。
 - FieldSelector:按字段選擇成員集群的過(guò)濾器。
 - ClusterNames:直接指定所選的集群。
 - ExcludeClusters:排除指定的集群。
 
比如我們這里直接通過(guò) clusterNames 屬性指定了 member1 和 member2 集群,這意味著 Deployment 對(duì)象 nginx 可以被分發(fā)到 member1 和 member2 集群中。
此外我們還可以設(shè)置 ClusterAffinities 字段來(lái)聲明多個(gè)集群組。調(diào)度器將按照它們?cè)谝?guī)范中出現(xiàn)的順序逐一評(píng)估這些組,不滿足調(diào)度限制的組將被忽略,這意味著該組中的所有集群都不會(huì)被選擇。如果沒(méi)有一個(gè)組滿足調(diào)度限制,則調(diào)度失敗,這意味著不會(huì)選擇任何集群。
另外還要注意 ClusterAffinities 不能與 ClusterAffinity 共存。如果 ClusterAffinity 和 ClusterAffinities 均未設(shè)置,則任何集群都可以作為調(diào)度候選者。
比如現(xiàn)在我們有兩個(gè)分組的集群,其中本地?cái)?shù)據(jù)中心的私有集群可以是主要的集群,云提供商提供的托管集群可以是次組。因此,Karmada 調(diào)度程序更愿意將工作負(fù)載調(diào)度到主集群組,并且只有在主組不滿足限制(例如缺乏資源)的情況下才會(huì)考慮第二組集群,那么就可以配置如下所示的 PropagationPolicy 對(duì)象:
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: test-propagation
spec:
  #...
  placement:
    clusterAffinities: # 逐一評(píng)估這些組
      - affinityName: local-clusters
        clusterNames:
          - local-member1
          - local-member2
      - affinityName: cloud-clusters
        clusterNames:
          - public-cloud-member1
          - public-cloud-member2
    #...又比如對(duì)于災(zāi)難恢復(fù)的場(chǎng)景,集群可以分為 primary 集群和 backup 集群,工作負(fù)載將首先調(diào)度到主集群,當(dāng)主集群發(fā)生故障(例如數(shù)據(jù)中心斷電)時(shí),Karmada 調(diào)度程序可以遷移工作負(fù)載到備份集群。這種情況下可以配置如下所示的 PropagationPolicy 對(duì)象:
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: test-propagation
spec:
  #...
  placement:
    clusterAffinities:
      - affinityName: primary-clusters
        clusterNames:
          - member1
      - affinityName: backup-clusters
        clusterNames:
          - member1
          - member2
    #...現(xiàn)在我們已經(jīng)指定了分發(fā)的集群,那么具體應(yīng)該如何調(diào)度呢?哪一個(gè)集群應(yīng)該有多少副本呢?這就需要指定調(diào)度策略了。和原生 Kubernetes 類似,Karmada 支持多種調(diào)度策略,比如支持容忍污點(diǎn)、權(quán)重等。
通過(guò) .spec.placement.clusterTolerations 字段可以設(shè)置容忍度,與 kubernetes 一樣,容忍需要與集群上的污點(diǎn)結(jié)合使用。在集群上設(shè)置一個(gè)或多個(gè)污點(diǎn)后,無(wú)法在這些集群上調(diào)度或運(yùn)行工作負(fù)載,除非策略明確聲明可以容忍這些污點(diǎn)。Karmada 目前支持效果為 NoSchedule 和 NoExecute 的污點(diǎn)。我們可以使用 karmadactl taint 命令來(lái)設(shè)置集群的污點(diǎn):
# 為集群 foo 設(shè)置包含鍵 dedicated、值 special-user 和效果 NoSchedule 的污點(diǎn)
# 如果具有該鍵和效果的污點(diǎn)已經(jīng)存在,則其值將按指定替換
karmadactl taint clusters foo dedicated=special-user:NoSchedule為了調(diào)度到上述集群,我們需要在 PropagationPolicy 中聲明以下內(nèi)容:
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: nginx-propagation
spec:
  #...
  placement:
    clusterTolerations:
      - key: dedicated
        value: special-user
        Effect: NoSchedule我們常常使用 NoExecute 污點(diǎn)來(lái)實(shí)現(xiàn)多集群故障轉(zhuǎn)移。
然后更多的時(shí)候我們需要設(shè)置副本調(diào)度策略,我們可以通過(guò) .spec.placement.replicaScheduling 字段來(lái)設(shè)置副本調(diào)度策略,該字段表示將規(guī)范中具有副本的資源傳播到成員集群時(shí)處理副本數(shù)量的調(diào)度策略。Karmada 一共提供了兩種副本調(diào)度類型,用于確定 Karmada 傳播資源時(shí)如何調(diào)度副本:
- Duplicated:從資源中將相同的副本復(fù)制到每個(gè)候選成員集群。
 - Divided:根據(jù)有效候選成員集群的數(shù)量將副本劃分為若干部分,每個(gè)集群的確切副本由 ReplicaDivisionPreference 確定。
 
ReplicaDivisionPreference 用于描述當(dāng) ReplicaSchedulingType 為 Divided 時(shí)副本如何被劃分,也提供了兩種副本劃分方式:
- Aggregated:將副本盡可能少地劃分到集群,同時(shí)在劃分過(guò)程中尊重集群的資源可用性。
 - Weighted:根據(jù) WeightPreference 按權(quán)重劃分副本,一共有兩種方式。StaticWeightList 根據(jù)權(quán)重靜態(tài)分配副本到目標(biāo)集群,可以通過(guò) ClusterAffinity 選擇目標(biāo)集群。DynamicWeight 指定生成動(dòng)態(tài)權(quán)重列表的因子,如果指定,StaticWeightList 將被忽略。
 
上面我們創(chuàng)建的 Nginx 的 PropagationPolicy 對(duì)象中,我們指定了 ReplicaDivisionPreference 為 Weighted,ReplicaSchedulingType 為 Divided,weightPreference 為 1,表示兩個(gè)集群的權(quán)重相同,這意味著副本將均勻地傳播到 member1 和 member2。
我們這里直接應(yīng)用傳播策略資源對(duì)象即可:
$ sudo kubectl apply -f samples/nginx/propagationpolicy.yaml --kubeconfig /etc/karmada/karmada-apiserver.config
propagationpolicy.policy.karmada.io/nginx-propagation created
$ sudo kubectl get propagationpolicy --kubeconfig /etc/karmada/karmada-apiserver.config
NAME                AGE
nginx-propagation   31s當(dāng)創(chuàng)建 PropagationPolicy 對(duì)象后,Karmada 控制平面 watch 到過(guò)后就會(huì)自動(dòng)將資源對(duì)象分發(fā)到成員集群中,我們可以查看 Deployment 對(duì)象的狀態(tài):
$ sudo kubectl describe deploy nginx --kubeconfig /etc/karmada/karmada-apiserver.config
# ......
Events:
  Type    Reason                  Age                    From                                Message
  ----    ------                  ----                   ----                                -------
  Normal  ApplyPolicySucceed      2m17s (x2 over 2m17s)  resource-detector                   Apply policy(default/nginx-propagation) succeed
  Normal  SyncWorkSucceed         2m17s (x3 over 2m17s)  binding-controller                  Sync work of resourceBinding(default/nginx-deployment) successful.
  Normal  ScheduleBindingSucceed  2m17s                  default-scheduler                   Binding has been scheduled successfully.
  Normal  SyncSucceed             2m17s                  execution-controller                Successfully applied resource(default/nginx) to cluster member2
  Normal  SyncSucceed             2m17s                  execution-controller                Successfully applied resource(default/nginx) to cluster member1
  Normal  AggregateStatusSucceed  2m2s (x9 over 2m17s)   resource-binding-status-controller  Update resourceBinding(default/nginx-deployment) with AggregatedStatus successfully.可以看到 Deployment 對(duì)象已經(jīng)成功分發(fā)到了 member1 和 member2 集群中,我們也可以查看 member1 和 member2 集群中的 Pod 對(duì)象來(lái)進(jìn)行驗(yàn)證:
$ kubectl get pods --kubeconfig ~/.kube/member1.config
NAME                     READY   STATUS    RESTARTS   AGE
nginx-77b4fdf86c-54qhc   1/1     Running   0          2m59s
$ kubectl get pods --kubeconfig ~/.kube/member2.config
NAME                     READY   STATUS    RESTARTS   AGE
nginx-77b4fdf86c-9x98b   1/1     Running   0          3m24s和我們聲明的副本調(diào)度策略一樣,兩個(gè) Pod 對(duì)象均勻地分布在 member1 和 member2 集群中。
分發(fā) CRD
除了內(nèi)置的資源對(duì)象之外,Karmada 還支持分發(fā)自定義資源對(duì)象(CRD)。這里我們以 Karmada 倉(cāng)庫(kù)中的 guestbook 為例進(jìn)行說(shuō)明。
首先進(jìn)入 Karmada 倉(cāng)庫(kù)的 guestbook 目錄下:
?  cd samples/guestbook
?  guestbook git:(master) ll
total 48
-rw-r--r--  1 cnych  staff   1.8K May 16 11:26 README.md
-rw-r--r--  1 cnych  staff   135B May 16 11:26 guestbook.yaml
-rw-r--r--  1 cnych  staff   353B May 16 11:26 guestbooks-clusterpropagationpolicy.yaml
-rw-r--r--  1 cnych  staff   2.7K May 16 11:26 guestbooks-crd.yaml
-rw-r--r--  1 cnych  staff   455B May 16 11:26 guestbooks-overridepolicy.yaml
-rw-r--r--  1 cnych  staff   255B May 16 11:26 guestbooks-propagationpolicy.yaml然后在 Karmada 的控制平面上創(chuàng)建 Guestbook CRD:
sudo kubectl apply -f guestbooks-crd.yaml --kubeconfig /etc/karmada/karmada-apiserver.config該 CRD 應(yīng)該被應(yīng)用到 karmada-apiserver。
然后我們可以創(chuàng)建一個(gè) ClusterPropagationPolicy 對(duì)象,將 Guestbook CRD 分發(fā)到 member1,如下所示:
# guestbooks-clusterpropagationpolicy.yaml
apiVersion: policy.karmada.io/v1alpha1
kind: ClusterPropagationPolicy
metadata:
  name: example-policy
spec:
  resourceSelectors:
    - apiVersion: apiextensions.k8s.io/v1
      kind: CustomResourceDefinition
      name: guestbooks.webapp.my.domain
  placement:
    clusterAffinity:
      clusterNames:
        - member1需要注意的是 CustomResourceDefinition 是全局資源,所以我們使用 ClusterPropagationPolicy 對(duì)象來(lái)分發(fā),該對(duì)象的配置和 PropagationPolicy 對(duì)象類似,注意 resourceSelectors 字段中的 apiVersion 和 kind 需要設(shè)置為 apiextensions.k8s.io/v1 和 CustomResourceDefinition,name 字段需要設(shè)置為 Guestbook CRD 的名稱。
然后我們直接創(chuàng)建 ClusterPropagationPolicy 對(duì)象即可:
sudo kubectl apply -f guestbooks-clusterpropagationpolicy.yaml --kubeconfig /etc/karmada/karmada-apiserver.config應(yīng)用后正常就會(huì)將 Guestbook CRD 對(duì)象分發(fā)到 member1 集群中。
$ sudo kubectl karmada get crd --kubeconfig /etc/karmada/karmada-apiserver.config
NAME                          CLUSTER   CREATED AT             ADOPTION
guestbooks.webapp.my.domain   member1   2024-05-18T11:56:10Z   Y
$ kubectl get crd --kubeconfig ~/.kube/member1.config
NAME                          CREATED AT
guestbooks.webapp.my.domain   2024-05-18T11:56:10Z
$ kubectl get crd --kubeconfig ~/.kube/member2.config
No resources found接下來(lái)我們就可以部署分發(fā) Guestbook CRD 對(duì)象了,我們可以創(chuàng)建一個(gè) Guestbook CR 對(duì)象:
# guestbook.yaml
apiVersion: webapp.my.domain/v1
kind: Guestbook
metadata:
  name: guestbook-sample
spec:
  size: 2
  configMapName: test
  alias: Name同樣在 Karmada 控制平面上應(yīng)用該 Guestbook CR 對(duì)象即可:
$ sudo kubectl apply -f guestbook.yaml --kubeconfig /etc/karmada/karmada-apiserver.config然后就可以創(chuàng)建 PropagationPolicy 對(duì)象,將 guestbook-sample 分發(fā)到 member1 集群:
# guestbooks-propagationpolicy.yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: example-policy
spec:
  resourceSelectors:
    - apiVersion: webapp.my.domain/v1
      kind: Guestbook
  placement:
    clusterAffinity:
      clusterNames:
        - member1上面的 PropagationPolicy 對(duì)象和我們之前創(chuàng)建的類似,只是這里的 resourceSelectors 字段中的 apiVersion 和 kind 需要設(shè)置為 webapp.my.domain/v1 和 Guestbook(我們自己的 CRD)。同樣直接應(yīng)用該 PropagationPolicy 對(duì)象即可:
$ sudo kubectl apply -f guestbooks-propagationpolicy.yaml --kubeconfig /etc/karmada/karmada-apiserver.config應(yīng)用后就可以將 guestbook-sample 這個(gè) Guestbook CR 對(duì)象分發(fā)到 member1 集群中了。
$ kubectl get guestbook --kubeconfig ~/.kube/member1.config
NAME               AGE
guestbook-sample   39s可以看到 CRD 的分發(fā)和普通資源對(duì)象的分發(fā)原理是一樣的,只是需要先將 CRD 對(duì)象分發(fā)到成員集群中。
有的時(shí)候我們可能需要對(duì)分發(fā)的資源到不同集群進(jìn)行一些覆蓋操作,這個(gè)時(shí)候我們就可以使用 OverridePolicy 和 ClusterOverridePolicy 對(duì)象,用于聲明資源傳播到不同集群時(shí)的覆蓋規(guī)則。
比如我們創(chuàng)建一個(gè) OverridePolicy 對(duì)象,用于覆蓋 member1 中 guestbook-sample 的 size 字段,如下所示:
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
  name: guestbook-sample
spec:
  resourceSelectors:
    - apiVersion: webapp.my.domain/v1
      kind: Guestbook
  overrideRules:
    - targetCluster:
        clusterNames:
          - member1
      overriders:
        plaintext:
          - path: /spec/size
            operator: replace
            value: 4
          - path: /metadata/annotations
            operator: add
            value: { "OverridePolicy": "test" }上面的對(duì)象中通過(guò) resourceSelectors 字段指定了要覆蓋的資源對(duì)象,然后通過(guò) overrideRules 字段指定了覆蓋規(guī)則,targetCluster 字段指定了目標(biāo)集群,overriders 字段指定了覆蓋規(guī)則,這里我們將 guestbook-sample 的 size 字段覆蓋為 4,同時(shí)添加了一個(gè) OverridePolicy: test 的注解。
我們直接應(yīng)用該 OverridePolicy 對(duì)象即可:
$ sudo kubectl apply -f guestbooks-overridepolicy.yaml --kubeconfig /etc/karmada/karmada-apiserver.config創(chuàng)建完成后可以查看 member1 集群中的 guestbook-sample 對(duì)象來(lái)進(jìn)行驗(yàn)證:
$ kubectl get guestbook guestbook-sample --kubeconfig ~/.kube/member1.config -oyaml
apiVersion: webapp.my.domain/v1
kind: Guestbook
metadata:
  annotations:
    OverridePolicy: test
# ......
  name: guestbook-sample
  namespace: default
  resourceVersion: "82669"
  uid: 5893b85d-3946-44a0-b210-d67bd021cb65
spec:
  alias: Name
  configMapName: test
  size: 4可以看到 guestbook-sample 對(duì)象的 size 字段已經(jīng)被覆蓋為 4,同時(shí)添加了一個(gè) OverridePolicy: test 的注解,證明覆蓋操作成功。
Karmada 提供了多種聲明覆蓋規(guī)則的方案:
- ImageOverrider:覆蓋工作負(fù)載的鏡像。
 - CommandOverrider:覆蓋工作負(fù)載的命令。
 - ArgsOverrider:覆蓋工作負(fù)載的參數(shù)。
 - LabelsOverrider:覆蓋工作負(fù)載的標(biāo)簽。
 - AnnotationsOverrider:覆蓋工作負(fù)載的注釋。
 - PlaintextOverrider:用于覆蓋任何類型資源的通用工具。
 
PlaintextOverrider
上面我們使用的是 PlaintextOverrider 覆蓋規(guī)則,可以覆蓋任何類型資源的字段。PlaintextOverrider 可以根據(jù)路徑、運(yùn)算符和值覆蓋目標(biāo)字段,就像 kubectl patch 一樣。允許的操作如下:
- add:向資源追加一個(gè)或多個(gè)元素。
 - remove:從資源中刪除一個(gè)或多個(gè)元素。
 - replace:替換資源中的一個(gè)或多個(gè)元素。
 
ImageOverrider
ImageOverrider 用于覆蓋工作負(fù)載的鏡像,用于覆蓋格式為 [registry/]repository[:tag|@digest](例如 /spec/template/spec/containers/0/image )的鏡像。允許的操作如下:
- add:將注冊(cè)表、存儲(chǔ)庫(kù)或 tag/digest 附加到容器中的鏡像。
 - remove:從容器中的鏡像中刪除注冊(cè)表、存儲(chǔ)庫(kù)或 tag/digest。
 - replace:替換容器中鏡像的注冊(cè)表、存儲(chǔ)庫(kù)或 tag/digest。
 
比如我們需要?jiǎng)?chuàng)建一個(gè)如下所示的 Deployment 對(duì)象:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  #...
spec:
  template:
    spec:
      containers:
        - image: myapp:1.0.0
          name: myapp當(dāng)工作負(fù)載傳播到特定集群時(shí)添加注冊(cè)表,可以使用如下所示的 OverridePolicy 對(duì)象:
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
  name: example
spec:
  #...
  overrideRules:
    - overriders:
        imageOverrider:
          - component: Registry
            operator: add
            value: test-repo上面的覆蓋規(guī)則表示添加 test-repo 這個(gè)鏡像倉(cāng)庫(kù)到 myapp 的鏡像中,這樣在傳播到集群時(shí)就會(huì)變成 test-repo/myapp:1.0.0。
containers:
  - image: test-repo/myapp:1.0.0
    name: myappreplace 和 remove 操作也是類似的,只是分別用于替換和刪除鏡像中的某些字段。
跨集群彈性伸縮
在 Karmada 中,我們可以使用 FederatedHPA 來(lái)實(shí)現(xiàn)跨多個(gè)集群擴(kuò)展/縮小工作負(fù)載的副本,旨在根據(jù)需求自動(dòng)調(diào)整工作負(fù)載的規(guī)模。

FederatedHPA
當(dāng)負(fù)載增加時(shí),如果 Pod 的數(shù)量低于配置的最大值,則 FederatedHPA 擴(kuò)展工作負(fù)載(例如 Deployment、StatefulSet 或其他類似資源)的副本數(shù)。當(dāng)負(fù)載減少時(shí),如果 Pod 的數(shù)量高于配置的最小值,則 FederatedHPA 縮小工作負(fù)載的副本數(shù)。
FederatedHPA 是作為 Karmada API 資源和控制器實(shí)現(xiàn)的,該資源確定了控制器的行為。FederatedHPA 控制器運(yùn)行在 Karmada 控制平面中,定期調(diào)整其目標(biāo)(例如 Deployment)的所需規(guī)模,以匹配觀察到的指標(biāo),例如平均 CPU 利用率、平均內(nèi)存利用率或任何其他自定義指標(biāo)。

FederatedHPA實(shí)現(xiàn)原理
為了實(shí)現(xiàn)跨集群的自動(dòng)擴(kuò)縮容,Karmada 引入了 FederatedHPA 控制器和 karmada-metrics-adapter,它們的工作方式如下:
- HPA 控制器定期通過(guò)指標(biāo) API metrics.k8s.io 或 custom.metrics.k8s.io 使用標(biāo)簽選擇器查詢指標(biāo)。
 - karmada-apiserver 獲取指標(biāo) API 查詢結(jié)果,然后通過(guò) API 服務(wù)注冊(cè)將其路由到 karmada-metrics-adapter。
 - karmada-metrics-adapter 將從目標(biāo)集群(Pod 所在的集群)查詢指標(biāo)。收集到指標(biāo)后,它會(huì)對(duì)這些指標(biāo)進(jìn)行聚合并返回結(jié)果。
 - HPA 控制器將根據(jù)指標(biāo)計(jì)算所需的副本數(shù),并直接擴(kuò)展/縮小工作負(fù)載的規(guī)模。然后,karmada-scheduler 將這些副本調(diào)度到成員集群中。
 
注意:要使用此功能,Karmada 版本必須為 v1.6.0 或更高版本。
下面我們就來(lái)演示如何使用 FederatedHPA 控制器來(lái)實(shí)現(xiàn)跨集群的自動(dòng)擴(kuò)縮容。首先至少需要兩個(gè)成員集群,我們需要在成員集群中安裝 ServiceExport 和 ServiceImport 來(lái)啟用多集群服務(wù)。在 Karmada 控制平面上安裝 ServiceExport 和 ServiceImport 后(init 安裝后會(huì)自動(dòng)安裝),我 們可以創(chuàng)建 ClusterPropagationPolicy 將這兩個(gè) CRD 傳播到成員集群。
# propagate-service-export-import.yaml
# propagate ServiceExport CRD
apiVersion: policy.karmada.io/v1alpha1
kind: ClusterPropagationPolicy
metadata:
  name: serviceexport-policy
spec:
  resourceSelectors:
    - apiVersion: apiextensions.k8s.io/v1
      kind: CustomResourceDefinition
      name: serviceexports.multicluster.x-k8s.io
  placement:
    clusterAffinity:
      clusterNames:
        - member1
        - member2
---
# propagate ServiceImport CRD
apiVersion: policy.karmada.io/v1alpha1
kind: ClusterPropagationPolicy
metadata:
  name: serviceimport-policy
spec:
  resourceSelectors:
    - apiVersion: apiextensions.k8s.io/v1
      kind: CustomResourceDefinition
      name: serviceimports.multicluster.x-k8s.io
  placement:
    clusterAffinity:
      clusterNames:
        - member1
        - member2直接應(yīng)用該 ClusterPropagationPolicy 對(duì)象即可:
$ sudo kubectl apply -f propagate-service-export-import.yaml --kubeconfig /etc/karmada/karmada-apiserver.config應(yīng)用后就可以在 member1 和 member2 集群中創(chuàng)建 ServiceExport 和 ServiceImport 對(duì)象了。
另外我們還需要為成員集群安裝 metrics-server 來(lái)提供 metrics API,通過(guò)運(yùn)行以下命令來(lái)安裝:
hack/deploy-k8s-metrics-server.sh $HOME/.kube/member1.config member1
hack/deploy-k8s-metrics-server.sh $HOME/.kube/member2.config member2最后我們還需要在 Karmada 控制平面中安裝 karmada-metrics-adapter 以提供指標(biāo) API,通過(guò)運(yùn)行以下命令來(lái)安裝它:
sudo hack/deploy-metrics-adapter.sh ~/.kube/host.config host /etc/karmada/karmada-apiserver.config karmada-apiserver需要注意使用 karmada init 安裝的 Karmada 控制平面,需要將 karmada-cert 這個(gè) Secret 對(duì)象重新拷貝創(chuàng)建一個(gè)名為 karmada-cert-secret 的 Secret 對(duì)象。
部署后在 Karmada 控制平面中就會(huì)有 karmada-metrics-adapter 這個(gè) Pod 對(duì)象。
接下來(lái)我們?cè)?member1 和 member2 中部署 Deployment(1 個(gè)副本)和 Service 對(duì)象,如下所示:
# nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - image: nginx
          name: nginx
          resources:
            requests:
              cpu: 25m
              memory: 64Mi
            limits:
              cpu: 25m
              memory: 64Mi
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: nginx
---
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: nginx-propagation
spec:
  resourceSelectors:
    - apiVersion: apps/v1
      kind: Deployment
      name: nginx
    - apiVersion: v1
      kind: Service
      name: nginx-service
  placement:
    clusterAffinity:
      clusterNames:
        - member1
        - member2
    replicaScheduling:
      replicaDivisionPreference: Weighted
      replicaSchedulingType: Divided
      weightPreference:
        staticWeightList:
          - targetCluster:
              clusterNames:
                - member1
            weight: 1
          - targetCluster:
              clusterNames:
                - member2
            weight: 1直接應(yīng)用上面的資源對(duì)象即可:
$ sudo kubectl apply -f nginx.yaml --kubeconfig /etc/karmada/karmada-apiserver.config
deployment.apps/nginx configured
service/nginx-service created
propagationpolicy.policy.karmada.io/nginx-propagation configured
$ sudo kubectl karmada get pods --kubeconfig /etc/karmada/karmada-apiserver.config
NAME                     CLUSTER   READY   STATUS    RESTARTS   AGE
nginx-5c54b4855f-ztmnk   member1   1/1     Running   0          43s
$ sudo kubectl karmada get svc --kubeconfig /etc/karmada/karmada-apiserver.config
NAME            CLUSTER   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE     ADOPTION
nginx-service   member2   ClusterIP   100.171.35.78    <none>        80/TCP    52s     Y
nginx-service   member1   ClusterIP   100.91.124.245   <none>        80/TCP    52s     Y然后讓我們?cè)?Karmada 控制平面中部署一個(gè) FederatedHPA 對(duì)象,用來(lái)自動(dòng)擴(kuò)縮容,如下所示:
# nginx-federatedhpa.yaml
apiVersion: autoscaling.karmada.io/v1alpha1
kind: FederatedHPA
metadata:
  name: nginx
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  minReplicas: 1
  maxReplicas: 10
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 10
    scaleUp:
      stabilizationWindowSeconds: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 10上面的 FederatedHPA 對(duì)象中,我們指定了 scaleTargetRef 字段為 Deployment 對(duì)象 nginx,minReplicas 和 maxReplicas 分別為 1 和 10,metrics 字段中指定了 CPU 利用率為 10% 時(shí)進(jìn)行擴(kuò)縮容。同樣直接應(yīng)用該 FederatedHPA 對(duì)象即可:
$ sudo kubectl apply -f nginx-federatedhpa.yaml --kubeconfig /etc/karmada/karmada-apiserver.config
$ sudo kubectl get fhpa --kubeconfig /etc/karmada/karmada-apiserver.config
NAME    REFERENCE-KIND   REFERENCE-NAME   MINPODS   MAXPODS   REPLICAS   AGE
nginx   Deployment       nginx            1         10        1          19s我們還需要一個(gè)多集群服務(wù)將請(qǐng)求路由到 member1 和 member2 集群中的 pod。首先在 Karmada 控制平面上創(chuàng)建 ServiceExport 對(duì)象,然后創(chuàng)建 PropagationPolicy 以將 ServiceExport 對(duì)象傳播到 member1 和 member2 集群。
# nginx-serviceexport.yaml
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceExport
metadata:
  name: nginx-service
---
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: serve-export-policy
spec:
  resourceSelectors:
    - apiVersion: multicluster.x-k8s.io/v1alpha1
      kind: ServiceExport
      name: nginx-service
  placement:
    clusterAffinity:
      clusterNames:
        - member1
        - member2然后在 Karmada 控制平面上創(chuàng)建 ServiceImport 對(duì)象,然后創(chuàng)建 PropagationPolicy 以將 ServiceImport 對(duì)象傳播到 member1 集群。
# nginx-serviceimport.yaml
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceImport
metadata:
  name: nginx-service
spec:
  type: ClusterSetIP
  ports:
    - port: 80
      protocol: TCP
---
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: serve-import-policy
spec:
  resourceSelectors:
    - apiVersion: multicluster.x-k8s.io/v1alpha1
      kind: ServiceImport
      name: nginx-service
  placement:
    clusterAffinity:
      clusterNames:
        - member1直接應(yīng)用上面的資源對(duì)象即可:
$ sudo kubectl apply -f nginx-serviceexport.yaml --kubeconfig /etc/karmada/karmada-apiserver.config
$ sudo kubectl apply -f nginx-serviceimport.yaml --kubeconfig /etc/karmada/karmada-apiserver.config部署完成后,可以查看多集群服務(wù):
$ sudo kubectl karmada get svc --kubeconfig /etc/karmada/karmada-apiserver.config
NAME                    CLUSTER   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE     ADOPTION
nginx-service           member2   ClusterIP   100.171.35.78    <none>        80/TCP    6m36s   Y
derived-nginx-service   member1   ClusterIP   100.91.3.68      <none>        80/TCP    17s     Y
nginx-service           member1   ClusterIP   100.91.124.245   <none>        80/TCP    6m36s   Y接下來(lái)我們?cè)?member1 集群使用 hey 工具來(lái)進(jìn)行 http 負(fù)載測(cè)試,模擬請(qǐng)求增加,從而觸發(fā) Pod 的 CPU 使用率增加:
$ wget https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64
$ chmod +x hey_linux_amd64
$ docker cp hey_linux_amd64 member1-control-plane:/usr/local/bin/hey然后我們可以使用 hey 請(qǐng)求多集群服務(wù)以增加 nginx pod 的 CPU 使用率。
$ docker exec member1-control-plane hey -c 1000 -z 1m http://100.91.3.68
Summary:
  Total:        61.4678 secs
  Slowest:      4.7916 secs
  Fastest:      0.0244 secs
  Average:      0.9024 secs
  Requests/sec: 1090.3758
  Total data:   41219145 bytes
  Size/request: 615 bytes
Response time histogram:
  0.024 [1]     |
  0.501 [23047] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.978 [23117] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  1.455 [8696]  |■■■■■■■■■■■■■■■
  1.931 [5681]  |■■■■■■■■■■
  2.408 [3352]  |■■■■■■
  2.885 [1534]  |■■■
  3.361 [832]   |■
  3.838 [375]   |■
  4.315 [318]   |■
  4.792 [70]    |
Latency distribution:
  10% in 0.2733 secs
  25% in 0.4264 secs
  50% in 0.6478 secs
  75% in 1.1603 secs
  90% in 1.9114 secs
  95% in 2.3694 secs
  99% in 3.4382 secs
Details (average, fastest, slowest):
  DNS+dialup:   0.0019 secs, 0.0244 secs, 4.7916 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0000 secs
  req write:    0.0006 secs, 0.0000 secs, 0.1423 secs
  resp wait:    0.7861 secs, 0.0002 secs, 4.6641 secs
  resp read:    0.0553 secs, 0.0000 secs, 1.3870 secs
Status code distribution:
  [200] 67023 responses等一會(huì)兒,副本就會(huì)開(kāi)始擴(kuò)容了,我們可以查看 FederatedHPA 對(duì)象的狀態(tài)來(lái)了解副本的變化:
$ sudo kubectl describe fhpa nginx --kubeconfig /etc/karmada/karmada-apiserver.config
Name:         nginx
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  autoscaling.karmada.io/v1alpha1
Kind:         FederatedHPA
# ...
Spec:
  Behavior:
    Scale Down:
      Policies:
        Period Seconds:              15
        Type:                        Percent
        Value:                       100
      Select Policy:                 Max
      Stabilization Window Seconds:  10
    Scale Up:
      Policies:
        Period Seconds:              15
        Type:                        Pods
        Value:                       4
        Period Seconds:              15
        Type:                        Percent
        Value:                       100
      Select Policy:                 Max
      Stabilization Window Seconds:  10
  Max Replicas:                      10
  Metrics:
    Resource:
      Name:  cpu
      Target:
        Average Utilization:  10
        Type:                 Utilization
    Type:                     Resource
  Min Replicas:               1
  Scale Target Ref:
    API Version:  apps/v1
    Kind:         Deployment
    Name:         nginx
Status:
  Conditions:
    Last Transition Time:  2024-05-19T01:43:16Z
    Message:               recommended size matches current size
    Reason:                ReadyForNewScale
    Status:                True
    Type:                  AbleToScale
    Last Transition Time:  2024-05-19T01:43:16Z
    Message:               the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request)
    Reason:                ValidMetricFound
    Status:                True
    Type:                  ScalingActive
    Last Transition Time:  2024-05-19T01:45:16Z
    Message:               the desired replica count is less than the minimum replica count
    Reason:                TooFewReplicas
    Status:                True
    Type:                  ScalingLimited
  Current Metrics:
    Resource:
      Current:
        Average Utilization:  0
        Average Value:        0
      Name:                   cpu
    Type:                     Resource
  Current Replicas:           1
  Desired Replicas:           1
  Last Scale Time:            2024-05-19T01:45:16Z
Events:
  Type    Reason             Age   From                     Message
  ----    ------             ----  ----                     -------
  Normal  SuccessfulRescale  2m7s  federatedHPA-controller  New size: 5; reason: cpu resource utilization (percentage of request) above target
  Normal  SuccessfulRescale  112s  federatedHPA-controller  New size: 10; reason: cpu resource utilization (percentage of request) above target
  Normal  SuccessfulRescale  67s   federatedHPA-controller  New size: 3; reason: All metrics below target
  Normal  SuccessfulRescale  52s   federatedHPA-controller  New size: 1; reason: All metrics below target同時(shí)可以查看 member1 和 member2 集群中的 Pod 對(duì)象:
$ sudo kubectl karmada get pods --kubeconfig /etc/karmada/karmada-apiserver.config
NAME                     CLUSTER   READY   STATUS    RESTARTS   AGE
nginx-5c54b4855f-4p6wq   member1   1/1     Running   0          40s
nginx-5c54b4855f-kdwpc   member1   1/1     Running   0          2m6s
nginx-5c54b4855f-l4vm4   member1   1/1     Running   0          40s
nginx-5c54b4855f-t4ghv   member1   1/1     Running   0          25s
nginx-5c54b4855f-vbj9c   member1   1/1     Running   0          25s
nginx-5c54b4855f-hx2xn   member2   1/1     Running   0          25s
nginx-5c54b4855f-kfnbh   member2   1/1     Running   0          40s
nginx-5c54b4855f-rmbv9   member2   1/1     Running   0          40s
nginx-5c54b4855f-wfd92   member2   1/1     Running   0          25s
nginx-5c54b4855f-wwsvq   member2   1/1     Running   0          25s可以看到 Pod 的副本數(shù)已經(jīng)擴(kuò)容到 10 個(gè)了。同樣當(dāng)負(fù)載測(cè)試結(jié)束后,Pod 的副本數(shù)會(huì)自動(dòng)縮小為 1 個(gè)副本。
$ sudo kubectl karmada get pods --kubeconfig /etc/karmada/karmada-apiserver.config
NAME                     CLUSTER   READY   STATUS    RESTARTS   AGE
nginx-5c54b4855f-kdwpc   member1   1/1     Running   0          5m25s到這里我們就完成了使用 FederatedHPA 進(jìn)行跨集群的自動(dòng)擴(kuò)縮容,除此之外我們還可以使用 CronFederatedHPA 用于定期自動(dòng)縮放操作,它可以縮放具有 scale 子資源的工作負(fù)載或 Karmada FederatedHPA。典型的場(chǎng)景是在可預(yù)見(jiàn)的流量高峰到來(lái)前提前擴(kuò)容工作負(fù)載。例如,如果我知道每天早上 9 點(diǎn)會(huì)突發(fā)流量洪峰,我們就可以提前半個(gè)小時(shí)擴(kuò)容相關(guān)服務(wù),以處理高峰負(fù)載并確保服務(wù)持續(xù)可用性。在 Karmada 控制平面內(nèi)運(yùn)行的 CronFederatedHPA 控制器根據(jù)預(yù)定義的 cron 計(jì)劃來(lái)伸縮工作負(fù)載的副本或 FederatedHPA 的最小/最大副本數(shù)。
比如我們有一個(gè)如下所示的 CronFederatedHPA 對(duì)象:
apiVersion: autoscaling.karmada.io/v1alpha1
kind: CronFederatedHPA
metadata:
  name: nginx-cronfhpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  rules:
    - name: "scale-up"
      schedule: "*/1 * * * *"
      targetReplicas: 5
      suspend: false其中表達(dá)式 */1 * * * * 的意思是 nginx deployment 的副本應(yīng)該每分鐘更新為 5 個(gè),確保了處理接下來(lái)的流量突發(fā)流量洪峰。
除了這些使用場(chǎng)景之外,Karmada 還有很多實(shí)踐場(chǎng)景,比如跨集群的災(zāi)備、多集群網(wǎng)絡(luò)、多集群服務(wù)治理、多集群 CI/CD 等等,這些場(chǎng)景都可以通過(guò) Karmada 來(lái)實(shí)現(xiàn),更多最佳實(shí)踐方案可以參考 Karmada 官方文檔以了解更多。
參考鏈接:https://karmada.io/zh/docs/。















 
 
 














 
 
 
 