Operator工作原理
发表于:2025-11-25 | 分类: 学习

Operator 的工作原理,利用了 Kubernetes 的自定义 API 资源(CRD),来描述我们想要部署的“有状态应用”;然后在自定义控制器里,根据自定义 API 对象的变化,来完成具体的部署和运维工作。

Etcd Operator

使用 Helm Chart 部署 etcd 集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 添加 Bitnami Helm 仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# 2. 创建命名空间
kubectl create namespace etcd-system

# 3. 部署 etcd 集群
helm install etcd-cluster bitnami/etcd \
--namespace etcd-system \
--set replicaCount=3 \
--set auth.rbac.create=true \
--set persistence.enabled=true \
--set persistence.size=8Gi

部署完成后实际上是在 Kubernetes 里添加了一个名叫 EtcdCluster 的自定义资源类型。而 Etcd Operator 本身,就是这个自定义资源类型对应的自定义控制器。

Etcd Operator 部署 Etcd 集群,采用的是静态集群(Static)的方式

静态集群的好处是,它不必依赖于一个额外的服务发现机制来组建集群,非常适合本地容器化部署。而它的难点,则在于你必须在部署的时候,就规划好这个集群的拓扑结构,并且能够知道这些节点固定的 IP 地址。

Etcd Operator集群具体的组建过程是逐个节点动态添加的方式,即:

首先,Etcd Operator 会创建一个“种子节点”;
然后,Etcd Operator 会不断创建新的 Etcd 节点,然后将它们逐一加入到这个集群当中,直到集群的节点数等于 size。

这就意味着,在生成不同角色的 Etcd Pod 时,Operator 需要能够区分种子节点与普通节点。

而这两种节点的不同之处,就在于一个名叫–initial-cluster-state 的启动参数:

  • 当这个参数值设为 new 时,就代表了该节点是种子节点。种子节点还必须通过–initial-cluster-token 声明一个独一无二的 Token。
  • 而如果这个参数值设为 existing,那就是说明这个节点是一个普通节点,Etcd Operator 需要把它加入到已有集群里。

Etcd Operator 启动要做的第一件事( c.initResource),是创建 EtcdCluster 对象所需要的 CRD

接下来,Etcd Operator 会定义一个 EtcdCluster 对象的 Informer

Etcd Operator 并没有用工作队列来协调 Informer 和控制循环。

在控制循环里执行的业务逻辑,往往是比较耗时间的。比如,创建一个真实的 Etcd 集群。而 Informer 的 WATCH 机制对 API 对象变化的响应,则非常迅速。所以,控制器里的业务逻辑就很可能会拖慢 Informer 的执行周期,甚至可能 Block 它。而要协调这样两个快、慢任务的一个典型解决方法,就是引入一个工作队列。

Etcd Operator 在业务逻辑的流程图如下:

Etcd Operator 的特殊之处在于,它为每一个 EtcdCluster 对象,都启动了一个控制循环,“并发”地响应这些对象的变化。这种做法不仅可以简化 Etcd Operator 的代码实现,还有助于提高它的响应速度。

当YAML文件第一次被提交到 Kubernetes 之后,Etcd Operator 的 Informer,就会立刻“感知”到一个新的 EtcdCluster 对象被创建了出来。所以,EventHandler 里的“添加”事件会被触发。

而这个 Handler 要做的操作也很简单,即:在 Etcd Operator 内部创建一个对应的 Cluster 对象(cluster.New),比如流程图里的 Cluster1。

这个 Cluster 对象,就是一个 Etcd 集群在 Operator 内部的描述,所以它与真实的 Etcd 集群的生命周期是一致的。

一个 Cluster 对象需要具体负责的,其实有两个工作。

**第一个工作只在该 Cluster 对象第一次被创建的时候才会执行。这个工作,就是 Bootstrap,即:创建一个单节点的种子集群。**由于种子集群只有一个节点,所以这一步直接就会生成一个 Etcd 的 Pod 对象。这个 Pod 里有一个 InitContainer,负责检查 Pod 的 DNS 记录是否正常。如果检查通过,用户容器也就是 Etcd 容器就会启动起来。这个 Etcd 容器最重要的部分,是它的启动命令。启动中,Etcd Operator 只会使用 Pod 的 DNS 记录,而不是它的 IP 地址。因为在 Operator 生成上述启动命令的时候,Etcd 的 Pod 还没有被创建出来,它的 IP 地址自然也无从谈起。

这也就意味着,每个 Cluster 对象,都会事先创建一个与该 EtcdCluster 同名的 Headless Service。这样,Etcd Operator 在接下来的所有创建 Pod 的步骤里,就都可以使用 Pod 的 DNS 记录来代替它的 IP 地址了。

Cluster 对象的第二个工作,则是启动该集群所对应的控制循环。

这个控制循环每隔一定时间,就会执行一次下面的 Diff 流程。

首先,控制循环要获取到所有正在运行的、属于这个 Cluster 的 Pod 数量,也就是该 Etcd 集群的“实际状态”。

而这个 Etcd 集群的“期望状态”,正是用户在 EtcdCluster 对象里定义的 size。

所以接下来,控制循环会对比这两个状态的差异。

如果实际的 Pod 数量不够,那么控制循环就会执行一个添加成员节点的操作(即:上述流程图中的 addOneMember 方法);反之,就执行删除成员节点的操作(即:上述流程图中的 removeOneMember 方法)。

以 addOneMember 方法为例,它执行的流程如下所示:

  1. 生成一个新节点的 Pod 的名字,比如:example-etcd-cluster-v6v6s6stxd;
  2. 调用 Etcd Client,执行 etcdctl member add example-etcd-cluster-v6v6s6stxd 命令;
  3. 使用这个 Pod 名字,和已经存在的所有节点列表,组合成一个新的 initial-cluster 字段的值;
  4. 使用这个 initial-cluster 的值,生成这个 Pod 里 Etcd 容器的启动命令。

当这个容器启动之后,一个新的 Etcd 成员节点就被加入到了集群当中。控制循环会重复这个过程,直到正在运行的 Pod 数量与 EtcdCluster 指定的 size 一致。

有了这样一个与 EtcdCluster 对象一一对应的控制循环之后,后续对这个 EtcdCluster 的任何修改,比如:修改 size 或者 Etcd 的 version,它们对应的更新事件都会由这个 Cluster 对象的控制循环进行处理。

上一篇:
PV、PVC、StorageClass
下一篇:
RBAC