本文面向已具备一定 Kubernetes 运维经验的中高级工程师,旨在深度剖析其控制平面核心——API Server 的高可用与负载均衡架构。我们将从生产环境中遇到的单点故障问题切入,回归到虚拟 IP 和负载均衡的底层网络原理,并最终落地到经典的 Keepalived+HAProxy 方案的具体实现与配置。文章不止于“如何做”,更会深入探讨架构选型背后的技术权衡、性能瓶颈以及面向未来的架构演进路径。
现象与问题背景
在任何一个 Kubernetes 集群中,API Server 都是绝对的控制中枢。无论是开发者执行 kubectl apply,还是集群内部的 Controller Manager、Scheduler、Kubelet 等组件,所有对集群状态的读写操作都必须通过 API Server 这个唯一的入口。它验证请求、处理业务逻辑,并将最终状态持久化到后端的 etcd 集群。这种中心化的设计简化了模型,但也带来了显而易见的风险:API Server 的单点故障(SPOF)。
在一个仅部署了单个 Master 节点的测试环境中,一旦该节点的 kube-apiserver 进程异常退出,或整个节点宕机,整个集群将瞬间“失联”。具体表现为:
kubectl命令全部超时或连接拒绝。- 新的 Pod 无法被调度,现有的 Deployment 无法进行扩缩容。
- 所有依赖于 Watch 机制的控制器(如 Ingress Controller、Prometheus Operator)全部失效,无法响应集群状态变化。
- 正在运行的业务 Pod 虽然暂时不受影响,但集群已经进入一个无法管理、无法自愈的“僵尸”状态。
因此,对于任何生产级别的 Kubernetes 集群,构建一套高可用的控制平面是不可或缺的前置条件。而实现 API Server 高可用的核心,就是解决两个基本问题:
- 冗余:运行多个 API Server 实例,分布在不同的物理节点上,避免单点。
- 流量分发与故障转移:需要一个统一、稳定的访问入口(通常是一个虚拟 IP),能将客户端流量智能地分发到健康的 API Server 实例上,并在某个实例故障时自动将其从可用列表中移除。
这正是负载均衡器(Load Balancer)和高可用组件(High Availability Component)需要解决的问题。在自建机房或裸金属环境中,Keepalived 结合 HAProxy 组成的架构,是业界经过长期检验的、最为经典的解决方案之一。
关键原理拆解
在我们深入架构实现之前,必须先回到计算机科学的基础,理解支撑这套体系的几个关键原理。这部分内容将由一位严谨的“大学教授”为你讲解。
分布式系统的无状态设计与共识
首先,我们能对 API Server 做负载均衡的前提是它可以水平扩展。这意味着我们可以同时运行多个功能完全对等的 API Server 实例。这得以实现,是因为 kube-apiserver 本身被设计为基本无状态(Largely Stateless)的服务。它不负责存储集群状态,而是将所有数据(如 Pod、Service 的定义)的持久化工作全权委托给后端的 etcd 集群。etcd 是一个基于 Raft 共识算法的强一致性键值存储,它才是 Kubernetes 集群状态的“唯一事实来源”(Single Source of Truth)。
因此,任何一个 API Server 实例都可以处理任何客户端请求,因为它们都连接到同一个 etcd 集群来读写数据。这使得 API Server 实例之间无需进行复杂的状态同步或主从选举,极大地简化了其自身的 HA 架构。负载均衡器可以简单地将请求分发给任意一个健康的实例。
网络协议栈中的虚拟 IP (VIP) 与 ARP 协议
为了提供一个稳定的访问入口,我们需要一个不与任何特定物理网卡绑定的 IP 地址,这就是虚拟 IP(Virtual IP, VIP)。Keepalived 正是利用 Linux 内核的网络功能来实现 VIP 的管理和漂移。
其核心原理与 ARP(Address Resolution Protocol) 协议息息相关。在以太网中,通信需要知道目标的 MAC 地址。ARP 协议负责将 IP 地址解析为 MAC 地址。当一台主机想与 VIP 通信时,它会广播一个 ARP 请求:“谁拥有 IP 地址 192.168.10.100?请告诉我你的 MAC 地址。”
Keepalived 利用 VRRP(Virtual Router Redundancy Protocol) 协议在多个节点间进行心跳检测和主节点选举。胜出成为 MASTER 的节点会将 VIP 绑定到自己的物理网卡上,并开始响应针对该 VIP 的 ARP 请求。当 MASTER 节点宕机,心跳中断,其他 BACKUP 节点会通过选举产生新的 MASTER。新的 MASTER 会立即执行一个关键动作:广播一个 免费 ARP(Gratuitous ARP) 包。这个包向整个局域网宣告:“现在 IP 地址 192.168.10.100 的 MAC 地址是我的 MAC 地址!”。交换机和路由器收到这个广播后,会更新自己的 ARP 缓存表,后续发往 VIP 的数据包就会被正确地转发到新的 MASTER 节点上。这个过程通常在 1-3 秒内完成,实现了 IP 地址的快速漂移。
负载均衡的 L4 与 L7 模式
当流量到达持有 VIP 的节点后,我们需要 HAProxy 将其转发给后端的 API Server。HAProxy 可以在两种主要模式下工作:
- L4 负载均衡(传输层):工作在 TCP/UDP 层面。它只关心 IP 地址和端口,不对应用层数据进行任何解析。它在客户端和后端服务器之间建立一个 TCP 连接,进行原始字节流的转发。这种模式性能极高,延迟极低,对于像 API Server 这样基于 TLS 加密的 HTTPS 流量是完美的选择。
- L7 负载均衡(应用层):可以解析 HTTP/HTTPS 协议。它能够根据 URL 路径、请求头、Cookie 等信息做出更智能的路由决策。虽然功能强大,但因需要解析和重组报文,性能开销远大于 L4。对于 API Server 这种单一、不透明的 gRPC/protobuf 流量,L7 的优势无法体现,反而会增加不必要的延迟。
因此,在我们的场景中,HAProxy 采用 L4 TCP 模式是最佳实践。
系统架构总览
基于上述原理,一个经典的三 Master 节点高可用 Kubernetes 集群架构如下:
- 物理部署:三台独立的物理机或虚拟机(master-1, master-2, master-3)。
- Kubernetes 组件:每台 Master 节点上都完整部署了
kube-apiserver,kube-scheduler,kube-controller-manager和etcd实例。三个 etcd 实例组成一个高可用的 etcd 集群。 - 高可用与负载均衡组件:每台 Master 节点上还部署了
Keepalived和HAProxy进程。 - 网络配置:
- 三台 Master 节点位于同一个二层网络中,以便 VRRP 心跳和 ARP 广播能够正常工作。
- 规划一个未被使用的 VIP(例如:192.168.10.100),作为整个集群控制平面的统一入口。
正常工作时的数据流:
- Keepalived 通过 VRRP 选举,假设 master-1 成为 MASTER,master-2 和 master-3 成为 BACKUP。
- master-1 节点将 VIP (192.168.10.100) 绑定到其物理网卡上。
- 客户端(
kubectl,kubelet等)向 VIP 的 6443 端口发起请求。 - 流量到达 master-1 节点,被监听在 VIP:6443 上的 HAProxy 进程接收。
- HAProxy 根据其负载均衡策略(如轮询),将 TCP 连接转发到后端的某个健康 API Server 实例(如 master-1:6443, master-2:6443, 或 master-3:6443)。
故障切换时的数据流:
- master-1 节点宕机,其上的 Keepalived 进程停止发送 VRRP 心跳包。
- master-2 和 master-3 在短暂超时后,感知到 MASTER 丢失,开始新一轮选举。
- 假设 master-2 因优先级更高而胜出,成为新的 MASTER。
- master-2 立即将 VIP 绑定到自己的网卡,并广播免费 ARP。
- 网络设备更新 ARP 表,后续发往 VIP 的流量全部转向 master-2 节点。
- master-2 上的 HAProxy 接管流量,并将其转发给仍然健康的 API Server 实例(master-2 和 master-3)。整个切换过程对客户端几乎是透明的。
核心模块设计与实现
现在,让我们切换到“极客工程师”模式,直接看代码和配置。细节是魔鬼,错误的配置会让整个高可用体系形同虚设。
Keepalived 配置
Keepalived 的核心是 /etc/keepalived/keepalived.conf 文件。三台 Master 节点的配置大同小异,主要区别在于 state 和 priority。
global_defs {
# 必须设置,否则多个 vrrp_instance 可能会有问题
vrrp_strict
# 标识这台机器,日志中会看到
router_id K8S_MASTER_01
}
# 监控 HAProxy 进程是否存活的脚本
vrrp_script check_haproxy {
script "/usr/bin/killall -0 haproxy" # 检查 haproxy 进程是否存在
interval 2 # 每 2 秒检查一次
weight -20 # 如果脚本失败,优先级降低 20
}
vrrp_instance VI_K8S {
# master-1 设置为 MASTER,其他设置为 BACKUP
state MASTER
# 绑定的物理网卡
interface ens192
# 虚拟路由 ID,同一组 Keepalived 必须相同
virtual_router_id 51
# 优先级,MASTER 节点最高,BACKUP 依次降低
# master-1: 100, master-2: 90, master-3: 80
priority 100
# VRRP 心跳包发送间隔,单位秒
advert_int 1
authentication {
auth_type PASS
auth_pass k8s_ha_secret
}
# 定义 VIP
virtual_ipaddress {
192.168.10.100/24
}
# 引用监控脚本
track_script {
check_haproxy
}
}
工程坑点与解析:
vrrp_strict:这个参数必须加上。在老版本中,如果没有它,当内核的 IPVS 模块未加载时,Keepalived 可能无法正确处理 VIP 的添加和删除,导致一些诡异的网络问题。virtual_router_id:这是 VRRP 实例的唯一标识。同一个二层网络中,绝对不能有两组 Keepalived 使用相同的 ID,否则会产生冲突,导致 VIP 在不同组之间疯狂漂移。priority:这是选举的核心。优先级高的会成为 MASTER。注意,priority值会与track_script的weight联动。在上面的例子中,如果 HAProxy 进程挂了,master-1 的实际优先级会变成 100 + (-20) = 80。此时,优先级为 90 的 master-2 就会接管 VIP,实现了服务级别的故障转移,而不仅仅是节点级别的。只检查节点存活而不检查服务存活的高可用是伪高可用。authentication:虽然 VRRP 是一个局域网协议,但配置一个简单的密码可以防止网络中意外的 VRRP 报文干扰选举,增加一层安全性。
HAProxy 配置
HAProxy 的配置位于 /etc/haproxy/haproxy.cfg。我们主要关注 frontend 和 backend 部分。
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
defaults
mode tcp
log global
option tcplog
option dontlognull
retries 3
timeout connect 5s
timeout client 1m
timeout server 1m
frontend k8s-api
bind 192.168.10.100:6443
mode tcp
default_backend k8s-api-backend
backend k8s-api-backend
mode tcp
balance roundrobin
# 健康检查配置,每 5 秒检查一次
option tcp-check
server master-1 192.168.10.11:6443 check port 6443 inter 5s
server master-2 192.168.10.12:6443 check port 6443 inter 5s
server master-3 192.168.10.13:6443 check port 6443 inter 5s
工程坑点与解析:
bind 192.168.10.100:6443:HAProxy 必须显式绑定在 VIP 上。但是,如果 HAProxy 启动时 VIP 还不在本机,启动会失败。为了解决这个问题,需要设置内核参数 `net.ipv4.ip_nonlocal_bind = 1`,允许进程绑定一个不属于本机的 IP 地址。这是部署这套架构时一个非常常见的坑。mode tcp:再次强调,对 API Server 必须使用 TCP 模式,以获得最佳性能。balance roundrobin:轮询是最简单的负载均衡算法。对于 API Server 这种长连接(如 Watch)和短连接(如 CRUD 操作)混合的场景,leastconn(最少连接数)可能是个更好的选择,它会将新连接发送给当前活动连接数最少的后端服务器。option tcp-check:这是 HAProxy 的健康检查机制。它会定期尝试与后端的 API Server 建立 TCP 连接。如果连接失败,HAProxy 会自动将该后端标记为宕机,不再向其转发流量。这构成了第二层健康检查,与 Keepalived 的进程检查相辅相成。- 后端 IP 地址:注意,
backend中配置的 IP 是各个 Master 节点的物理 IP,而不是 VIP。HAProxy 的角色就是将来自 VIP 的流量,转发到物理 IP 上。
性能优化与高可用设计
这套架构虽然经典,但并非银弹。在设计和运维中,我们需要理解其固有的权衡(Trade-off)。
Keepalived 的权衡与风险
- Active-Passive 模式:VRRP 本质上是主备模式。任何时候,只有一台节点的上行网络带宽在为 VIP 服务,其他 BACKUP 节点的上行带宽被“浪费”了。不过,由于控制平面的流量通常远低于数据平面,这在绝大多数场景下都不是瓶颈。
- 故障切换时间:切换时间主要由
advert_int和网络中 ARP 缓存的更新速度决定,通常在秒级。这对 Kubernetes 控制平面是完全可以接受的。但对于要求毫秒级切换的超低延迟交易系统,这个方案可能就不够用了。 - 脑裂(Split-Brain):这是主备架构的经典难题。如果 Master 节点之间出现网络分区(例如,连接它们的交换机故障),导致它们无法收到彼此的心跳,那么每个分区内的节点都可能认为自己应该成为 MASTER,从而导致多个节点同时宣告拥有同一个 VIP。这会造成严重的网络混乱。通过配置 `vrrp_strict` 和 `authentication` 能缓解但不能根除此问题。终极解决方案需要更复杂的仲裁机制,但这会显著增加架构复杂度。
HAProxy 的角色与瓶颈
- 性能瓶颈:HAProxy 本身性能极高,在 L4 模式下处理几十万并发连接、达到万兆线速转发都不是问题。真正的瓶颈几乎总是在后端的 API Server 或 etcd。
- 长连接与负载均衡:API Server 的 Watch 请求是 HTTP Long Polling,会建立长时间保持的 TCP 连接。如果使用 `roundrobin` 算法,可能会导致长连接在各个后端分布不均。`leastconn` 算法能更好地处理这种情况。
- 同节点转发优化(Co-located HAProxy):当流量到达持有 VIP 的 Master 节点,其上的 HAProxy 将流量转发给本机的 API Server 时,数据包需要经过完整的 TCP/IP 协议栈(从网卡进,内核处理,转发给 HAProxy 进程,HAProxy 再通过 loopback 接口发给 API Server 进程)。虽然性能很高,但仍有开销。在极端追求低延迟的场景下,可能会考虑更底层的 LVS/DR 模式,但其配置和维护复杂度要高得多。
替代方案的思考:为什么不用 DNS?
一个常见的疑问是,为什么不用 DNS 轮询来做负载均衡?将一个域名(如 `k8s-api.my-company.com`)解析到多个 Master 节点的 IP。主要原因在于 DNS 缓存。客户端和各级 DNS 服务器都会缓存解析结果。当一台 API Server 宕机,即使你修改了 DNS 记录,旧的、指向故障 IP 的缓存可能在几分钟甚至几小时内都不会过期。这对于需要快速响应故障的集群内部组件(如 controller-manager)是致命的。VIP 漂移则能在秒级完成切换,可靠性远高于 DNS 方案。
架构演进与落地路径
技术架构不是一成不变的,它需要根据环境和业务规模不断演进。
第一阶段:单 Master 节点
适用于开发、测试或非常小型的、非关键业务集群。搭建简单,成本最低。但必须清醒认识到其不具备任何生产可用性。
第二阶段:Keepalived + HAProxy 方案
这是本文详述的方案,是裸金属或私有云环境下的“黄金标准”。它技术成熟,社区文档丰富,运维人员技能栈匹配度高。对于绝大多数自建 Kubernetes 集群,这套方案提供了足够的可用性和性能,是构建生产级集群的理想起点。
第三阶段:云原生与网络协议演进
当集群规模进一步扩大,或部署在更现代化的数据中心网络中时,可以考虑更高级的方案。
- 公有云托管方案:如果在 AWS/GCP/Azure 等公有云上,最佳实践是直接使用云厂商提供的托管 Kubernetes 服务(EKS, GKE, AKS)。它们会为你管理整个控制平面,包括 API Server 的高可用和负载均衡(通常通过其内建的网络负载均衡器 NLB/ELB 实现),用户无需关心底层细节。
– 基于 BGP 的方案(如 MetalLB, Kube-VIP):在支持 BGP(Border Gateway Protocol)的现代数据中心网络中,可以通过 MetalLB 这类项目,让 Kubernetes 的 Service(LoadBalancer 类型)直接与物理网络路由器进行路由宣告。你可以创建一个 LoadBalancer 类型的 Service 指向后端的 API Server Pods。MetalLB 会负责从一个地址池中分配一个 VIP,并通过 BGP 协议向路由器宣告“想到达这个 VIP,请通过我”。多个节点可以同时宣告,路由器会通过 ECMP(Equal-Cost Multi-Path)进行真正的 Active-Active 负载均衡。这种方案消除了 VRRP 的主备瓶颈,故障切换更快(通常亚秒级),是大规模裸金属集群的终极演进方向。
总而言之,从单点到 Keepalived+HAProxy,再到云原生或 BGP 方案,这条演进路径体现了在不同环境和规模下,对可用性、性能和运维复杂度的不同权衡。理解每种方案背后的核心原理,才能在实际工作中做出最恰当的架构决策。
延伸阅读与相关资源
-
想系统性规划股票、期货、外汇或数字币等多资产的交易系统建设,可以参考我们的
交易系统整体解决方案。 -
如果你正在评估撮合引擎、风控系统、清结算、账户体系等模块的落地方式,可以浏览
产品与服务
中关于交易系统搭建与定制开发的介绍。 -
需要针对现有架构做评估、重构或从零规划,可以通过
联系我们
和架构顾问沟通细节,获取定制化的技术方案建议。