Kubernetes 的高可用(High Availability, HA)集群设计旨在确保集群在面对节点故障、网络分区或其他意外情况时,仍能持续提供服务。本文将详细介绍 K8S 的核心组件及其高可用实现方式。
K8S核心组件

K8S API Server
K8S 控制平面的核心组件,运行在 Master 节点上,负责接收和处理所有针对集群的 REST API 调用。
- 为用户、控制平面组件以及外部系统提供统一的访问入口。
- 所有模块通过 API Server 将数据写入 etcd;当需要获取或操作数据时,也通过 API Server 提供的 REST 接口(如 GET/LIST/WATCH)进行交互。
- 用户通常通过 kubectl/kubeadm 等工具间接访问 API Server,也可以直接发送 REST 请求。
👉 接收流量对象:Scheduler、Controller、Kubelet、Kube-Proxy、kubectl/外部客户端 👉 发送流量对象:etcd
K8S Scheduler
K8S 的调度器,运行在 Master 节点上。
- 持续监听 API Server,发现处于 待调度状态 的 Pod(即 PodSpec.NodeName 为空)。
- 根据资源需求、节点负载、亲和/反亲和策略等条件筛选候选节点(预选)。
- 对候选节点进行打分排序,选出最优节点(优选),并通过 Binding 对象 将调度结果写回 API Server。
👉 发送流量对象:API Server
K8S Controller
控制器管理器,运行在 Master 节点上,负责运行多个控制循环(Controller)。
每个 Controller 负责维持某类资源的期望状态,例如:
- NodeController:检测和管理节点健康;
- ReplicaSetController:确保副本数与期望一致;
- EndpointController:维护 Service 与 Endpoint 的对应关系;
- 其他控制器:NamespaceController、ServiceAccountController 等。
通过监听 API Server 获取资源的当前状态,当发现与期望状态不符时,触发相应的修复操作。
👉 发送流量对象:API Server(所有状态监控与修复操作均经由 API Server)
etcd
K8S 的分布式键值存储,提供强一致性和高可用性。
- 保存 K8S 的 完整集群状态数据(如 Pod、Service、Secrets、ConfigMap 等)。
- 仅有 API Server 可以直接读写 etcd,其他组件必须通过 API Server 进行访问。
👉 接收流量对象:API Server
Kubelet
运行在每个节点上的节点代理。
- 接收来自 API Server 的 PodSpec,确保容器按规范运行。
- 定期上报节点与 Pod 的状态信息。
- 与容器运行时(如 Docker、containerd、CRI-O)交互,负责镜像拉取、容器创建与销毁。
- 执行存活探针(liveness)与就绪探针(readiness),并上报容器健康状态。
👉 发送流量对象:API Server(获取 PodSpec、上报状态)
Kube-Proxy
运行在每个节点上的网络代理组件。
- 维护 iptables/ipvs 规则,确保 Service 的流量能正确分发至对应的 Pod。
- 保障集群内部 Pod 与 Service 的网络通信。
- 在某些生产环境中,可能被 Ingress Controller、Nginx 或 HAProxy 等方案替代。
👉 发送流量对象:API Server(获取 Service 与 Endpoint 信息)、各节点的网络栈
K8S高可用(HA)集群
组件 | HA 类型 | 原理 / 实现方式 | 关键说明 |
---|---|---|---|
etcd | 多副本 + Raft 单活 | 部署奇数个节点,Leader 处理写操作并通过 Raft 日志复制到 Follower,确保一致性 | 支持多数节点容错,保证已提交日志不丢失,未提交日志可被覆盖;节点≥3且为奇数最优 |
API Server | 多副本 + 负载均衡 | 无状态服务,前置 LB(如 Nginx/LVS)实现请求分发 | 可多活,任何副本都可处理请求,客户端无需感知副本切换 |
Scheduler | 多副本 + Leader Election | 通过 Lease 锁对象选主,只有 Leader 执行业务循环 | Leader 出故障时备用实例自动接管;保证单活执行避免冲突 |
Controller Manager | 多副本 + Leader Election | 同 Scheduler | Leader 故障切换同 Scheduler;确保资源状态一致性 |
节点级组件(Kubelet、Kube-Proxy、实际服务 Pod) | 控制平面管理 | 本质上都是运行在节点上的pod,由 控制平面调度和管理 | 节点上独立运行,通过控制平面调度和副本管理实现整体高可用;节点故障时 Pod 会被重新调度 |
K8S 的高可用本质上是保证集群中 Pod 的高可用。由于 Pod 的高可用可以通过 Deployment 等控制器来管理多个副本实现,因此 集群高可用的核心目标在于保证 K8S 控制平面关键组件的高可用。
接下来对每一个控制组件的高可用进行分析:
etcd
etcd 通过部署多个实例形成集群来实现高可用。
- 每个 etcd 节点都维护一份状态机,集群中始终只有一个有效的领导者(Leader)。
- 所有写操作必须由 Leader 处理,并通过 Raft 协议 将操作日志同步到其他节点。
- Raft 协议确保集群中所有节点的日志保持一致,从而保证最终存储的键值对数据一致。
Raft 集群中的每个节点在任意时刻都处于以下三种状态之一:
- 跟随者(Follower):被动角色,只响应来自 Leader 或 Candidate 的请求。如果长时间未收到 Leader 消息,会转为候选者。
- 候选者(Candidate):由跟随者超时转变而来,发起选举请求以争取成为 Leader。
- 领导者(Leader):唯一主动角色,负责处理所有客户端请求并将日志复制到其他节点。集群在任意时刻只能有一个 Leader。
存在以下几种状态转换操作:
领导者选举(Leader Election)
领导者选举是Raft协议的基石,它确保了集群中总有一个领导者来协调工作。
- 任期(Term): Raft使用一个递增的整数来表示任期。每个任期都以一次选举开始。当一个节点发起选举时,它会增加自己的任期号。
- 超时(Timeout): 每个跟随者都有一个随机的选举超时时间(通常在150-300毫秒之间)。如果在超时时间内没有收到来自领导者的心跳消息,跟随者就会开始一次新的选举。
- 成为候选者: 当一个跟随者超时后,它会:
- 增加自己的任期号。
- 将自己的状态从跟随者转换为候选者。
- 给自己投一票。
- 向集群中的其他所有节点发送RequestVote(请求投票)RPC。
- 投票:
其他节点收到RequestVote请求后,会根据以下规则进行投票:
- 它会首先比较自己的任期号和候选者的任期号,如果候选者的任期号小于自己的任期号,该节点会立即拒绝投票,因为它认为候选者已经过时了。
- 如果候选者的任期号大于自己的任期号,该节点会更新自己的任期号为候选者的任期号,然后将自己的状态变为跟随者。
- 如果候选者的任期号等于自己的任期号,此时需要进一步比较日志新旧:由于每条日志会记录(term,index),所以仍然先比较最后一条日志的term,更新的优先;如果相等,再比较index,更大的优先。
- 选举结果: 候选者会等待其他节点的回复,选举有三种可能的结果:
- 赢得选举: 如果一个候选者从集群中的大多数节点(超过一半)那里获得投票,它就赢得了选举,成为新的领导者。
- 分裂投票(Split Vote): 多个候选者同时发起选举,票数分散,没有一个候选者获得多数票。这种情况下,候选者会等待一个新的随机超时时间,然后再次发起选举。
- 被新领导者取代: 如果在等待投票期间,一个节点发现另一个节点发来的RPC的任期号比自己的大,它会立即放弃成为领导者的尝试,将自己的状态转为跟随者,并更新自己的任期号。
一旦选举出领导者,它会周期性地发送心跳消息(AppendEntries RPC,不带日志条目)给所有跟随者,以维持自己的领导者地位。
投票机制中一个候选者成为领导者,必须得到半数以上的票数才行,所以etcd高可用集群中节点数量至少是大于等于3的奇数
- 如果只有 1 个节点:没有容错能力,一旦宕机整个集群不可用
- 如果只有 2 个节点:半数以上=2,任意一个节点宕机后,另一个节点只能得到自己的1票,无法选出 Leader
- 对于节点数大于等于3的集群,偶数个节点并不会提升容错能力:例如3个节点,只能容忍1个出故障,因为最少需要剩下半数以上的节点,他们这些票都投给同一个节点才能让这个节点成为Leader;但对于4个节点,半数以上=3,所以也最多只能容忍1个出故障。
Raft协议中的每个跟随者节点的选举超时时间都是随机且不同的,好处是:
由于每个节点的超时时间不同,一个节点(比如它的超时时间最短)会比其他节点更早地转换为候选者并发送投票请求。这个“先发制人”的候选者很有可能在其他节点超时之前就获得了多数票,从而成功当选为领导者。一旦有领导者产生,其他节点就会转为跟随者,接受新领导者的心跳,避免了分裂投票的结果(防止一直超时,一直在选举)
日志复制(Log Replication)
日志复制是Raft协议的核心功能,它确保了集群中的所有状态机都执行相同的、顺序一致的命令,以实现最终所有机器存储的信息都一样。
客户端请求: 所有的客户端请求都必须先发送给领导者。
日志条目(Log Entry): 领导者将每个客户端请求作为一个日志条目追加到自己的日志中。每个条目包含:
- 命令:要执行的操作(etcd里就是一个键值对存储信息)。
- 任期号:该日志条目创建时的任期号。
- 索引:日志条目在日志中的位置。
发送AppendEntries: 领导者收到日志条目后,会并发地向所有跟随者发送AppendEntries(追加日志条目)RPC。
1
2
3
4
5
6
7
8AppendEntries(
term, # Leader 当前任期
leaderId,
prevLogIndex, # 新日志前一条日志的下标
prevLogTerm, # 该日志的任期
entries[], # 要追加的新日志(可能为空,表示心跳)
leaderCommit # Leader 已提交的索引
)跟随者响应:
- 如果跟随者的日志和领导者不一致,它会回复一个失败的响应。领导者会根据失败信息,尝试发送一个更旧的日志条目,直到找到匹配的点,然后从该点开始同步日志(假设最终匹配到的索引是t,那么代表索引t及其之前的日志也一定和领导者是相同的)
- 如果跟随者成功接收并追加日志条目,它会回复领导者一个成功的响应
- 最终目的是让跟随者的日志和当前领导者日志保持强一致
日志提交(Commit):
- 当某条日志被复制到大多数节点时,Leader 将其标记为已提交;
- Leader 将命令应用到状态机并返回客户端结果;
- Leader 通过心跳通知 Follower 哪些日志已提交,Follower 随后应用到状态机。
日志提交的安全性保障
已提交日志的安全性
Raft的一个核心安全属性是领导者完全性(Leader Completeness)。这个属性保证了:如果一个日志条目在一个任期内被提交了(即被多数节点复制成功),那么这个条目一定会存在于后续所有当选的领导者的日志中。
- 一个日志条目要被提交,必须被多数节点成功复制。
- 一个候选者要当选为领导者,也必须获得多数节点的投票。
- 这两个“多数”集合之间必然存在重叠。这意味着,一个新当选的领导者,它的日志中必然包含了所有在上一任期被提交的日志条目。
所以无论Leader还是Follower宕机,已提交日志都不会丢失,都会被慢慢同步到所有节点上。
未提交日志的潜在丢失
Raft 在处理日志冲突时,会强制覆盖未提交的日志。
这样做是安全的,因为:
- 未提交日志尚未获得多数节点确认,不被视为集群的最终状态;
- 客户端也不会收到成功响应,因此客户端知道该操作未被持久化,可以选择重试;
- 覆盖这些日志正是为了保证集群一致性。
例如,Leader A 写了一条新日志,只成功发给了 1 个 Follower(没到多数)。这时候 Leader A 宕机,新的 Leader B 当选,它的日志没有这条未提交日志。那么 A 和那个 Follower 的日志就包含了“未提交条目”,此时Learder B会强制覆盖掉所有节点上这个日志。
但由于是未提交的,Leader A并不会向客户端发送成功响应,所以客户端是知道这条日志对应的操作是没有被写入集群中的,所以此时客户端会根据自己的需要判断是否重传这个操作,或者是执行其他操作。
因此未提交日志就是错误的日志,需要用新日志覆盖掉,只有提交的日志才被客户端认可。
API Server
API Server 作为集群中其他组件与 etcd 之间的中间层,本身并不存储任何状态数据,因此是一个 无状态服务。
其高可用方案相对简单:通过部署多个 API Server 副本,并在其前面添加一个负载均衡组件(如 Nginx、LVS 或云负载均衡),即可保证外部访问的连续性与可靠性。

