简介 腾讯云开源的k8s多集群管理方案,可发布应用到多个k8s集群。https://github.com/clusternet/clusternet GitHub Star:891
架构 包含clusternet-hub和clusternet-agent两个组件。
clusternet-hub部署在父集群,用途:
接收子集群的注册请求,并为每个自己群创建namespace、serviceaccount等资源
提供父集群跟各个子集群的长连接
提供restful api来访问各个子集群,同时支持子集群的互访
clusternet-agent部署在子集群,用途:
自动注册子集群到父集群
心跳报告到中心集群,包括k8s版本、运行平台、健康状态等
通过websocket协议跟父集群通讯
CRD抽象 ClusterRegistrationRequest clusternet-agent在父集群中创建的CR,一个k8s集群一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: clusters.clusternet.io/v1beta1 kind: ClusterRegistrationRequest metadata: labels: clusters.clusternet.io/cluster-id : 2 bcd48d2-0ead-45f7-938b-5af6af7da5fd clusters.clusternet.io/cluster-name : clusternet-cluster-pmlbp clusters.clusternet.io/registered-by : clusternet-agent name: clusternet-2bcd48d2-0ead-45f7-938b-5af6af7da5fd spec: clusterId: 2 bcd48d2-0ead-45f7-938b-5af6af7da5fd clusterName: clusternet-cluster-pmlbp clusterType: EdgeCluster syncMode: Dual status: caCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1USXhNekV3TWpjeE5Wb1hEVE14TVRJeE1URXdNamN4TlZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTHVCCmJwdXNaZXM5RXFXaEw1WUVGb2c4eHRJai9jbFh2S1lTMnZYbGR4cUt1MUR3VHV3aUcxZUN4VGlHa2dLQXVXd1IKVFFheEh5aGY3U29lc0hqMG43NnFBKzhQT05lS1VGdERJOWVzejF2WTF5bXFoUHR0QVo0cGhkWmhmbXJjZTJLRQpuMS84MzNWbWlXd0pSZmNWcEJtTU52MjFYMVVwNWp6RGtncS9tY0JOOGN0ZU5PMEpKNkVVeTE2RXZLbjhyWG90ClErTW5PUHE4anFNMzJjRFFqYWVEdGxvM2kveXlRd1NMa2wzNFo4aElwZDZNWWVSTWpXcmhrazF4L0RYZjNJK3IKOGs5S1FBbEsvNWZRMXk5NHYwT25TK0FKZTliczZMT2srYVFWYm5SbExab2E2aVZWbUJNam03UjBjQ2w0Y1hpRwpyekRnN1ZLc3lsY1o3aGFRRTNFQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZPaGwxMHUwNnJvenZJUm9XVmpNYlVPMzFDbERNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBQytqMXpPQVZYVXpNUFJ0U29Kd3JhMU1FSkJOUTNMWitmWnZRYjdIbk53b00zaCthMgoyc25yUitSWTYrOFFDbXVKeis1eU5yYStEZDlnNzQ1Q0hDaFpVZzlsY3RjQTRzZVR5OXZqcUVQNVBNSzEvbi9PCnFEcDQyMUpqTjYvUXJmamIvbVM0elUvZXNGZGowQXRYQ0FLMWJsNmF0ai9jZXVBbzh1bTRPaUVlNnJhanBYTHUKWFlmQ1FVZFV5TWpQdEZHTDMwNytna0RpdlVsdXk3RkQ4aUpURTh2QWpxOVBXOW40SmxFMjdQWXR5QnNocy9XSApCZ2czQjZpTG10SjZlNzJiWnA3ZmptdmJWTC9VdkxzYXZqRXltZDhrMnN1bFFwQUpzeVJrMkEzM1g3NWJpS0RGCk1CU29DaHc3U2JMSkJrN0FNckRzTjd1Q2U3WWVIWGdZemdhRAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== dedicatedNamespace: clusternet-sdqrh managedClusterName: clusternet-cluster-pmlbp result: Approved token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNkluZFpWMUV6UmxCbFdUUldWa1Y0UmxBeWMzaDJOV3RuUzBabVJsaG9jWG94TkhKM2JtUkxiRTlrYUZVaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUpqYkhWemRHVnlibVYwTFhOa2NYSm9JaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbU5zZFhOMFpYSnVaWFF0YmpneWFHc3RkRzlyWlc0dGN6SnpjRFVpTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1dVlXMWxJam9pWTJ4MWMzUmxjbTVsZEMxdU9ESm9heUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG5WcFpDSTZJamN3TWpRMk1tTTBMV0prTlRFdE5HVXpNeTA1WXpnekxUWmpPV1F6TUdGall6bG1aU0lzSW5OMVlpSTZJbk41YzNSbGJUcHpaWEoyYVdObFlXTmpiM1Z1ZERwamJIVnpkR1Z5Ym1WMExYTmtjWEpvT21Oc2RYTjBaWEp1WlhRdGJqZ3lhR3NpZlEuUjJCdEQ1YzQxRm1seC1hTEFKSWQzbGxvOThSY25TakVUak5pSnVuTXg1US1jYXhwc3VkamUxekVtUF9mTHR6TjU4d21Dd3RXcHpoaXhSMWFTUHE5LXJTRlBIYzk3aDlTT3daZGdicC10alFBSjA4dllfYWdiVFRKLU1WN1dpN0xQVzRIcmt1U3RlemUzVHh2RW11NWwySlpzbG5UTXdkR3NGRVlVZzVfeWFoLUQwQ2pnTkZlR1ljUjJ1TlJBWjdkNW00Q1c5VmRkdkNsanNqRE5WX1k0RkFEbGo2cHgzSlh0SDQ4U1ZUd254TS1sNHl0eXBENjZFbG1PYXpUMmRwSWd4eWNSZ0tJSUlacWNQNnZVc0ZOM1Zka21ZZ29ydy1NSUcwc25YdzFaZ1lwRk9SUDJRa3hjd2NOUVVOTjh6VUZqSUZSSmdSSFdjNlo4aXd2eURnZTVn
ClusterRole和Role ClusterRegistrationRequest被接收后会创建ClusterRole
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 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: clusternet.io/autoupdate: "true" labels: clusternet.io/created-by : clusternet-hub clusters.clusternet.io/bootstrapping: rbac-defaults clusters.clusternet.io/cluster-id : 2 bcd48d2-0ead-45f7-938b-5af6af7da5fd name: clusternet-2bcd48d2-0ead-45f7-938b-5af6af7da5fd rules: - apiGroups: - clusters.clusternet.io resources: - clusterregistrationrequests verbs: - create - get - apiGroups: - proxies.clusternet.io resourceNames: - 2 bcd48d2-0ead-45f7-938b-5af6af7da5fd resources: - sockets verbs: - '*'
并会在cluster对应的namespace下创建Role
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: annotations: clusternet.io/autoupdate: "true" labels: clusternet.io/created-by : clusternet-hub clusters.clusternet.io/bootstrapping: rbac-defaults name: clusternet-managedcluster-role namespace: clusternet-sdqrh rules: - apiGroups: - '*' resources: - '*' verbs: - '*'
ManagedCluster clusternet-hub在接受ClusterRegistrationRequest后,会创建一个ManagedCluster。一个k8s集群一个namespace
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: clusters.clusternet.io/v1beta1 kind: ManagedCluster metadata: labels: clusternet.io/created-by : clusternet-agent clusters.clusternet.io/cluster-id : 2 bcd48d2-0ead-45f7-938b-5af6af7da5fd clusters.clusternet.io/cluster-name : clusternet-cluster-pmlbp name: clusternet-cluster-pmlbp namespace: clusternet-sdqrh spec: clusterId: 2 bcd48d2-0ead-45f7-938b-5af6af7da5fd clusterType: EdgeCluster syncMode: Dual status: allocatable: cpu: 7600 m memory: "30792789992" apiserverURL: https://10.233 .0.1 :443 appPusher: true capacity: cpu: "8" memory: 32192720 Ki clusterCIDR: 10.233 .0.0 /18 conditions: - lastTransitionTime: "2021-12-15T07:47:10Z" message: managed cluster is ready. reason: ManagedClusterReady status: "True" type : Ready healthz: true heartbeatFrequencySeconds: 180 k8sVersion: v1.21.5 lastObservedTime: "2021-12-15T07:47:10Z" livez: true nodeStatistics: readyNodes: 1 parentAPIServerURL: https://172.21 .115.160 :6443 platform: linux/amd64 readyz: true serviceCIDR: 10.233 .64.0 /18 useSocket: true
Subscription 应用发布的抽象,人工提交到环境中。要发布的资源即可以是helm chart,也可以是原生的k8s对象。clusternet在部署的时候会查看部署的优先级,并且支持weight,优先部署cluster级别的资源,这点跟helm部署的逻辑一致。
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 apiVersion: apps.clusternet.io/v1alpha1 kind: Subscription metadata: name: app-demo namespace: default spec: subscribers: - clusterAffinity: matchLabels: clusters.clusternet.io/cluster-id : dc91021d-2361-4f6d-a404-7c33b9e01118 feeds: - apiVersion: apps.clusternet.io/v1alpha1 kind: HelmChart name: mysql namespace: default - apiVersion: v1 kind: Namespace name: foo - apiVersion: v1 kind: Service name: my-nginx-svc namespace: foo - apiVersion: apps/v1 kind: Deployment name: my-nginx namespace: foo
HelmChart Subscriber发布的一个子资源之一,对应一个helm chart的完整描述
1 2 3 4 5 6 7 8 9 10 apiVersion: apps.clusternet.io/v1alpha1 kind: HelmChart metadata: name: mysql namespace: default spec: repo: https://charts.bitnami.com/bitnami chart: mysql version: 8.6 .2 targetNamespace: abc
Localization 差异化不同于其他集群的配置,用来描述namespace级别的差异化配置。
Globalization 用来描述不同集群的cluster级别的差异化配置。
Base Description 跟Base对象根据Localization和Globalization渲染得到的最终要发布到集群中的最终对象。
安装 本文使用kind进行测试,使用kind创建两个k8s集群host和member1,两个集群的apiserver均监听在宿主机的端口,确保从一个集群可以访问到另外一个集群的apiserver。
在父集群执行如下命令安装clusternet管控组件:
1 2 3 helm repo add clusternet https://clusternet.github.io/charts helm install clusternet-hub -n clusternet-system --create-namespace clusternet/clusternet-hub kubectl apply -f https://raw.githubusercontent.com/clusternet/clusternet/main/manifests/samples/cluster_bootstrap_token.yaml
会在clusternet-system namespace下创建deployment clusternet-hub。
最后一条命令,会创建一个secret,其中的token信息为07401b.f395accd246ae52d,在子集群注册的时候需要用到。
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 apiVersion: v1 kind: Secret metadata: name: bootstrap-token-07401b namespace: kube-system type : bootstrap.kubernetes.io/tokenstringData: description: "The bootstrap token used by clusternet cluster registration." token-id : 07401 b token-secret : f395accd246ae52d expiration: 2025 -05-10T03 :22 :11 Z usage-bootstrap-authentication : "true" usage-bootstrap-signing : "true" auth-extra-groups : system:bootstrappers:clusternet:register-cluster -token
在子集群执行如下命令,其中parentURL要修改为父集群的apiserver地址
1 2 3 4 5 helm repo add clusternet https://clusternet.github.io/charts helm install clusternet-agent -n clusternet-system --create-namespace \ --set parentURL=https://10.0 .248.96 :8443 \ --set registrationToken=07401 b.f395accd246ae52d \ clusternet/clusternet-agent
其中parentURL为父集群的apiserver地址,registrationToken为父集群注册的token信息。
访问子集群 kubectl-clusternet命令行方式访问 安装kubectl krew插件,参考 https://krew.sigs.k8s.io/docs/user-guide/setup/install/ ,执行如下命令:
1 2 3 4 5 6 7 8 9 10 11 12 yum install git -y ( set -x ; cd "$ (mktemp -d)" && OS="$ (uname | tr '[:upper:]' '[:lower:]')" && ARCH="$ (uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$ /arm64/')" && KREW="krew-$ {OS}_$ {ARCH}" && curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/$ {KREW}.tar.gz" && tar zxvf "$ {KREW}.tar.gz" && ./"$ {KREW}" install krew ) echo 'export PATH="${PATH}:${HOME}/.krew/bin"' >> ~/.bashrcsource ~/.bashrc
安装kubectl插件clusternet:kubectl krew install clusternet
使用kubectl clusternet get可以看到发布的应用,非发布的应用看不到。
代码层面访问子集群 可以参照例子:https://github.com/clusternet/clusternet/blob/main/examples/clientgo/demo.go#L42-L45
改动代码非常小,仅需要获取到对应集群的config信息即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 config, err := rest.InClusterConfig() if err != nil { klog.Error(err) os.Exit(1 ) } // This is the ONLY place you need to wrap for Clusternet config.Wrap(func(rt http.RoundTripper) http.RoundTripper { return clientgo.NewClusternetTransport(config.Host, rt) }) // now we could create and visit all the resources client := kubernetes.NewForConfigOrDie(config) _, err = client.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "demo" , }, }, metav1.CreateOptions{})
应用发布 在主集群提交如下的yaml文件
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 apiVersion: apps.clusternet.io/v1alpha1 kind: Subscription metadata: name: app-demo namespace: default spec: subscribers: - clusterAffinity: matchLabels: clusters.clusternet.io/cluster-id : dc91021d-2361-4f6d-a404-7c33b9e01118 feeds: - 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
实现分析 在主集群实现了两个aggregated apiservice
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: annotations: meta.helm.sh/release-name : clusternet-hub meta.helm.sh/release-namespace : clusternet-system labels: app.kubernetes.io/instance: clusternet-hub app.kubernetes.io/managed-by : Helm app.kubernetes.io/name: clusternet-hub helm.sh/chart: clusternet-hub-0 .2.1 name: v1alpha1.proxies.clusternet.io spec: group : proxies.clusternet.io groupPriorityMinimum: 1000 insecureSkipTLSVerify: true service: name: clusternet-hub namespace: clusternet-system port: 443 version: v1alpha1 versionPriority: 100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: annotations: meta.helm.sh/release-name : clusternet-hub meta.helm.sh/release-namespace : clusternet-system labels: app.kubernetes.io/instance: clusternet-hub app.kubernetes.io/managed-by : Helm app.kubernetes.io/name: clusternet-hub helm.sh/chart: clusternet-hub-0 .2.1 name: v1alpha1.shadow spec: group : shadow groupPriorityMinimum: 1 insecureSkipTLSVerify: true service: name: clusternet-hub namespace: clusternet-system port: 443 version: v1alpha1 versionPriority: 1
相关链接