騰訊開(kāi)源的 Kubernetes 多集群管理和跨集群編排工具 Clusternet
Clusternet(Cluster Internet) 是一個(gè)騰訊開(kāi)源的 Kubernetes 多集群管理云原生項(xiàng)目,可幫助你像訪問(wèn) Internet 一樣輕松管理數(shù)以百萬(wàn)計(jì)的 Kubernetes 集群。無(wú)論集群運(yùn)行在公共云、私有云、混合云還是邊緣,Clusternet 都可以讓你管理/訪問(wèn)它們,就像它們?cè)诒镜剡\(yùn)行一樣。這也有助于消除為每個(gè)集群處理不同管理工具的需要。Clusternet 還可以幫助你從托管集群中的一組 API 將應(yīng)用程序部署和協(xié)調(diào)到多個(gè)集群。當(dāng)你的集群在 VPC 網(wǎng)絡(luò)、邊緣或防火墻后面運(yùn)行時(shí),Clusternet 可以通過(guò)可配置的方式設(shè)置網(wǎng)絡(luò)隧道。
Clusternet 還提供了一個(gè) Kubernetes 風(fēng)格的 API,你可以繼續(xù)使用 Kubernetes 的方式,比如 KubeConfig,來(lái)訪問(wèn)某個(gè)管理的 Kubernetes 集群,或者一個(gè) Kubernetes 服務(wù)。
以 Clusternet 項(xiàng)目為基礎(chǔ)實(shí)現(xiàn)多云多集群管理平臺(tái),為用戶(hù)提供跨云、跨集群、跨 region/zone 的分布式容器服務(wù),將更好的滿(mǎn)足多種場(chǎng)景需求。
1 架構(gòu)
下圖是 Clusternet 的一個(gè)簡(jiǎn)單的架構(gòu)圖:
Clusternet 主要由 clusternet-agent 和 clusternet-hub 兩個(gè)組件組成,非常輕量級(jí)。
其中 clusternet-agent 組件需要部署在各個(gè)子集群中,主要負(fù)責(zé):
- 將當(dāng)前集群作為子集群自動(dòng)注冊(cè)到父集群,也稱(chēng)為子集群 ManagedCluster
- 上報(bào)當(dāng)前集群元信息,包括 Kubernetes 版本、運(yùn)行平臺(tái)、healthz/readyz/livez 健康狀態(tài)、節(jié)點(diǎn)狀態(tài)等
- 與父集群建立一個(gè) TCP 全雙工的 websocket 安全隧道連接
clusternet-hub 組件部署和運(yùn)行在父集群中,通過(guò) AA(Aggregated APIServer) 的方式進(jìn)行工作,主要負(fù)責(zé):
- 批準(zhǔn)各個(gè)子集群注冊(cè)請(qǐng)求,并為子集群創(chuàng)建專(zhuān)用資源,例如 namespace、ServiceAccount 和 RBAC 規(guī)則等
- 作為聚合的 apiserver (AA),用作 websocket 服務(wù)器,維護(hù)來(lái)自子集群的多個(gè) websocket 連接
- 提供 Kubernstes 風(fēng)格的 REST API 來(lái)重定向/代理/升級(jí)請(qǐng)求到每個(gè)子集群 ( 從一組 API 協(xié)調(diào)應(yīng)用程序并將其部署到多個(gè)集群;
注意:由于 clusternet-hub 作為 AA 運(yùn)行,所以需要確保父級(jí) apiserver 可以訪問(wèn)該 clusternet-hub 服務(wù)。
2 概念
對(duì)于每個(gè)想要被管理的 Kubernetes 集群,我們稱(chēng)之為子集群,子集群注冊(cè)到的集群,我們稱(chēng)之為父集群。組件 clusternet-agent 在子集群中運(yùn)行,clusternet-hub 在父集群中運(yùn)行。Clusternet 支持向不同集群分發(fā)和管理各種應(yīng)用資源,包括原生 Kubernetes 各類(lèi)資源(Deployment/StatefulSet/ConfigMap/Secret 等)、各類(lèi) CRD 資源,以及 HelmChart 應(yīng)用等等。
下圖是 Clusternet 的多集群應(yīng)用分發(fā)模型,其中綠色的模塊是需要用戶(hù)去創(chuàng)建的,紫色的模塊是 Clusternet 內(nèi)部做流轉(zhuǎn)的資源對(duì)象,此外 Clusternet 還提供了 kubectl 插件,可以通過(guò) kubectl clusternet apply 命令來(lái)創(chuàng)建資源。
- ClusterRegistrationRequest 是 clusternet-agent 在父集群中為子集群注冊(cè)創(chuàng)建的對(duì)象
- ManagedCluster 是 clusternet-hub 在批準(zhǔn) ClusterRegistrationRequest 后在父集群中創(chuàng)建的一個(gè)對(duì)象
- HelmChart 是一個(gè) helm chart 配置的對(duì)象
- Subscription 定義了訂閱者想要安裝到集群中的資源,對(duì)于每個(gè)匹配的集群,將在其專(zhuān)用命名空間中創(chuàng)建一個(gè)相應(yīng)的 Base 對(duì)象
- Localization 和 Globalization 將以?xún)?yōu)先級(jí)來(lái)定義 Override,數(shù)字越小則優(yōu)先級(jí)越低,Localization是命名空間范圍的資源,而 Globalization 是集群范圍的。
- Base 對(duì)象將被渲染為應(yīng)用了 Globalization 和 Localization 設(shè)置 Description 對(duì)象,Descritpion 是最終要部署到目標(biāo)子群中的資源
3 部署
從上面的架構(gòu)可以看出我們需要在子集群和父集群中分別部署 clusternet-agent 和 clusternet-hub 組件。
首先在集群中 Clone 項(xiàng)目代碼:
- $ git clone https://github.com/clusternet/clusternet.git
在父集群中部署 clusternet-hub 組件:
- $ kubectl apply -f deploy/hub
然后為 clusternet-agent 創(chuàng)建一個(gè) bootstrap token:
- $ # 下面命令將創(chuàng)建一個(gè) bootstrap token:07401b.f395accd246ae52d
- $ kubectl apply -f manifests/samples/cluster_bootstrap_token.yaml
然后在子集群中部署 clusternet-agent,幫助子集群注冊(cè)到父集群,clusternet-agent 可以配置以下三種同步模式(通過(guò)標(biāo)志 --cluster-sync-mode 配置):
- Push(推) 模式是指父集群的所有資源變化將由 clusternet-hub 自動(dòng)同步、推送并應(yīng)用到子集群
- Pull(拉) 模式表示 clusternet-agent 將自動(dòng) watch、同步和應(yīng)用所有從父集群到子集群的資源變化
- Dual 推拉結(jié)合模式,這種模式強(qiáng)烈推薦,通常與特性 AppPusher 一起使用
特性 AppPusher 在 agent 端工作,這主要是出于以下兩個(gè)原因:
- 不建議在注冊(cè)后改變同步模式,這可能會(huì)帶來(lái)不一致的配置和行為,這就是為什么強(qiáng)烈推薦雙模式。當(dāng)雙模式被設(shè)置后,AppPusher 提供了一種方法來(lái)幫助將 Push 模式切換到 Pull 模式,而無(wú)需真正更改標(biāo)志 --cluster-sync-mode,反之亦然。
- 出于安全考慮,如子集群的安全風(fēng)險(xiǎn)等。
- 當(dāng)一個(gè)子集群禁用 AppPusher 時(shí),父集群不會(huì)向其部署任何應(yīng)用程序,即使設(shè)置為 Push 或 Dual 模式,這個(gè)時(shí)候,這個(gè)子集群的工作方式就像 Pull 模式。
- 要部署的資源被表示為 Description 對(duì)象,你也可以運(yùn)行你自己的控制器來(lái) watch 該對(duì)象的變化,然后來(lái)分發(fā)和部署資源。
部署 clusternet-agent 后,首先要?jiǎng)?chuàng)建一個(gè)包含集群注冊(cè)用的 Token 的 Secret:
- $ # create namespace clusternet-system if not created
- $ kubectl create ns clusternet-system
- $ # here we use the token created above
- $ PARENTURL=https://192.168.10.10 REGTOKEN=07401b.f395accd246ae52d envsubst < ./deploy/templates/clusternet_agent_secret.yaml | kubectl apply -f -
上面的 PARENTURL 是你想注冊(cè)的父集群的 apiserver 地址,必須指定 https 方案,它是目前唯一支持的方案。如果 apiserver 不是在標(biāo)準(zhǔn)的 https 端口(:443)上監(jiān)聽(tīng),請(qǐng)?jiān)?URL 中指定端口號(hào),以確保代理連接到正確的端點(diǎn),例如 https://192.168.10.10:6443。
- $ # 部署之前,根據(jù)自己的需求更新同步模式
- $ kubectl apply -f deploy/agent
部署完成后檢查集群注冊(cè)情況:
- $ # clsrr 是 ClusterRegistrationRequest 對(duì)象的別名
- $ kubectl get clsrr
- NAME CLUSTER ID STATUS AGE
- clusternet-dc91021d-2361-4f6d-a404-7c33b9e01118 dc91021d-2361-4f6d-a404-7c33b9e01118 Approved 3d6h
- $ kubectl get clsrr clusternet-dc91021d-2361-4f6d-a404-7c33b9e01118 -o yaml
- apiVersion: clusters.clusternet.io/v1beta1
- kind: ClusterRegistrationRequest
- metadata:
- labels:
- clusters.clusternet.io/cluster-id: dc91021d-2361-4f6d-a404-7c33b9e01118
- clusters.clusternet.io/cluster-name: clusternet-cluster-dzqkw
- clusters.clusternet.io/registered-by: clusternet-agent
- name: clusternet-dc91021d-2361-4f6d-a404-7c33b9e01118
- spec:
- clusterId: dc91021d-2361-4f6d-a404-7c33b9e01118
- clusterName: clusternet-cluster-dzqkw
- clusterType: EdgeClusterSelfProvisioned
- status:
- caCertificate: REDACTED
- dedicatedNamespace: clusternet-dhxfs
- managedClusterName: clusternet-cluster-dzqkw
- result: Approved
- token: REDACTED
在 ClusterRegistrationRequest 被批準(zhǔn)后,狀態(tài)將被更新,如果需要的話,可以用相應(yīng)的憑證來(lái)訪問(wèn)父集群。這些憑證已經(jīng)用指定范圍內(nèi)的 RBAC 規(guī)則設(shè)置了,可以查看下面的兩個(gè)規(guī)則。
- apiVersion: rbac.authorization.k8s.io/v1
- kind: ClusterRole
- metadata:
- annotations:
- clusternet.io/autoupdate: "true"
- labels:
- clusters.clusternet.io/bootstrapping: rbac-defaults
- clusters.clusternet.io/cluster-id: dc91021d-2361-4f6d-a404-7c33b9e01118
- clusternet.io/created-by: clusternet-hub
- name: clusternet-dc91021d-2361-4f6d-a404-7c33b9e01118
- rules:
- - apiGroups:
- - clusters.clusternet.io
- resources:
- - clusterregistrationrequests
- verbs:
- - create
- - get
- - apiGroups:
- - proxies.clusternet.io
- resourceNames:
- - dc91021d-2361-4f6d-a404-7c33b9e01118
- resources:
- - sockets
- verbs:
- - '*'
- ---
- apiVersion: rbac.authorization.k8s.io/v1
- kind: Role
- metadata:
- annotations:
- clusternet.io/autoupdate: "true"
- labels:
- clusters.clusternet.io/bootstrapping: rbac-defaults
- clusternet.io/created-by: clusternet-hub
- name: clusternet-managedcluster-role
- namespace: clusternet-dhxfs
- rules:
- - apiGroups:
- - '*'
- resources:
- - '*'
- verbs:
- - '*'
然后檢查被管理集群的狀態(tài):
- $ # mcls 是 ManagedCluster 對(duì)象的別名
- $ # kubectl get mcls -A
- $ # or append "-o wide" to display extra columns
- $ kubectl get mcls -A -o wide
- NAMESPACE NAME CLUSTER ID CLUSTER TYPE SYNC MODE KUBERNETES READYZ AGE
- clusternet-dhxfs clusternet-cluster-dzqkw dc91021d-2361-4f6d-a404-7c33b9e01118 EdgeClusterSelfProvisioned Dual v1.19.10 true 7d23h
- $ kubectl get mcls -n clusternet-dhxfs clusternet-cluster-dzqkw -o yaml
- apiVersion: clusters.clusternet.io/v1beta1
- kind: ManagedCluster
- metadata:
- labels:
- clusters.clusternet.io/cluster-id: dc91021d-2361-4f6d-a404-7c33b9e01118
- clusters.clusternet.io/cluster-name: clusternet-cluster-dzqkw
- clusternet.io/created-by: clusternet-agent
- name: clusternet-cluster-dzqkw
- namespace: clusternet-dhxfs
- spec:
- clusterId: dc91021d-2361-4f6d-a404-7c33b9e01118
- clusterType: EdgeClusterSelfProvisioned
- syncMode: Dual
- status:
- apiserverURL: http://10.0.0.10:8080
- appPusher: true
- healthz: true
- k8sVersion: v1.19.10
- lastObservedTime: "2021-06-30T08:55:14Z"
- livez: true
- platform: linux/amd64
- readyz: true
默認(rèn)情況下,clusternet-agent 每3分鐘更新一次 ManagedCluster 的狀態(tài),這可以通過(guò)標(biāo)志 --cluster-status-update-frequency 來(lái)進(jìn)行配置。
然后我們可以通過(guò) krew 來(lái)安裝 Clusternet 的 kubectl 插件:
- $ kubectl krew install clusternet
安裝完成后就可以使用 kubectl clusternet 命令了:
- $ kubectl clusternet -h
- Usage:
- clusternet [flags]
- clusternet [command]
- Available Commands:
- apply Apply a configuration to a resource by filename or stdin
- create Create a resource from a file or from stdin.
- delete Delete resources by filenames, stdin, resources and names, or by resources and label selector
- edit Edit a resource on the server
- get Display one or many resources
- help Help about any command
- scale Set a new size for a Deployment, ReplicaSet or Replication Controller
- version Print the plugin version information
4 示例
當(dāng) Clusternet 部署完成后,接下來(lái)我們就可以來(lái)嘗試將應(yīng)用部署到多個(gè)集群了。Clusternet 支持從一個(gè)托管集群的一組 API 中向多個(gè)集群部署應(yīng)用程序。
注意:Deployer 特性需要由 clusternet-hub 開(kāi)啟。
首先,讓我們看一個(gè)示例應(yīng)用。下面名為 "app-demo" 的 Subscription 定義了要分發(fā)的目標(biāo)子集群,以及要部署的資源。
- # examples/applications/subscription.yaml
- apiVersion: apps.clusternet.io/v1alpha1
- kind: Subscription
- metadata:
- name: app-demo
- namespace: default
- spec:
- subscribers: # defines the clusters to be distributed to
- - clusterAffinity:
- matchLabels:
- clusters.clusternet.io/cluster-id: dc91021d-2361-4f6d-a404-7c33b9e01118 # PLEASE UPDATE THIS CLUSTER-ID TO YOURS!!!
- feeds: # defines all the resources to be deployed with
- - apiVersion: apps.clusternet.io/v1alpha1
- kind: HelmChart
- name: mysql
- namespace: default
- - apiVersion: v1
- kind: Namespace
- name: foo
- - apiVersion: apps/v1
- kind: Service
- name: my-nginx-svc
- namespace: foo
- - apiVersion: apps/v1
- kind: Deployment
- name: my-nginx
- namespace: foo
在應(yīng)用這個(gè) Subscription 對(duì)象之前,請(qǐng)用你的集群 ID 更新 examples/applications/subscription.yaml。
在安裝了 kubectl 插件 kubectl-clusternet 之后,你可以運(yùn)行下面的命令將這個(gè)應(yīng)用程序分發(fā)到子集群:
- $ kubectl clusternet apply -f examples/applications/
- helmchart.apps.clusternet.io/mysql created
- namespace/foo created
- deployment.apps/my-nginx created
- service/my-nginx-svc created
- subscription.apps.clusternet.io/app-demo created
然后可以使用下面的命令查看剛剛創(chuàng)建的資源:
- $ # list Subscription
- $ kubectl clusternet get subs -A
- NAMESPACE NAME AGE
- default app-demo 6m4s
- $ kubectl clusternet get chart
- NAME CHART VERSION REPO STATUS AGE
- mysql mysql 8.6.2 https://charts.bitnami.com/bitnami Found 71s
- $ kubectl clusternet get ns
- NAME CREATED AT
- foo 2021-08-07T08:50:55Z
- $ kubectl clusternet get svc -n foo
- NAME CREATED AT
- my-nginx-svc 2021-08-07T08:50:57Z
- $ kubectl clusternet get deploy -n foo
- NAME CREATED AT
- my-nginx 2021-08-07T08:50:56Z
Clusternet 將幫助部署和協(xié)調(diào)應(yīng)用程序到多個(gè)集群,可以通過(guò)以下命令檢查狀態(tài)。
- $ kubectl clusternet get mcls -A
- NAMESPACE NAME CLUSTER ID SYNC MODE KUBERNETES READYZ AGE
- clusternet-5l82l clusternet-cluster-hx455 dc91021d-2361-4f6d-a404-7c33b9e01118 Dual v1.21.0 true 5d22h
- $ # list Descriptions
- $ kubectl clusternet get desc -A
- NAMESPACE NAME DEPLOYER STATUS AGE
- clusternet-5l82l app-demo-generic Generic Success 2m55s
- clusternet-5l82l app-demo-helm Helm Success 2m55s
- $ kubectl describe desc -n clusternet-5l82l app-demo-generic
- ...
- Status:
- Phase: Success
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal SuccessfullyDeployed 2m55s clusternet-hub Description clusternet-5l82l/app-demo-generic is deployed successfully
- $ # list Helm Release
- $ # hr is an alias for HelmRelease
- $ kubectl clusternet get hr -n clusternet-5l82l
- NAME CHART VERSION REPO STATUS AGE
- helm-demo-mysql mysql 8.6.2 https://charts.bitnami.com/bitnami deployed 2m55s
當(dāng)然也可以在子集群中用 Helm 命令行工具來(lái)驗(yàn)證安裝情況,比如:
- $ helm ls -n abc
- NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
- helm-demo-mysql abc 1 2021-07-06 14:34:44.188938 +0800 CST deployed mysql-8.6.2 8.0.25
這樣我們就成功將一個(gè)應(yīng)用輕松分發(fā)到多個(gè)集群了。關(guān)于 Clusternet 的更多細(xì)節(jié)和使用方法請(qǐng)查看官方倉(cāng)庫(kù):https://github.com/clusternet/clusternet 了解更多。