Kubernetes 控制面的高可用不是简单地把 master 节点从一台扩成三台。不同组件对高可用的要求差异很大:kube-apiserver 是无状态的,可以直接横向扩展;kube-controller-manager 和 kube-scheduler 虽然也可以部署多副本,但同时只能有一个实例在真正工作,否则可能重复创建资源或并发调度冲突;etcd 则依赖 Raft 共识协议,必须部署为奇数个节点并通过法定人数保证数据一致性。理解这些差异是设计高可用方案的前提。

控制面组件的高可用特性
先看 kube-apiserver。它是所有客户端和集群内部组件访问 Kubernetes 的唯一入口,本身不保存任何业务状态,所有的对象数据都存放在 etcd 中。因此多副本部署非常直接:启动多个 kube-apiserver 实例,让它们连接到同一个 etcd 集群即可。客户端通过负载均衡器访问这些实例,任何一台宕机都不会影响整体服务。
kube-controller-manager 和 kube-scheduler 则不同。它们的工作逻辑要求同一时刻只能有一个领导者实例执行实际任务。例如 kube-controller-manager 中的副本控制器如果多个实例同时工作,就可能重复创建或删除 Pod;kube-scheduler 多个实例同时调度可能把同一个 Pod 绑定到不同节点上。Kubernetes 通过 Leader Election 机制解决这个问题:所有副本启动后竞争一个租约,获得租约的实例成为领导者,其他实例处于待命状态。默认使用 kube-system 命名空间下的 leases 资源来实现选主,而不是早期的 endpoints 资源。
etcd 的高可用则依赖分布式共识。一个 etcd 集群由多个成员组成,通常为 3 个或 5 个,每次写入都需要超过半数成员确认。如果少于半数成员存活,集群会进入只读甚至不可用状态,这也是为什么 etcd 节点数量必须是奇数。单个 etcd 节点故障不会影响写入,因为剩余节点仍然满足多数派条件,但两节点同时故障就会让集群失去仲裁能力。
两种主流的拓扑结构
部署高可用控制面时,首先要决定 etcd 是和控制平面组件放在一起,还是独立部署。这两种方式分别被称为堆叠 etcd 拓扑和外部 etcd 拓扑。
堆叠 etcd 拓扑中,每个控制平面节点上同时运行 kube-apiserver、kube-controller-manager、kube-scheduler 以及一个 etcd 成员。这种方式的优点是节点数量少、部署简单,适合中小规模集群或对成本敏感的场景。但缺点也很明显:etcd 和 API Server 共享同一台机器的 CPU、内存和磁盘,控制平面负载较高时可能互相影响,而且节点故障会同时失去一个 etcd 成员和一个 API Server 实例,恢复时需要更谨慎地处理 etcd 成员变更。
外部 etcd 拓扑则把 etcd 集群部署在独立的物理机或虚拟机上,控制平面节点只运行 Kubernetes 组件。这种隔离带来了更稳定的性能表现和更清晰的故障域划分,etcd 可以针对高 IOPS 配置高性能磁盘,控制节点故障也不会影响 etcd 的多数派。官方推荐生产环境采用外部 etcd 拓扑,但需要额外维护一组 etcd 节点,运维复杂度更高。
使用 kubeadm 初始化堆叠 etcd 集群时,配置文件中 etcd.local 部分会自动在控制平面节点上部署 etcd。示例配置如下:
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controlPlaneEndpoint: "10.0.0.100:6443"
apiServer:
certSANs:
- "10.0.0.100"
etcd:
local:
dataDir: /var/lib/etcd
如果采用外部 etcd,则需要在 etcd.external 字段中指定 etcd 集群的访问地址和证书,kubeadm 不会在控制平面节点上启动 etcd 容器。
用负载均衡统一 API Server 入口
多个 kube-apiserver 实例部署完成后,客户端无法直接选择要连接哪个实例。常见的做法是在控制平面节点前面架设一个 TCP 负载均衡器,把 6443 端口的流量均匀分发到各个 API Server。负载均衡器可以使用云厂商提供的负载均衡服务,也可以自行部署 HAProxy 加 Keepalived 实现高可用。
HAProxy 工作在四层 TCP 模式时,不解析 HTTP 内容,只是简单转发连接,这正好符合 Kubernetes API Server 的加密通信需求。下面是一个最小化的 HAProxy 配置,监听虚拟 IP 10.0.0.100:6443 并转发到三个控制平面节点:
frontend k8s-api
bind 10.0.0.100:6443
mode tcp
option tcplog
default_backend k8s-masters
backend k8s-masters
mode tcp
balance roundrobin
server master1 10.0.0.11:6443 check
server master2 10.0.0.12:6443 check
server master3 10.0.0.13:6443 check
除了四层负载均衡,也可以使用七层代理配合 SNI 转发,但四层 TCP 转发性能更高、配置也更简单。需要注意的是,kubeadm 初始化时填写的 controlPlaneEndpoint 必须指向这个负载均衡地址,后续加入集群的其他控制平面节点也会通过该地址访问 API Server。证书需要包含该虚拟 IP 或域名,否则客户端会遭遇证书验证失败。
Keepalived 可以为 HAProxy 提供虚拟 IP 的故障转移能力。两台 HAProxy 节点同时运行 Keepalived,一台处于 MASTER 状态持有虚拟 IP,另一台处于 BACKUP 状态。当 MASTER 故障时,BACKUP 通过 VRRP 协议接管虚拟 IP,保证负载均衡入口不中断。控制平面节点本身不再直接对外暴露 6443 端口,所有流量都经过这一层。
验证高可用与故障演练
部署完成后不能只看所有 Pod 都在运行,必须实际模拟节点故障来验证切换是否正常。首先确认 kube-controller-manager 和 kube-scheduler 的领导者身份。执行下面的命令可以查看当前租约的持有者:
kubectl get leases -n kube-system kubectl describe lease kube-controller-manager -n kube-system
输出中的 holderIdentity 字段会显示当前领导者所在的主机名。接下来可以直接停止该领导者所在的控制平面节点,例如关机或者停止 kubelet 服务。此时其他副本会检测到租约过期,并在几秒到十几秒内发起新一轮选举,选出新的领导者。通过再次查看租约信息,可以看到 holderIdentity 已经切换到另一个节点。
对于 API Server 的高可用验证,可以持续向负载均衡地址发送请求,同时停止其中一个控制平面节点的 kube-apiserver 容器或整个节点。如果负载均衡器配置了健康检查,失败的 API Server 会被自动摘除,客户端请求会路由到剩余的实例。可以使用 curl 命令访问 https://10.0.0.100:6443/healthz 查看健康状态,但需要注意携带正确的认证信息或直接检查负载均衡器的后端状态页。
故障演练中还要关注 etcd 的行为。依次停止一个 etcd 成员,集群应该仍然能够正常读写。使用 etcdctl endpoint health 命令可以查看每个成员的详细健康状态。如果停止了超过半数的成员,写入操作会失败,这符合 Raft 共识的预期,也提醒我们在维护 etcd 集群时不能同时重启多个节点。
通过以上逐层设计和验证,控制面多副本高可用部署才算真正落地。故障切换时间、组件告警、日志监控等后续工作还需要配合 Prometheus 和告警规则持续完善,但核心的部署思路和验证方法已经具备。

