深入剖析 Kubernetes API Server 的高可用负载均衡架构

本文面向已具备一定 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 高可用的核心,就是解决两个基本问题:

  1. 冗余:运行多个 API Server 实例,分布在不同的物理节点上,避免单点。
  2. 流量分发与故障转移:需要一个统一、稳定的访问入口(通常是一个虚拟 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-manageretcd 实例。三个 etcd 实例组成一个高可用的 etcd 集群。
  • 高可用与负载均衡组件:每台 Master 节点上还部署了 KeepalivedHAProxy 进程。
  • 网络配置
    • 三台 Master 节点位于同一个二层网络中,以便 VRRP 心跳和 ARP 广播能够正常工作。
    • 规划一个未被使用的 VIP(例如:192.168.10.100),作为整个集群控制平面的统一入口。

正常工作时的数据流

  1. Keepalived 通过 VRRP 选举,假设 master-1 成为 MASTER,master-2 和 master-3 成为 BACKUP。
  2. master-1 节点将 VIP (192.168.10.100) 绑定到其物理网卡上。
  3. 客户端(kubectl, kubelet 等)向 VIP 的 6443 端口发起请求。
  4. 流量到达 master-1 节点,被监听在 VIP:6443 上的 HAProxy 进程接收。
  5. HAProxy 根据其负载均衡策略(如轮询),将 TCP 连接转发到后端的某个健康 API Server 实例(如 master-1:6443, master-2:6443, 或 master-3:6443)。

故障切换时的数据流

  1. master-1 节点宕机,其上的 Keepalived 进程停止发送 VRRP 心跳包。
  2. master-2 和 master-3 在短暂超时后,感知到 MASTER 丢失,开始新一轮选举。
  3. 假设 master-2 因优先级更高而胜出,成为新的 MASTER。
  4. master-2 立即将 VIP 绑定到自己的网卡,并广播免费 ARP。
  5. 网络设备更新 ARP 表,后续发往 VIP 的流量全部转向 master-2 节点。
  6. master-2 上的 HAProxy 接管流量,并将其转发给仍然健康的 API Server 实例(master-2 和 master-3)。整个切换过程对客户端几乎是透明的。

核心模块设计与实现

现在,让我们切换到“极客工程师”模式,直接看代码和配置。细节是魔鬼,错误的配置会让整个高可用体系形同虚设。

Keepalived 配置

Keepalived 的核心是 /etc/keepalived/keepalived.conf 文件。三台 Master 节点的配置大同小异,主要区别在于 statepriority


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_scriptweight 联动。在上面的例子中,如果 HAProxy 进程挂了,master-1 的实际优先级会变成 100 + (-20) = 80。此时,优先级为 90 的 master-2 就会接管 VIP,实现了服务级别的故障转移,而不仅仅是节点级别的。只检查节点存活而不检查服务存活的高可用是伪高可用
  • authentication:虽然 VRRP 是一个局域网协议,但配置一个简单的密码可以防止网络中意外的 VRRP 报文干扰选举,增加一层安全性。

HAProxy 配置

HAProxy 的配置位于 /etc/haproxy/haproxy.cfg。我们主要关注 frontendbackend 部分。


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 方案,这条演进路径体现了在不同环境和规模下,对可用性、性能和运维复杂度的不同权衡。理解每种方案背后的核心原理,才能在实际工作中做出最恰当的架构决策。

延伸阅读与相关资源

  • 想系统性规划股票、期货、外汇或数字币等多资产的交易系统建设,可以参考我们的
    交易系统整体解决方案
  • 如果你正在评估撮合引擎、风控系统、清结算、账户体系等模块的落地方式,可以浏览
    产品与服务
    中关于交易系统搭建与定制开发的介绍。
  • 需要针对现有架构做评估、重构或从零规划,可以通过
    联系我们
    和架构顾问沟通细节,获取定制化的技术方案建议。
滚动至顶部