可不可以认为k8s系统中存在着非常多的单点?这些单点会造成客户的服务中断么?
这是个很好的问题,答案是”部分对,但不完全对”——确实有很多单点,但单点存在跟”会不会导致客户服务中断”是两件需要分开看的事,这也是 Kubernetes 架构设计里一个挺关键、也容易被误解的地方。
先看你的观察本身:在你现在这个 kind 单节点集群里,确实每一个控制面组件都是字面意义上的单点——只有一个 apiserver 进程、一个 etcd 进程、一个 scheduler 进程、一个 controller-manager 进程,每个节点上的 kubelet、kube-proxy 也天然就是”这个节点独一份”,没有任何冗余。这在生产环境里当然是不能接受的,所以生产集群会针对每一类组件分别做高可用,做法还都不一样,值得挨个说一下。
kube-apiserver 是最容易做到无状态水平扩展的,因为它自己不持有任何需要跨请求记住的状态(真正的状态全在 etcd 里),所以生产环境通常会跑好几个 apiserver 实例(比如 3 个),前面挂一个负载均衡器,客户端(kubectl、kubelet、scheduler 等等)连的是这个负载均衡器的地址,不是某一个具体 apiserver 实例。这几个 apiserver 实例互相之间完全不需要协调、不需要知道彼此存在,各自独立读写同一个 etcd 集群,靠 etcd 自身的强一致性保证大家看到的数据一致。所以只要负载均衡器还能把流量导向存活的实例,挂掉一个 apiserver 完全无感,这一层做到了真正意义上的无单点。
etcd 我们前面聊得很细了,只要按 3 或 5 节点部署,靠 Raft 的多数派机制,挂掉少数节点(3 节点挂 1 个、5 节点挂 2 个以内)整个集群完全不受影响,也不是单点,前提是你得真的部署成多节点集群,而不是像你现在这样只跑一个。
kube-scheduler 和 kube-controller-manager 走的是另一种 HA 模式——不是像 apiserver 那样”谁都能处理请求、随便分流”,而是”主备”模式:可以同时跑多个副本,但同一时刻只有一个在真正干活(做调度决策/跑控制器逻辑),其余的都是待命状态。这几个副本之间靠抢占一个叫 Lease 的对象来决定谁是当前的”leader”(这个 Lease 机制正好就是我们前面聊 etcd 内部模块时提到的租约功能,Kubernetes 拿它实现了应用层的 leader election)——抢到 Lease 的那个副本才会真正开始调度 Pod 或者跑控制器逻辑,其他副本只是持续尝试抢占,一旦当前 leader 挂了、不再续租,Lease 到期后马上会有另一个副本抢到并接管,切换通常是秒级的。所以只要部署了多副本,这两个组件也不是真正意义上的单点,只是活跃实例是唯一的,备用实例随时待命。
节点侧的 kubelet、kube-proxy、容器运行时,这几个天生就是”每个节点一份”,没办法像 apiserver 那样做冗余——你不可能给一个节点配两个 kubelet 互相备份,这个粒度的高可用思路完全不一样:Kubernetes 不指望单个节点上的这些组件本身有冗余,而是指望”节点这个层级”有冗余,也就是集群里有足够多的节点,一个节点的 kubelet 挂了、或者整个节点宕机了,controller-manager 里的 Node 控制器会通过心跳超时检测到这个节点失联(大概是几十秒到几分钟的超时窗口),把这个节点标记为不健康,然后把原本调度在这个节点上的 Pod 标记为需要重新调度,通过 scheduler 重新分配到其他健康节点上去。所以单个节点确实是它自己那份工作的单点,但整个集群不会因为一个节点挂了就出问题,代价是这个节点上原本跑着的 Pod 会经历一段”从检测到失联,到被重新调度到别处,再到新 Pod 真正启动完成”的中断窗口,这段时间这些 Pod 提供的服务确实会受影响,除非你的应用本身有多副本分布在不同节点上,靠 Service 把流量导到其他健康副本,用户基本感知不到。
现在回到你问的核心——这些单点会不会造成客户服务中断,这里最重要的一个认知是:控制面(apiserver、scheduler、controller-manager、etcd)出问题,跟已经在跑的、正在服务用户流量的 Pod 出问题,是两个不同层面的事情,Kubernetes 的设计刻意把这两者解耦开了。哪怕你把整个控制面全部关掉(apiserver、etcd、scheduler、controller-manager 通通停掉),已经在各个节点上正常运行的容器不会因此停止——因为容器进程实际上是由每个节点本地的容器运行时(containerd)在直接管理和保活的,不依赖控制面持续在线;已经写好的 Service 转发规则(iptables/ipvs)也已经提前固化在每个节点的内核网络栈里了,不需要 kube-proxy 或者控制面持续在线才能生效,用户请求照样能正常打进来、被正确转发到后端 Pod、拿到正常响应。控制面挂掉真正影响的,是”对集群状态做出新的改变”这件事——没法调度新 Pod、没法响应 Deployment 滚动更新、没法根据 HPA 自动扩容、一个节点如果这时候真的宕机了也没法把它上面的 Pod 重新调度到别处去补救。也就是说,控制面短暂不可用的直接后果是”系统暂时失去自愈和响应变化的能力”,而不是”正在提供服务的东西立刻停摆”,这两者的影响面差得很远。
所以完整的答案是:单点确实广泛存在,尤其是在小规模或者没做高可用配置的集群里(你现在这个 kind 集群就是全员单点的极端例子);生产环境通过给不同组件分别配上适合它们各自特性的高可用方案(apiserver 无状态水平扩展、etcd 走 Raft、scheduler/controller-manager 走主备 leader election、节点层面靠”节点数量冗余+自动重调度”而不是单节点内部冗余)基本能把这些单点都消化掉;但即便某个单点真的暂时挂了,是否会造成客户能感知到的服务中断,取决于挂的是控制面还是数据面——控制面单点故障通常只是让系统”暂时僵住、没法响应新变化”,已经在正常运行、正常服务流量的部分,在很短的故障窗口内基本不受影响,只有当故障持续时间足够长、期间又恰好发生了需要控制面介入才能处理的意外(比如某个节点也跟着挂了)时,才会真正传导成用户能感知到的服务中断。
