---
# System prepended metadata

title: 多集群 ZooKeeper Operator 设计

---

# 多集群 ZooKeeper Operator 设计

## MultiClusterZooKeeper

我们新增了一个 CRD MultiClusterZooKeeper，用于在多集群控制面维护 ZooKeeper 集群，它的作用如下：

1. 承担 ZooKeeper 实例配置接口
2. 协调跨集群部署的 ZooKeeper 实例的状态
3. 部署与更新跨集群部署的 ZooKeeper 实例

该 CRD 的 reconciliation 逻辑如下：

> 注，简称对应关系为：
> `MCZK`: MultiClusterZooKeeper
> `ZK`: ZooKeeper
> `PP`: PropagationPolicy
> `OP`: OverridePolicy
> `RB`: ResourceBinding
> `SvcEx`: ServiceExport
> `ES`: EndpointSlice
> `RIWC`: ResourceInterpreterWebhookConfiguration

![image-20240110123043074](https://hackmd.io/_uploads/BJCuMoi_6.png)

1. 获取 MCZK 实例

2. 生成 ZK 实例、ZK SvcEx 以及对应的 PP，在多集群控制面 apply

3. 在 MCZK 的 RIWC responseWithExploreReviseReplica 实现中，将 .spec.replicas 都设置为 0，即让所有集群都处于已有 ZK CR，但副本均为 0 的状态

4. 从 RB 中获取调度结果

5. 根据调度结果构建部署记录（record），record 的结构体大致如下：

   ```go
   // MultiClusterZooKeeperStatus defines the observed state of MultiClusterZooKeeper
   type MultiClusterZooKeeperStatus struct {
   	// ObservedGeneration is the most recent generation observed for this cluster.
   	// It corresponds to the metadata generation, which is updated on mutation by the API Server.
   	// +optional
   	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
   
   	// Conditions represent observations of the MultiClusterZooKeeper's state
   	Conditions []metav1.Condition `json:"conditions,omitempty"`
   
     // Records represent the deployment history of MultiClusterZooKeeper
   	Records []Record `json:"records,omitempty"`
   }
   
   type Record struct {
     // ObservedGenerations 用于记录 multiclusterzookeeper 的代数，以及控制面中的 zookeeper 的代数
     ObservedGenerations map[string]int64  `json:"observedGenerations"`
     // Order 用于记录集群的部署顺序，item 是集群名称
     Order               []string          `json:"order"`
     // Status 用于表示当前 Record 的状态，部署中-“Deploy”、就绪-“Ready”
     Status              string            `json:"status"`
     // CurrentState 用于记录当前正在部署的集群的状态，key 表示状态，如 “deploying”、“completed”、“pending”，value 表示属于当前状态的集群名称的集合
     CurrentState             map[string][]string `json:"currentState,omitempty"`
     // 记录 Record 更新的时间
     LastTransitionTime  metav1.Time       `json:"lastTransitionTime"`
   }
   ```

6. 按照部署规则完成子集群中 ZK 实例的部署：
   1. 生成全副本的 FQDN，该部分需要同步给所有副本
   2. 生成 OP 修改对应子群众 ZK 实例的 .spec.replicas 以及配置（全副本 FQDN 信息）
7. 更新状态

## Record 部分的处理逻辑

规则如下：

1. 判断当前是否处于可部署状态

   1. 判断 `.status.records` 是否为空，为空表示目前没有任何部署记录，处于可以开始新部署的状态
   2. 如果 `.status.records` 不为空，则检查最后一个 item，该 item 表示最近一次部署的记录。对该 item 进行以下检查：

   - 检查 `item.observedGenerations["zookeeper"]` 的值是否【小于】控制面中的 ZK 实例的 generation，【小于】表示控制面中的 ZK 实例发生了变更
   - 检查 `item.status` 是否处于就绪状态，即 `Ready`

   以上两个条件均达成则表示当前处于可以开始新部署的状态

2. 当开始一次新的部署时：

   1. 如果 `.status.records` 为空：
      1. 则生成新的 record，填写 order（当前暂时使用 karmada 调度结果顺序部署）
      2. 将 `record.status` 设置为 `Deploying`

   2. 如果 `.status.records` 不为空：
      1. 取出最后一个 record
   3. 现在我们持有一个用于新部署的 deployRecord，对该 deployRecord 进行处理：
      1. 如果 deployRecord 的 completed 集合的数量与 order 中的集群数量一致，则表示所有集群已经部署完毕，可以跳过本次部署
      2. 如果 deployRecord 的 deploying 集合的数量为 0，而同时 pending 集合的数量大于 0，则将 pending 集合中的第一个集群移到 deploying 集合中，然后开始部署该集群
      3. 如果 deployRecord 的 deploying 集合的数量大于 0，则对 deploying 集合中的集群进行检查，判断它是否就绪，如果就绪则将该集群从 deploying 集合中移到 completed 集合

3. 重复上述步骤直至完成部署，更新 `record.status = "Ready"`