Karmada Cluster 组件详解
目录
- 一、使用背景
- 二、Cluster API 定义
- 三、使用方式
- 四、源码原理
- 五、总结
一、使用背景
1.1 为什么需要 Cluster 资源?
在 Karmada 多集群管理系统中,Cluster 是代表成员集群的核心资源对象。它的主要作用是:
- 集群注册与发现:将 Kubernetes 集群注册到 Karmada 控制平面
- 集群生命周期管理:管理集群的加入、健康检查、故障处理、删除等
- 集群属性定义:记录集群的地理位置(Region/Zone)、云提供商、资源模型等信息
- 调度决策依据:为 Scheduler 提供集群信息,用于资源分发决策
- 状态同步:收集和展示成员集群的实时状态(节点、资源等)
1.2 在多集群架构中的位置
- Karmada 控制平面
- │
- ├── Cluster (成员集群1) ──┐
- ├── Cluster (成员集群2) ──┼──> ResourceBinding ──> Work ──> 实际资源
- └── Cluster (成员集群3) ──┘
复制代码 Cluster 是 Karmada 与成员集群之间的桥梁,所有资源分发都必须基于已注册的 Cluster。
二、Cluster API 定义
2.1 基本结构
Cluster 是一个集群级(ClusterScope)的 CRD 资源,定义在 pkg/apis/cluster/v1alpha1/types.go:- // Cluster represents the desired state and status of a member cluster.
- type Cluster struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
-
- // Spec 定义了集群的期望状态(配置信息)
- Spec ClusterSpec `json:"spec"`
-
- // Status 表示集群的实际状态(运行时信息)
- Status ClusterStatus `json:"status,omitempty"`
- }
复制代码 2.2 ClusterSpec - 期望配置
ClusterSpec 定义了集群的配置信息:
核心字段
- type ClusterSpec struct {
- // ID: 集群唯一标识符
- // 1. 优先从 ClusterProperty API 获取
- // 2. 否则使用 kube-system namespace 的 UID
- ID string `json:"id,omitempty"`
-
- // SyncMode: 同步模式(Push 或 Pull)
- // - Push: 控制平面主动推送资源到成员集群
- // - Pull: 成员集群的 Agent 主动拉取资源
- SyncMode ClusterSyncMode `json:"syncMode"`
-
- // APIEndpoint: 成员集群的 API 地址
- APIEndpoint string `json:"apiEndpoint,omitempty"`
-
- // SecretRef: 访问成员集群的凭证(包含 token 和 caBundle)
- SecretRef *LocalSecretReference `json:"secretRef,omitempty"`
-
- // ImpersonatorSecretRef: Impersonator 的凭证(用于 Pull 模式)
- ImpersonatorSecretRef *LocalSecretReference `json:"impersonatorSecretRef,omitempty"`
-
- // 连接配置
- InsecureSkipTLSVerification bool `json:"insecureSkipTLSVerification,omitempty"`
- ProxyURL string `json:"proxyURL,omitempty"`
- ProxyHeader map[string]string `json:"proxyHeader,omitempty"`
-
- // 集群属性(用于调度)
- Provider string `json:"provider,omitempty"` // 云提供商(如 aws, alibaba)
- Region string `json:"region,omitempty"` // 区域
- Zones []string `json:"zones,omitempty"` // 可用区列表
-
- // Taints: 集群污点(用于调度决策)
- Taints []corev1.Taint `json:"taints,omitempty"`
-
- // ResourceModels: 资源模型(用于资源估算和调度)
- ResourceModels []ResourceModel `json:"resourceModels,omitempty"`
- }
复制代码 SyncMode 详解
- const (
- // Push 模式:控制平面主动推送
- // - 控制器在控制平面运行,直接连接成员集群 API
- // - 适合网络可达的场景
- Push ClusterSyncMode = "Push"
-
- // Pull 模式:成员集群主动拉取
- // - Agent 在成员集群运行,拉取控制平面的资源
- // - 适合网络受限或安全隔离的场景
- Pull ClusterSyncMode = "Pull"
- )
复制代码 2.3 ClusterStatus - 运行时状态
ClusterStatus 记录了集群的实时状态:- type ClusterStatus struct {
- // KubernetesVersion: Kubernetes 版本
- KubernetesVersion string `json:"kubernetesVersion,omitempty"`
-
- // APIEnablements: 集群支持的 API 资源列表
- APIEnablements []APIEnablement `json:"apiEnablements,omitempty"`
-
- // Conditions: 集群条件状态
- Conditions []metav1.Condition `json:"conditions,omitempty"`
-
- // NodeSummary: 节点摘要
- NodeSummary *NodeSummary `json:"nodeSummary,omitempty"`
-
- // ResourceSummary: 资源摘要(可分配、已分配、等待分配)
- ResourceSummary *ResourceSummary `json:"resourceSummary,omitempty"`
-
- // RemedyActions: 需要执行的修复操作
- RemedyActions []string `json:"remedyActions,omitempty"`
- }
复制代码 Conditions 类型
- const (
- // ClusterConditionReady: 集群是否就绪
- ClusterConditionReady = "Ready"
-
- // ClusterConditionCompleteAPIEnablements: API 启用是否完整
- ClusterConditionCompleteAPIEnablements = "CompleteAPIEnablements"
- )
复制代码 ResourceSummary 结构
- type ResourceSummary struct {
- // Allocatable: 可分配资源总量(所有节点的总和)
- Allocatable corev1.ResourceList `json:"allocatable,omitempty"`
-
- // Allocating: 等待调度的资源(Pending Pods)
- Allocating corev1.ResourceList `json:"allocating,omitempty"`
-
- // Allocated: 已分配的资源(已调度的 Pods)
- Allocated corev1.ResourceList `json:"allocated,omitempty"`
-
- // AllocatableModelings: 资源模型统计
- AllocatableModelings []AllocatableModeling `json:"allocatableModelings,omitempty"`
- }
复制代码 三、使用方式
3.1 注册集群(Push 模式)
使用 karmadactl join 命令注册集群:- # 基本用法
- karmadactl join CLUSTER_NAME \
- --cluster-kubeconfig=<成员集群的 kubeconfig> \
- --kubeconfig=<Karmada 控制平面的 kubeconfig>
- # 完整示例
- karmadactl join member1 \
- --cluster-kubeconfig=$HOME/.kube/member1.config \
- --cluster-namespace=karmada-cluster \
- --cluster-provider=aliyun \
- --cluster-region=cn-hangzhou \
- --cluster-zones=cn-hangzhou-a,cn-hangzhou-b \
- --kubeconfig=$HOME/.kube/karmada.config
复制代码 工作原理:
- 读取成员集群的 kubeconfig
- 提取集群 ID(优先从 ClusterProperty,否则使用 kube-system UID)
- 创建访问凭证(Secret)
- 在控制平面创建 Cluster 对象
- Cluster Controller 检测到新集群,创建 ExecutionSpace(命名空间)
3.2 注册集群(Pull 模式)
Pull 模式需要成员集群主动注册:- # Step 1: 在控制平面创建 bootstrap token
- karmadactl token create --print-register-command \
- --kubeconfig=<Karmada 控制平面的 kubeconfig>
- # 输出示例:
- # karmadactl register 172.18.0.5:5443 \
- # --token t8xfio.640u9gp9obc72v5d \
- # --discovery-token-ca-cert-hash sha256:9cfa542ff48f43793d1816b1dd0a78ad574e349d8f6e005e6e32e8ab528e4244
- # Step 2: 在成员集群执行注册命令(指定成员集群的 kubeconfig)
- karmadactl register 172.18.0.5:5443 \
- --token t8xfio.640u9gp9obc72v5d \
- --discovery-token-ca-cert-hash sha256:9cfa542ff48f43793d1816b1dd0a78ad574e349d8f6e005e6e32e8ab528e4244 \
- --kubeconfig=<成员集群的 kubeconfig>
复制代码 Pull 模式特点:
- 成员集群主动连接控制平面
- 需要在成员集群部署 karmada-agent
- 适合网络隔离场景
3.3 查看集群
- # 列出所有集群
- kubectl get clusters
- # 查看集群详细信息
- kubectl get cluster <cluster-name> -o yaml
- # 查看集群状态
- kubectl describe cluster <cluster-name>
复制代码 3.4 删除集群
- # 删除集群(会触发优雅删除流程)
- kubectl delete cluster <cluster-name>
复制代码 删除流程:
- 从所有 ResourceBinding 和 ClusterResourceBinding 中移除该集群
- 删除该集群相关的所有 Work 对象
- 删除 ExecutionSpace 命名空间
- 删除 Cluster 对象
3.5 手动创建 Cluster(YAML)
也可以直接通过 YAML 创建:- apiVersion: cluster.karmada.io/v1alpha1
- kind: Cluster
- metadata:
- name: member1
- spec:
- syncMode: Push
- apiEndpoint: https://member1-api.example.com:6443
- secretRef:
- namespace: karmada-cluster
- name: member1
- provider: aliyun
- region: cn-hangzhou
- zones:
- - cn-hangzhou-a
- - cn-hangzhou-b
- taints: [] # 可以添加污点来阻止调度
复制代码 四、源码原理
4.1 Cluster Controller 架构
Cluster Controller 负责管理 Cluster 资源的生命周期,代码位于 pkg/controllers/cluster/cluster_controller.go。
核心结构
- type Controller struct {
- client.Client // Kubernetes 客户端
- EventRecorder record.EventRecorder
-
- // 健康检查配置
- ClusterMonitorPeriod time.Duration // 监控周期(默认 5s)
- ClusterMonitorGracePeriod time.Duration // 优雅期(默认 40s)
- ClusterStartupGracePeriod time.Duration // 启动优雅期(默认 1min)
- CleanupCheckInterval time.Duration // 清理检查间隔(10s)
-
- // 集群健康状态缓存
- clusterHealthMap *clusterHealthMap
-
- RateLimiterOptions ratelimiterflag.Options
- }
复制代码 4.2 核心工作流程
4.2.1 Reconcile 主流程
- func (c *Controller) Reconcile(ctx context.Context, req controllerruntime.Request) (controllerruntime.Result, error) {
- // 1. 获取 Cluster 对象
- cluster := &clusterv1alpha1.Cluster{}
- if err := c.Client.Get(ctx, req.NamespacedName, cluster); err != nil {
- if apierrors.IsNotFound(err) {
- return controllerruntime.Result{}, nil
- }
- return controllerruntime.Result{}, err
- }
-
- // 2. 判断是否为删除操作
- if !cluster.DeletionTimestamp.IsZero() {
- return c.removeCluster(ctx, cluster)
- }
-
- // 3. 同步集群(创建/更新)
- return c.syncCluster(ctx, cluster)
- }
复制代码 4.2.2 syncCluster - 集群同步
- func (c *Controller) syncCluster(ctx context.Context, cluster *clusterv1alpha1.Cluster) (controllerruntime.Result, error) {
- // Step 1: 创建 ExecutionSpace(执行空间)
- // ExecutionSpace 是一个命名空间,格式为: karmada-es-{cluster-name}
- // 用于存放该集群相关的 Work 对象
- err := c.createExecutionSpace(ctx, cluster)
- if err != nil {
- c.EventRecorder.Event(cluster, corev1.EventTypeWarning,
- events.EventReasonCreateExecutionSpaceFailed, err.Error())
- return controllerruntime.Result{}, err
- }
-
- // Step 2: 根据集群条件添加污点
- // - Ready=False: 添加 NotReady taint
- // - Ready=Unknown: 添加 Unreachable taint
- // - Ready=True: 移除所有调度相关的污点
- err = c.taintClusterByCondition(ctx, cluster)
- if err != nil {
- return controllerruntime.Result{}, err
- }
-
- // Step 3: 确保 Finalizer 存在
- // Finalizer 用于在删除时执行清理逻辑
- return c.ensureFinalizer(ctx, cluster)
- }
复制代码 关键点:
- ExecutionSpace:每个集群都有一个独立的命名空间,用于隔离该集群的 Work 对象
- Taint 机制:根据集群健康状态自动添加污点,调度器会据此决策是否调度到该集群
4.2.3 createExecutionSpace - 创建执行空间
- func (c *Controller) createExecutionSpace(ctx context.Context, cluster *clusterv1alpha1.Cluster) error {
- // 生成执行空间名称:karmada-es-{cluster-name}
- executionSpaceName := names.GenerateExecutionSpaceName(cluster.Name)
-
- executionSpaceObj := &corev1.Namespace{}
- err := c.Client.Get(ctx, types.NamespacedName{Name: executionSpaceName}, executionSpaceObj)
- if err != nil {
- if !apierrors.IsNotFound(err) {
- return err
- }
-
- // 创建新的执行空间
- executionSpace := &corev1.Namespace{
- ObjectMeta: metav1.ObjectMeta{
- Name: executionSpaceName,
- Labels: map[string]string{
- util.KarmadaSystemLabel: util.KarmadaSystemLabelValue,
- },
- },
- }
- err = c.Client.Create(ctx, executionSpace)
- if err != nil {
- return err
- }
- klog.V(2).InfoS("Created execution space", "cluster", cluster.Name, "namespace", executionSpaceName)
- }
-
- return nil
- }
复制代码 执行空间的作用:
- 隔离不同集群的 Work 对象
- 便于管理和查询特定集群的工作负载
- 支持 RBAC 权限隔离
4.2.4 taintClusterByCondition - 污点管理
- func (c *Controller) taintClusterByCondition(ctx context.Context, cluster *clusterv1alpha1.Cluster) error {
- currentReadyCondition := meta.FindStatusCondition(cluster.Status.Conditions,
- clusterv1alpha1.ClusterConditionReady)
-
- if currentReadyCondition != nil {
- switch currentReadyCondition.Status {
- case metav1.ConditionFalse:
- // 集群不健康,添加 NotReady 污点
- err = c.updateClusterTaints(ctx,
- []*corev1.Taint{NotReadyTaintTemplateForSched},
- []*corev1.Taint{UnreachableTaintTemplateForSched},
- cluster)
- case metav1.ConditionUnknown:
- // 集群不可达,添加 Unreachable 污点
- err = c.updateClusterTaints(ctx,
- []*corev1.Taint{UnreachableTaintTemplateForSched},
- []*corev1.Taint{NotReadyTaintTemplateForSched},
- cluster)
- case metav1.ConditionTrue:
- // 集群健康,移除所有调度污点
- err = c.updateClusterTaints(ctx,
- nil,
- []*corev1.Taint{NotReadyTaintTemplateForSched, UnreachableTaintTemplateForSched},
- cluster)
- }
- }
- return err
- }
复制代码 污点定义:- var (
- // UnreachableTaintTemplateForSched: 集群不可达时的污点
- UnreachableTaintTemplateForSched = &corev1.Taint{
- Key: clusterv1alpha1.TaintClusterUnreachable, // "cluster.karmada.io/unreachable"
- Effect: corev1.TaintEffectNoSchedule,
- }
-
- // NotReadyTaintTemplateForSched: 集群不健康时的污点
- NotReadyTaintTemplateForSched = &corev1.Taint{
- Key: clusterv1alpha1.TaintClusterNotReady, // "cluster.karmada.io/not-ready"
- Effect: corev1.TaintEffectNoSchedule,
- }
- )
复制代码 4.2.5 removeCluster - 集群删除
- func (c *Controller) removeCluster(ctx context.Context, cluster *clusterv1alpha1.Cluster) (controllerruntime.Result, error) {
- // Step 1: 删除 ExecutionSpace
- if err := c.removeExecutionSpace(ctx, cluster); err != nil {
- klog.ErrorS(err, "Failed to remove execution space", "cluster", cluster.Name)
- return controllerruntime.Result{}, err
- }
-
- // Step 2: 检查 ExecutionSpace 是否已删除
- if exist, err := c.ExecutionSpaceExistForCluster(ctx, cluster.Name); err != nil {
- return controllerruntime.Result{}, err
- } else if exist {
- // 如果还存在,等待下次重试
- return controllerruntime.Result{RequeueAfter: c.CleanupCheckInterval}, nil
- }
-
- // Step 3: 从健康状态缓存中删除
- c.clusterHealthMap.delete(cluster.Name)
-
- // Step 4: 检查集群是否已从所有 Binding 中移除
- if done, err := c.isTargetClusterRemoved(ctx, cluster); err != nil {
- return controllerruntime.Result{}, err
- } else if !done {
- // 如果还有 Binding 引用该集群,等待下次重试
- return controllerruntime.Result{RequeueAfter: c.CleanupCheckInterval}, nil
- }
-
- // Step 5: 移除 Finalizer,允许 Cluster 对象被删除
- return c.removeFinalizer(ctx, cluster)
- }
复制代码 删除流程说明:
- 检查依赖:确保没有 ResourceBinding/ClusterResourceBinding 引用该集群
- 清理资源:删除 ExecutionSpace 及其中的所有 Work 对象
- 移除 Finalizer:允许 Kubernetes 真正删除 Cluster 对象
4.2.6 monitorClusterHealth - 健康监控
Cluster Controller 启动一个后台 goroutine 定期监控集群健康状态:
[code]func (c *Controller) Start(ctx context.Context) error { klog.InfoS("Starting cluster health monitor") // 启动周期性健康检查 go wait.UntilWithContext(ctx, func(ctx context.Context) { if err := c.monitorClusterHealth(ctx); err != nil { klog.ErrorS(err, "Error monitoring cluster health") } }, c.ClusterMonitorPeriod) |