K8S Scheduler & K8S Controller
Scheduler 和 Controller Manager 都是 K8S 控制平面的无状态进程。它们的工作模式相同:从 API Server 读取各类资源(如 Pod、Node 和 ReplicaSet 等),执行决策或计算,然后将结果通过 API Server 写回(例如,为 Pod 创建 Binding 或更新资源状态)。
因此,为了实现高可用性,通常会部署多个 Scheduler 或 Controller Manager 实例。
然而,与 API Server 不同,它们不是被动地等待请求,而是主动地持续访问 API Server,以读取信息并执行相应的操作。如果多个实例同时执行相同类型的写入操作(例如,同时为同一个 Pod 绑定 Node,或同时创建/删除某个资源),可能会导致冲突或重复操作。
为了解决这一问题,K8S 内部采用了领导者选举(Leader Election)机制。该机制确保了在任何给定时间,多个 Scheduler 或 Controller 实例中只有一个处于活跃状态并执行任务,而其余的则保持备用。当活跃实例发生故障时,备用实例会自动通过选举接管其职责,从而保证了服务不中断。
下面简单说一下 K8S 的领导者选举机制:
锁对象与实现方式
K8S 的Leader Election依赖于在 API Server 上操作一个“锁对象”,从而实现分布式互斥。
常见的锁类型有:
- 传统方案:使用 Endpoints 或 ConfigMap 作为锁。这两种方式是早期实现,目前已不再推荐。
- 推荐方案:使用
Lease
(coordination.k8s.io/v1
的 Lease 资源)。Lease
资源专为租约型选主设计,具有轻量、更新开销小、语义清晰等优点,因此是目前的首选方案。
这个锁对象会记录关键元数据,例如当前持有者身份(holder identity)、租约持续时间、上次续约时间等,以便判断锁是否有效或已过期。
参与者的身份标识
每个参与选举的进程在启动时都会生成一个唯一的身份标识(例如,podname_uuid
或 hostname+pid
)。这个标识会被写入锁对象的
holder
字段,明确地表明“当前谁是领导者”。
三个重要时间参数
Leader Election涉及三个关键的时间参数,它们共同管理着选主流程:
- LeaseDuration(租约时长):Leader在不续约的情况下,仍然被视为持有锁的最长时间。
- RenewDeadline(续约截止):Leader必须在这个时间窗口内完成续约。如果续约失败并超过此截止时间,它将被认为续约失败。
- RetryPeriod(重试间隔):当候选者尝试获取或续约锁失败时,需要等待的时间间隔。
这些参数的关系通常为:RetryPeriod
较短,而
RenewDeadline
则小于
LeaseDuration
。这种配置确保了领导者会在租约到期前尽早尝试续约,从而降低因网络波动等原因导致误判的风险。
这些参数在
client-go
的leader-election
实现中是可配置的,可以根据具体的网络稳定性和部署环境进行调整。
Leader Election 主循环过程
核心是在每一个Scheduler或Controller实例上持续运行的循环,可以分解为以下几个步骤:
启动与初始化 每个进程副本启动后,首先生成唯一的身份标识,并读取或创建指定的锁对象。如果锁对象不存在,则会创建它。
尝试获取锁
- 进程读取锁对象的当前状态(包括持有者和上次续约时间)。
- 如果锁没有持有者,或者锁已过期,该候选者会尝试通过条件更新(乐观锁)来将自己的身份写入
holder
字段。 - 如果写入成功,该实例即成为领导者,并触发
OnStartedLeading
回调函数,开始执行其业务逻辑(例如调度或控制器循环)。
领导者续约
- 成为领导者后,实例会在一个周期内定期向 API Server 更新锁对象,以刷新
renewTime
字段。此举表明自己依然活跃并持有锁。 - 必须在
LeaseDuration
到期前完成续约。 - 如果领导者连续续约失败并达到设定的阈值,
client-go
会触发OnStoppedLeading
,该实例将放弃领导者身份,并停止执行其专属任务。
- 成为领导者后,实例会在一个周期内定期向 API Server 更新锁对象,以刷新
候选者等待与重试
- 如果锁已被其他实例持有且未过期,候选者不会立刻进行抢夺。它会等待
RetryPeriod
指定的时间后,再次重试读取并检查锁状态。 - 如果检测到锁的持有者发生变更(例如发现新的
holderIdentity
),会触发OnNewLeader
回调,常用于日志记录或监控。
- 如果锁已被其他实例持有且未过期,候选者不会立刻进行抢夺。它会等待
领导权争夺 当领导者无法续约(例如因网络断连或进程崩溃)导致租约过期时,多个备用候选者会几乎同时尝试条件更新锁对象。由于 API Server 对资源的更新是原子化的,最终只有一个请求会成功写入。其他请求会因为版本冲突而失败并进入重试,从而选出新的领导者。
原子性与一致性保障
- 领导者选举的互斥性完全依赖于 API Server
对资源的原子更新。当多个候选者同时尝试更新锁对象时,API Server
的乐观并发控制机制(通常基于
resourceVersion
)会确保只有第一个成功写入的请求被接受,其他请求会因版本不匹配而失败。 - 由于所有操作都通过 API Server 并最终写入 etcd,这利用了 etcd 集中式存储的一致性语义,有效地避免了 脑裂(split-brain)问题,确保在任何时刻都不会有两个实例同时被长期认定为领导者。