控制平面为什么被拆成四块
一句话总结
Kubernetes 控制平面分为 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 四个进程,其中只有 apiserver 与 etcd 对话。这一句话解释了 CKA 故障排查的一半。
为什么需要它
设想 scheduler 直接连接 etcd,读取 Pod 对象并写入 nodeName,会发生什么?
- scheduler 会与 etcd 的存储格式(protobuf 编码、
/registry/pods/<ns>/<name>键结构)耦合。存储版本改变,scheduler 也必须修改。 - 授权(RBAC)、验证(validation)、admission webhook 和审计日志会被全部绕过,无法记录谁更改了什么。
- 字段默认值填充和版本转换(v1beta1 to v1)必须在每个组件中分别实现。
因此 Kubernetes 把 apiserver 设为唯一入口。apiserver 不创建状态,只负责接收、验证、授权、保存,并通知观察者。真正的判断全部由外部控制器完成。
它如何运作
控制器的 reconcile 循环如下。
- 通过 watch 读取声明状态(spec)
- 观测实际状态(status)
- 计算差异,只调用足以消除差异的 API
- 返回第 1 步
关键在于它不是执行命令,而是缩小差异。因此即使漏掉一个事件,也会在下次 resync 时收敛;控制器重启后,也会从头重新对齐。Deployment 控制器创建 ReplicaSet,ReplicaSet 控制器创建 Pod,各自只关注自己下面的一层。
scheduler 分两个阶段运行。
| 阶段 | 工作 | 结果 |
|---|---|---|
| filter (predicate) | 排除资源不足、taint 未被容忍、nodeSelector 不匹配、volume zone 不匹配的节点 | 可放置节点列表 |
| score (priority) | 为剩余节点打分(资源平衡、镜像本地性、拓扑分散) | 得分最高的一个节点 |
0/3 nodes are available: 3 Insufficient cpu 这样的消息,就是 filter 阶段淘汰原因的汇总表。读懂这句话,90% 的 Pending Pod 都能当场诊断完毕。
在实际工作中会遇到的情况
案例 1——DHCP 杀死了集群。家庭实验室控制平面的 IP 曾从 10.0.0.111 变为 10.0.0.120,原因是 DHCP 租约续期。症状为 dial tcp 10.0.0.111:6443: connect: no route to host,真正原因却在证书:apiserver 证书的 SAN 只有 IP Address:10.0.0.111,没有 .120。有趣之处是各组件反应不同。etcd 与 kube-apiserver 尝试绑定不存在的 .111,陷入 CrashLoopBackOff;kube-scheduler 和 controller-manager 绑定 127.0.0.1,所以进程仍活着,却什么也做不了。只有了解四个进程各自绑定不同地址的设计,才能读懂这幅图。
**案例 2——etcd quorum 与 API 可用性是两回事。**同一家庭实验室从 3 个节点扩展到 7 个节点时,控制平面增加到了 3 台。etcdctl member list 正确列出 3 个成员,每个节点也各自运行 apiserver、scheduler 和 controller-manager,看似已经实现 HA,其实没有。
controlPlaneEndpoint: 10.0.0.120:6443 # cp-1 의 물리 IP
该值不是 VIP 或 DNS,而是第一个节点的真实 IP。因此 cp-1 死亡后,etcd quorum 仍以 2/3 正常,cp-2、cp-3 的 apiserver 也正常运行,但 kubectl 与 7 个节点的 kubelet 会全部无法连接。而且证书 SAN 没有其他控制平面 IP,直接连接 cp-2 也会因 TLS 验证失败。数据可用性和访问可用性是不同的问题。
此外,把控制平面只增加到 2 台,比 1 台还更危险。2 个成员的多数仍是 2,任意一台死亡都会丢失 quorum。推荐奇数(1、3、5)正是这个原因,所以这次扩展也必须做到 3 台。
后续实验要做什么
第一个实验将调查集群,操作 namespace、label 和 annotation,亲自创建并切换 kubeconfig context,再用 kubectl explain 与 --dry-run=client -o yaml 生成 manifest。第二个实验会亲自编写 CustomResourceDefinition 扩展 API,目标是亲眼看到 schema 验证真正拒绝请求的瞬间。