Kubernetes 靠什么活下来?拆解 K8s 集群可靠性设计的 5 个核心机制 建议收藏 | 遇到集群可靠性设计问题看这一篇就够了前阵子跟一个做基础设施的哥们聊天他说了一句话让我印象特别深“我们产品没有资源搞 K8s但我们自己写的集群服务三天两头出问题想参考一下 K8s 的架构看看它到底是怎么保证可靠的。”这个问题我太有感触了。很多人以为 K8s 的可靠性就是“多搞几个副本”——节点挂了自动重启 Pod 就完事了。但真拆开看K8s 从控制平面到数据平面从组件通信到状态存储每一层都有自己的一套生存逻辑。今天我就从 SRE 的视角把 K8s 集群保障可靠性的核心机制掰开了讲。下文会逐一展开这里先给你一个全景速查。 速查急救包序号核心机制一句话说明1声明式 API 控制器调谐控制器通过 Informer 监听资源变化持续对比期望状态 vs 实际状态自动修复偏差这是所有自愈能力的根基2工作负载自愈ReplicaSet/Deployment自动替换失败容器、节点宕机后重新调度工作负载3领导者选举LeaseController Manager 和 Scheduler 通过 Lease 分布式锁选主故障后自动切换经验值约 15~25 秒4控制平面多副本API Server、Controller Manager、Scheduler 各部署 ≥3 个实例消除单点故障5etcd 奇数节点集群3 节点可容忍 1 台故障5 节点可容忍 2 台生产环境最低推荐版本为 3.4.29 和 3.5.11 核心实体谁在背后撑着整个集群要理解 K8s 怎么保证可靠性先得搞清楚谁在干活。K8s 集群分两大部分控制平面Control Plane和工作节点Worker Node。控制平面是集群的“大脑”包含四个核心组件kube-apiserver集群的唯一入口所有操作都经过它etcd分布式键值存储保存集群所有状态数据kube-controller-manager运行各种控制器Deployment、ReplicaSet 等kube-scheduler负责把 Pod 调度到合适的节点工作节点上运行的是kubelet和kube-proxy负责执行控制平面下发的指令。K8s 保障可靠性的核心思路就一句话每个可能挂掉的组件都给它配个“备胎”并且让它们能自动感知故障、自动切换。 机制一声明式 API 控制器模式——这是所有自愈能力的根基先讲这个因为它是 K8s 最核心的设计思想也是很多人理解不到位的地方。声明式 API的意思是你告诉 K8s “我想要什么状态”而不是“你该怎么做”。比如你写一个 DeploymentapiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 # 我想要 3 个 Pod 一直跑着 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.21你用kubectl apply提交这个 YAML 后K8s 不会立刻去创建 3 个 Pod 然后就不管了。实际的工作流程是这样的Deployment 控制器会创建一个ReplicaSet然后ReplicaSet 控制器负责维持指定数量的 Pod 副本。这是一个两层控制器的嵌套关系——Deployment 管 ReplicaSetReplicaSet 管 Pod。每个控制器内部都运行着一个调谐循环Reconcile Loop不断做三件事读取“期望状态”Deployment 里写的 replicas: 3读取“实际状态”当前有多少个 Pod 在跑如果有偏差执行操作让实际状态向期望状态靠拢这个循环本质上是一个持续运行的无限循环但不是盲目地高频轮询。控制器通过Informer 机制监听 API Server 的资源变化事件Watch收到事件后触发调谐。同时Informer 也会定期将本地缓存的对象重新入队requeue触发调谐作为防止事件丢失的安全网——需要注意的是resync 并不会从 API Server 重新拉取全量数据只是将本地缓存中的对象重新加入工作队列。不同控制器的 resync period 配置差异很大。client-go 的sharedInformerFactory默认不启用 resyncdefaultResync 0controller-runtime 的 Manager 层面默认SyncPeriod为 10 小时且各控制器之间会有 10% 的抖动以避免同时发起请求kube-controller-manager 的--min-resync-period默认值为 12 小时。这就是 K8s 自愈能力的本质Pod 挂了 → 控制器发现实际 期望 → 自动创建新 Pod。节点宕了 → 控制器发现 Pod 不见了 → 在其他健康节点重新调度。⚠️一个致命误区很多人以为 K8s 的“自愈”是 K8s 本身会“监控”Pod 然后“重启”它。不对。是控制器模式在背后持续调谐——你只需要声明“我要 3 个副本”控制器会一直确保这个声明成立。区别在于前者是“被动响应”后者是“主动闭环”。 机制二工作负载自愈——从 Pod 到节点的全链路保护基于控制器模式K8s 实现了多层级自愈Pod 级别ReplicaSet保证指定数量的 Pod 副本一直在运行容器崩溃OOMKilled、健康检查失败→ 自动重启配置livenessProbe和readinessProbe防止流量进入异常 Pod节点级别节点宕机 → kube-controller-manager 检测到 Node 失联 → 将 Node 上的 Pod 标记为Terminating→ 在其他健康节点重建如果 Pod 挂载了 PersistentVolumeK8s 会将卷重新挂载到新节点调度级别Kubernetes 调度器在调度 Pod 时会综合考虑节点资源、污点容忍、亲和性等因素确保 Pod 被调度到符合条件的节点上给自建集群的启发别只做“故障告警”要做“故障自愈”。K8s 的控制器模式告诉我们与其等人收到告警再去修复不如设计一个持续巡检 自动修复的闭环系统。哪怕一开始只做一个“挂了自动重启”的 watchdog也比纯人工响应强。 机制三领导者选举——多个备胎只有一个干活kube-controller-manager和kube-scheduler这两个组件在高可用集群中会部署多个实例。但同一时间只有一个实例在干活Leader其他都是 Standby。怎么选出来的K8s 使用Lease API作为轻量级分布式锁所有实例竞争同一个 Lease 对象谁先成功更新 Lease谁就成为 LeaderLeader 定期续约renewLeader 挂了不续约Lease 过期后其他实例重新竞争LeaseSpec 的关键字段包括holderIdentity当前 Leader 的身份标识如 Pod 名称或基于主机名的字符串acquireTime获得领导权时的时间戳renewTime当前持有者最近一次续约的时间戳leaseDurationSeconds租约的有效期候选实例在尝试获取过期租约之前应等待此时间再加上一小段宽限时间leaseTransitions领导权发生变更的次数当 Lease 不存在或已过期候选者观察到当前时间 renewTime leaseDurationSeconds时候选实例尝试使用自己的身份更新此 Lease。由于多个候选者可能同时观察到 Lease 过期Kubernetes 依赖resourceVersion 乐观并发控制来保证只有一个更新成功。切换时间由三个参数共同决定--leader-elect-lease-duration默认15s租约有效期--leader-elect-renew-deadline默认10sLeader 续约截止时间--leader-elect-retry-period默认2s候选者重试间隔Leader 停止续约后其他候选者会在retryPeriod默认 2s周期内不断尝试获取 Lease。当renewTime leaseDurationSeconds过期后候选者即可成功获取。因此实际切换时间通常在15~25 秒左右——主要取决于leaseDuration的 15s额外的 0~10 秒来自候选者检测到 Leader 停止续约的时间差和重试间隔。另外Kubernetes v1.33 将协调领导者选举Coordinated Leader Election标记为 Beta 特性但默认仍为禁用。该特性允许控制平面组件通过协调领导者选举确定性地选择一个领导者。如需启用需要通过--feature-gatesCoordinatedLeaderElectiontrue开启特性门控同时通过--runtime-configcoordination.k8s.io/v1beta1true启用coordination.k8s.io/v1beta1API 组。对于 Kubernetes 1.36当特性门控和 API 组被启用时kube-controller-manager和kube-scheduler会自动使用协调领导者选举。当前大多数生产集群运行在 v1.28–v1.32 之间仍使用传统 Lease 选举机制。给自建集群的启发如果你需要部署多实例的服务用分布式锁实现“一主多备”比“多活”简单可靠得多。K8s 用的是 Lease 乐观锁resourceVersion 冲突检测你可以用 Redis 分布式锁或者 etcd 自身的 Lease 机制来实现类似效果。 机制四控制平面多副本——消除单点故障先讲一个我踩过的坑。最早搭实验集群的时候图省事搞了个1 Master 3 Worker的结构。跑 demo 没问题但一上生产就出事了——Master 节点重启整个集群的控制面直接瘫痪调度、扩缩容、发布全部中断。根本原因就四个字单点故障SPOF。K8s 官方推荐的高可用方案是控制平面节点 ≥3 个。这里有个细节需要注意。官方文档的措辞是“控制平面节点为奇数有利于机器故障或者分区故障时重新选举”用的是can help有助于而不是must必须。为什么因为在堆叠 etcd 拓扑中etcd 运行在控制平面节点上而 etcd 需要奇数个成员所以控制平面节点也建议为奇数。如果使用外部 etcd 拓扑控制平面节点和 etcd 节点可以各自独立配置奇数数量。多 Master 部署时客户端kubectl、worker 节点的 kubelet需要通过负载均衡器访问 API Server。常用的方案是 HAProxy Keepalived 提供 VIP或者直接用云厂商的 LB。️ 机制五etcd 高可用——状态存储不能丢为什么把 etcd 放在控制平面多副本后面讲因为理解了控制平面需要多节点之后你才会明白 etcd 为什么必须是奇数——etcd 就运行在控制平面节点上堆叠拓扑控制平面节点的数量决定了 etcd 的容错能力。etcd 是 K8s 的“命根子”——所有集群状态都在这里面。etcd 挂了整个集群就废了。官方文档明确生产环境中运行的 etcd 最低推荐版本为 3.4.29 和 3.5.11。此外如果计划升级到 etcd v3.6官方建议先升级到 v3.5.26 或更高版本——etcd v3.5.26 已引入自动同步机制将 v2store 中的成员信息同步到 v3store确保受影响的集群在升级到 3.6.x 之前得到修复避免出现“僵尸成员zombie members”问题。etcd 基于 Raft 共识协议需要“多数派”Quorum才能工作。所谓多数派是指超过半数50%的节点达成一致而不是“≥半数”1 节点挂 1 个就 0%无法工作仅限测试2 节点挂 1 个剩 1 个1 不大于 1无法达成共识 —— 所以 2 节点不能容错3 节点可容忍 1 个节点故障剩 2 个2 1.55 节点可容忍 2 个节点故障剩 3 个3 2.5etcd 集群成员个数应为奇数。官方建议生产环境使用五个成员的集群。K8s 官方提供两种 etcd 高可用拓扑1. 堆叠Stackedetcdetcd 和控制平面跑在同一批节点上基础设施少成本低这是 kubeadm 的默认拓扑每个控制平面节点创建一个本地 etcd 成员这个 etcd 成员只与该节点的 kube-apiserver 通信——API Server 通过--etcd-servers参数配置的是本地 etcd 地址etcd 集群内部通过 Raft 协议在成员之间同步数据风险一个节点挂掉etcd 成员和控制平面实例同时丢失2. 外部Externaletcdetcd 集群独立部署与控制平面节点分离可靠性更高可以独立扩缩容和维护每个 etcd 主机与每个控制平面节点的 kube-apiserver 通信需要两倍于堆叠拓扑的主机数量生产环境建议etcd 至少 3 节点并且分布在不同可用区AZ这样能容忍单个可用区故障。检查 etcd 集群健康状态ETCDCTL_API3 etcdctl --endpointsENDPOINTS endpoint health给自建集群的启发状态存储和业务逻辑要分离。etcd 独立部署这个思路在你自己的集群服务里同样适用——别把元数据存储和业务节点绑在一起。 一张表看懂 K8s 可靠性机制层级组件/机制故障场景应对措施恢复时间经验值接入层HAProxy/云 LB VIP单 LB 故障多实例 健康检查秒级API 层kube-apiserver≥3 副本单 API Server 宕机负载均衡转发到其他实例秒级调度层kube-schedulerLeader 选举Leader 宕机Lease 过期后其他实例竞选~15-25 秒控制层kube-controller-managerLeader 选举Leader 宕机Lease 过期后其他实例竞选~15-25 秒存储层etcd3/5 节点 Raft单 etcd 节点宕机Raft 自动切换 Leader数秒内工作负载Deployment/ReplicaSetPod 崩溃/节点宕机控制器调谐重建 Pod取决于调度速度 恢复时间为经验估算值实际受集群规模、网络状况、API Server 负载等因素影响并非官方承诺值。❓ 常见问题Q12 个控制平面节点行不行在堆叠拓扑中不行。etcd 基于 Raft 协议需要多数派Quorum2 节点挂 1 个就只剩 50%1 不大于 1无法达成共识。最少 3 个且建议为奇数。Q2Controller Manager 和 Scheduler 的多个实例是“多活”吗不是。它们是“一主多备”——通过领导者选举同一时间只有一个 Leader 在执行实际工作其他 Standby 随时待命。Q3协调领导者选举Coordinated Leader Election和传统领导者选举有什么区别协调领导者选举是 Kubernetes v1.33 标记为 Beta 的特性默认禁用允许控制平面组件通过 LeaseCandidate API 确定性地选择领导者。开启该特性需要同时通过--feature-gates启用特性门控并通过--runtime-config启用coordination.k8s.io/v1beta1API 组。当前大多数生产集群仍使用传统 Lease 选举机制。Q4K8s 能保证 100% 可靠吗不能。K8s 保障的是“在组件故障时尽快自动恢复”而不是“永远不会故障”。控制平面 HA 需要至少 3 台机器etcd 需要 SSD 和合理的性能规划。可靠性是有成本的。Q5我把这些机制抄到自己的集群服务里需要多大的改动量看你的现状。如果你现在是一个单 Master 架构改造成本最高——需要引入分布式锁Lease 机制、状态存储高可用etcd/ZooKeeper 多节点、接入层负载均衡。如果你已经是多实例部署但缺少故障切换逻辑加上领导者选举是最快见效的一步。⚠️ 彩蛋一个会让你翻车的隐藏细节在 Kubernetes v1.28 中kube-controller-manager和kube-scheduler的--leader-elect参数默认是true。但很多人不知道的是如果你部署了多个控制平面节点却没有正确配置网络插件和节点间网络互通Controller Manager 可能在选主成功后因为网络问题无法正常工作导致集群“半死不活”——API Server 能访问但 Pod 创建不出来。我见过不止一个团队在这个坑里翻车。检查清单确认 CNI 插件Calico/Flannel/Weave在所有节点上正常运行确认节点间网络互通kubectl get nodes能看到所有节点状态为Ready确认--cluster-cidr与 CNI 插件的配置相匹配确认每个控制平面节点的 etcd 集群通信正常2379/2380 端口互通不要只盯着--pod-cidr这一个参数——实际故障通常是 CNI 未部署、节点间网络不通、--cluster-cidr与 CNI 配置不匹配等多个因素共同导致的。 总结K8s 保障可靠性的核心就 5 件事声明式 API 控制器调谐通过 Informer 监听资源变化持续调谐自动修复偏差这是所有自愈能力的根基工作负载自愈从 Pod 到节点全链路自动恢复领导者选举Controller Manager 和 Scheduler 用 Lease 选主故障约 15~25 秒切换控制平面多副本≥3 个 Master消除单点故障etcd 奇数节点3 节点起步生产环境最低版本 3.4.29/3.5.11升级到 v3.6 前需先升级到 v3.5.26Raft 协议保证一致性给自建集群的三条建议别在单点上省钱控制节点至少 3 个这是分布式系统的最低门槛设计“调谐”而非“告警”与其等人收到告警去修不如让系统自己修状态存储独立出来别把元数据和业务节点绑在一起独立 etcd/ZooKeeper 集群是正经做法你现在的集群服务是怎么做高可用的有没有踩过什么坑欢迎在评论区聊聊我也想知道大家的实践方案。觉得有用的话转发给团队里负责基础设施的兄弟省得他再去翻几百页官方文档了。