使用 KubeBlocks 实现多 K8s 部署
KubeBlocks 支持管理多个 Kubernetes 集群,为用户在实例容灾、K8s 集群管理等方面提供新的选项。为支持多 K8s 管理,KubeBlocks 引入了控制面(control plane )和数据面(data plane)。
- 控制面:一个独立的 K8s 集群,KubeBlocks operator 运行在该集群当中,KubeBlocks 定义的相关对象大都存放在这个集群(比如 Definition、Cluster、Backup、Ops 等)。用户通过跟这个集群的 API 进行交互来实现对多集群实例的管理。
- 数据面:用于运行最终工作负载的 K8s 集群,数量可以是一到多个。这些集群当中会 hosting 实例相关的计算、存储、网络等资源,如 Pod、PVC、Service、SA、CM、Secret、Jobs 等,而 KubeBlocks operator 目前(v0.9.0)不会在这些集群中运行。
实际物理部署上,控制面可以选择部署在单个 AZ,简单灵活;也可以选择部署在多个不同 AZ,提供更高的可用性保证;也可以复用某个数据面集群,以更低成本的方式运行。
环境准备
准备 K8s 集群,并准备部署 KubeBlocks 所需的配置信息。本文示例准备了三个数据面集群,context 分别命名为:k8s-1
、k8s-2
、k8s-3
。
准备 K8s 集群:1 个设定为控制面,其他几个设定为数据面,确保这些数据面集群的 API server 在控制面集群中可以联通。这里的联通包含两个层面:一是网络连通,二是访问配置。
准备 KubeBlocks 访问数据面所需的配置信息,以 secret 形式放置在控制面集群当中,部署 KubeBlocks 时需要传入。其中,secret key 要求为 “kubeconfig”,value 为标准 kubeconfig 内容格式。示例如下:
apiVersion: v1
kind: Secret
metadata:
namespace: kb-system
name: <your-secret-name>
type: kubernetes.kubeconfig
stringData:
kubeconfig: |
apiVersion: v1
clusters:
...
contexts:
...
kind: Config
users:
...
部署多 K8s 集群
部署 Kubeblocks
在控制面安装 KubeBlocks。
安装 KubeBlocks。
# multiCluster.kubeConfig 指定存放数据面 k8s kubeconfig 信息的 secret
# multiCluster.contexts 指定数据面 K8s contexts
kbcli kubeblocks install --version=0.9.0 --set multiCluster.kubeConfig=<secret-name> --set multiCluster.contexts=<contexts>查看安装状态,确保 KubeBlocks 安装完成。
kbcli kubeblocks status
>
KubeBlocks is deployed in namespace: kb-system,version: 0.9.0
KubeBlocks Workloads:
NAMESPACE KIND NAME READY PODS CPU(CORES) MEMORY(BYTES) CREATED-AT
kb-system Deployment kb-addon-snapshot-controller 1/1 N/A N/A Oct 25,2024 15:18 UTC+0800
kb-system Deployment kubeblocks 1/1 N/A N/A Oct 25,2024 15:18 UTC+0800
kb-system Deployment kubeblocks-dataprotection 1/1 N/A N/A Oct 25,2024 15:18 UTC+0800
RBAC
实例工作负载在数据面中运行时,需要特定的 RBAC 资源进行管理动作,因此需要预先在各数据面集群单独安装 KubeBlocks 所需的 RBAC 资源。
# 1. 从控制面 dump 所需的 clusterrole 资源:kubeblocks-cluster-pod-role
kubectl get clusterrole kubeblocks-cluster-pod-role -o yaml > /tmp/kubeblocks-cluster-pod-role.yaml
# 2. 编辑文件内容,去除不必要的 meta 信息(比如 UID、resource version),保留其他内容
# 3. 将文件内容应用到其他数据面集群
kubectl apply -f /tmp/kubeblocks-cluster-pod-role.yaml --context=k8s-1
kubectl apply -f /tmp/kubeblocks-cluster-pod-role.yaml --context=k8s-2
kubectl apply -f /tmp/kubeblocks-cluster-pod-role.yaml --context=k8s-3
网络
KubeBlocks 基于 K8s service 抽象来提供内外部的服务访问。对于 service 的抽象,集群内的访问 K8s 一般会有默认的实现,对于来自集群外的流量通常需要用户自己提供方案。而在多 K8s 形态下,无论是实例间的复制流量、还是客户端的访问流量,基本都属于“集群外流量”。因此为了让跨集群实例能够正常工作,网络部分一般需要进行一些额外的处理。
这里会以一组可选的方案为例,用来完整描述整个流程。实际使用中,用户可以根据自身集群和网络环境,选择适合的方案进行部署。
东西向流量
云上方案
云厂商提供的 K8s 服务一般都提供了内/外网 load balancer service 可供使用,这样可以直接基于 LB service 来构建副本之间的互访,简单易用。
自建方案
东西向互访的自建方案以 Cilium Cluster Mesh 为例来进行说明,Cilium 的部署选择 overlay 模式,各数据面集群配置如下:
集群 | Context | 名称 | ID | CIDR |
---|---|---|---|---|
1 | k8s-1 | k8s-1 | 1 | 10.1.0.0/16 |
2 | k8s-2 | k8s-2 | 2 | 10.2.0.0/16 |
3 | k8s-3 | k8s-3 | 3 | 10.3.0.0/16 |
这里的 CIDR 是 Cilium Overlay 网络的地址,具体设置时要跟主机网络地址段区分开。
步骤:
下述操作步骤相关命令,可以在各个集群分别执行(不需要指定 --context
参数),也可以在有三个 context 信息的环境里统一执行(分别指定 --context
参数)。
安装 Cilium,指定集群 ID/名称和 Pool Pod CIDR。可参考官方文档:指定集群名称和 ID。
cilium install --set cluster.name=k8s-1 --set cluster.id=1 --set ipam.operator.clusterPoolIPv4PodCIDRList=10.1.0.0/16 —context k8s-1
cilium install --set cluster.name=k8s-2 --set cluster.id=2 --set ipam.operator.clusterPoolIPv4PodCIDRList=10.2.0.0/16 —context k8s-2
cilium install --set cluster.name=k8s-3 --set cluster.id=3 --set ipam.operator.clusterPoolIPv4PodCIDRList=10.3.0.0/16 —context k8s-3开启 Cilium Cluster Mesh,并等待其状态为
ready
。这里以 NodePort 方式提供对 Cluster Mesh 控制面的访问,其他可选方式及具体信息请参考官方文档:启用 Cluster Mesh。cilium clustermesh enable --service-type NodePort —context k8s-1
cilium clustermesh enable --service-type NodePort —context k8s-2
cilium clustermesh enable --service-type NodePort —context k8s-3
cilium clustermesh status —wait —context k8s-1
cilium clustermesh status —wait —context k8s-2
cilium clustermesh status —wait —context k8s-3打通各集群,并等待集群状态为
ready
。具体可参考官方文档:连接集群。cilium clustermesh connect --context k8s-1 --destination-context k8s-2
cilium clustermesh connect --context k8s-1 --destination-context k8s-3
cilium clustermesh connect --context k8s-2 --destination-context k8s-3
cilium clustermesh status —wait —context k8s-1
cilium clustermesh status —wait —context k8s-2
cilium clustermesh status —wait —context k8s-3(可选)可以通过 cilium-dbg 工具检查跨集群的 tunnel 情况。具体可参考官方文档:cilium dbg。
cilium-dbg bpf tunnel list
(可选)集群连通性测试,可参考官方文档:测试集群间 Pod 连通性。
南北向流量
南北向流量为客户端提供服务,需要每个数据面的 Pod 都有对外的连接地址,这个地址的实现可以是 NodePort、LoadBalancer 或者其他方案,我们以 NodePort 和 LoadBalancer 为例介绍。
如果客户端不具备读写路由能力,那在 Pod 地址之上,还需要提供读写分离地址,实现上可以用七层的 Proxy,四层的 SDN VIP,或者纯粹的 DNS。为了简化问题,此处先假设客户端具备读写路由能力,可以直接配置所有 Pod 连接地址。
NodePort
为每个数据面集群的 Pod 创建 NodePort Service,客户端使用用主机网络 IP 和 NodePort 即可连接。
LoadBalancer
此处以 MetalLB 提供 LoadBalancer Service 为例。
准备数据面的 LB 网段,该网段需要跟客户端路由可达,并且不同 K8s 集群要错开。
集群 Context 名称 ID CIDR 1 k8s-1 k8s-1 1 10.4.0.0/16 2 k8s-2 k8s-2 2 10.5.0.0/16 3 k8s-3 k8s-3 3 10.6.0.0/16 在所有数据面部署 MetalLB。
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb等待相关 Pod 状态变为
ready
。kubectl wait --namespace metallb-system --for=condition=ready pod --selector=app=metallb --timeout=90s
在三个 K8s 集群执行以下 YAML 文件,请注意替换
spec.addresses
为对应 K8s 集群的 LB 网段。apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: example
namespace: metallb-system
spec:
addresses:
- x.x.x.x/x
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: empty
namespace: metallb-system为每个数据面集群的 Pod 创建 LoadBalancer Service,拿到所有 VIP,即可供客户端连接。
验证
多集群实例的运行,各个副本之间的访问地址不能直接简单使用原 domain 内的地址(比如 Pod FQDN),需要显式的创建并配置使用跨集群的服务地址来进行通信,因此需要对引擎进行适配。
这里以社区版 etcd 为例来进行演示,相关适配的结果可以参考 etcd 引擎。
创建实例
由于不同网络要求的配置不同,这里分别以云上和自建两种方式为例说明如果创建一个三副本的跨集群 etcd 实例。
云上方案
这里以阿里云为例,其他厂商的配置可以参考官方文档。
apiVersion: apps.kubeblocks.io/v1alpha1
kind: Cluster
metadata:
namespace: default
generateName: etcd
annotations:
# 可选:可以用该 annotation 显式指定当前实例要求分布的集群
apps.kubeblocks.io/multi-cluster-placement: "k8s-1,k8s-2,k8s-3"
spec:
terminationPolicy: WipeOut
componentSpecs:
- componentDef: etcd-0.9.0
name: etcd
replicas: 3
resources:
limits:
cpu: 100m
memory: 100M
requests:
cpu: 100m
memory: 100M
volumeClaimTemplates:
- name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi # 云上 provisioning 要求的最小 size
- name: peer
serviceType: LoadBalancer
annotations:
# 如果运行在基于 LoadBalancer service 提供的互访方案上,这个 annotation key 为必填项
apps.kubeblocks.io/multi-cluster-service-placement: unique
# ACK LoadBalancer service 要求的 annotation key
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: intranet
podService: true
如下示例展示了如何跨云厂商部署。
apiVersion: apps.kubeblocks.io/v1alpha1
kind: Cluster
metadata:
namespace: default
generateName: etcd
annotations:
# 可选:可以用该 annotation 显式指定当前实例要求分布的集群
apps.kubeblocks.io/multi-cluster-placement: "k8s-1,k8s-2,k8s-3"
spec:
terminationPolicy: WipeOut
componentSpecs:
- componentDef: etcd-0.9.0
name: etcd
replicas: 3
resources:
limits:
cpu: 100m
memory: 100M
requests:
cpu: 100m
memory: 100M
volumeClaimTemplates:
- name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi # 云上 provisioning 要求的最小 size
services:
- name: peer
serviceType: LoadBalancer
annotations:
# 如果运行在基于 LoadBalancer service 提供的互访方案上,这个 annotation key 为必填项
apps.kubeblocks.io/multi-cluster-service-placement: unique
# ACK LoadBalancer service 要求的 annotation key。因为要跨云访问,因此需要配置为公网类型
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: internet
# VKE LoadBalancer service 要求的 annotation keys。因为要跨云访问,因此需要配置为公网类型
service.beta.kubernetes.io/volcengine-loadbalancer-subnet-id: <subnet-id>
service.beta.kubernetes.io/volcengine-loadbalancer-address-type: "PUBLIC"
podService: true
自建方案
该示例展示了如何在自建环境创建实例。
apiVersion: apps.kubeblocks.io/v1alpha1
kind: Cluster
metadata:
namespace: default
generateName: etcd
annotations:
# 可选:可以用该 annotation 显式指定当前实例要求分布的集群
apps.kubeblocks.io/multi-cluster-placement: "k8s-1,k8s-2,k8s-3"
spec:
terminationPolicy: WipeOut
componentSpecs:
- componentDef: etcd-0.9.0
name: etcd
replicas: 3
resources:
limits:
cpu: 100m
memory: 100M
requests:
cpu: 100m
memory: 100M
volumeClaimTemplates:
- name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
services:
- name: peer
serviceType: ClusterIP
annotations:
service.cilium.io/global: "true" # cilium clustermesh global service
podService: true