Kubernetes API Server 高可用负载均衡:从 VIP 漂移到四层代理的深度实践

本文专为期望在裸金属(On-Premise)环境中构建生产级 Kubernetes 集群的中高级工程师和架构师设计。我们将深入剖析 Kubernetes 控制平面的核心——API Server 的高可用性(HA)与负载均衡(LB)方案。我们将跳过云厂商提供的托管服务,直面问题的本质:如何利用 Keepalived 和 HAProxy 等基础组件,构建一个健壮、高效且自主可控的控制平面入口。本文将从分布式系统与网络协议的基础原理出发,结合一线实战中的配置细节与架构权衡,为你提供一份可直接落地的深度实践指南。

现象与问题背景

Kubernetes API Server 是整个集群的“大脑”和唯一入口。无论是开发者通过 kubectl 下发指令,还是集群内部的 Controller Manager、Scheduler、Kubelet 等核心组件进行状态同步与任务协调,所有操作都必须经过 API Server。它负责请求的认证、授权、准入控制,并将集群状态持久化到后端的 etcd 存储中。在一个未经高可用设计的集群里,API Server 通常运行在单一的 Master 节点上。这直接导致了一个致命的架构缺陷:单点故障(Single Point of Failure, SPOF)

一旦这台唯一的 Master 节点因硬件故障、操作系统崩溃或网络隔离而宕机,整个集群的控制平面将彻底瘫痪。虽然已经运行的 Pod 会继续保持运行状态(数据平面的短时幸存),但你将无法执行任何管理操作:无法部署新的应用,无法查看集群状态,无法进行故障排查,自动伸缩(HPA)等依赖控制器和 API Server 交互的功能也将全部失效。集群将进入一种“僵尸”状态,直到 Master 节点被修复。对于任何生产系统而言,这种脆弱性都是不可接受的。

因此,构建一个高可用的 Kubernetes 集群,首要任务就是解决 API Server 的单点问题。这引出了两个核心需求:

  • 冗余(Redundancy):必须运行多个 API Server 实例,分布在不同的物理节点上。通常建议至少三节点以形成一个高可用集群。
  • 流量分发与故障转移(Traffic Distribution & Failover):需要一个统一的、稳定的入口点,能将客户端流量智能地分发到健康的 API Server 实例上,并在某个实例或节点故障时,能自动、透明地将流量切换到其他健康实例。

本文将要探讨的 Keepalived + HAProxy 方案,正是解决上述问题的经典组合,它通过网络层的虚拟 IP(VIP)漂移和传输层的负载均衡,共同构筑了 API Server 的高可用堡垒。

关键原理拆解

在深入实现细节之前,我们必须回归计算机科学的基础原理,理解支撑这套架构的底层机制。这有助于我们不仅知其然,更能知其所以然,从而在遇到问题时能够从根源上进行分析。

从分布式系统视角:API Server 的无状态性

首先,一个关键的前提是:Kubernetes API Server 本身被设计为无状态(Stateless)应用。它不保存任何集群状态的持久化数据,所有状态都存储在后端的 etcd 集群中。etcd 使用 Raft 一致性算法来保证数据的强一致性和高可用性。这意味着我们可以水平扩展 API Server,启动任意多个实例,它们共享同一个 etcd 后端。客户端的任意请求,无论被哪个 API Server 实例处理,最终都会操作同一个 etcd 数据源,从而看到一致的集群视图。这种无状态设计是实现负载均衡和高可用的基石。API Server 的高可用问题,本质上从应用层问题,简化为了一个更为纯粹的网络流量分发问题。

网络协议层:虚拟路由冗余协议(VRRP)

为了给多个 API Server 实例提供一个统一入口,我们需要一个“虚拟”的 IP 地址,即 Virtual IP (VIP)。这个 VIP 不与任何单一节点的物理网卡绑定,而是可以在多个节点之间“漂移”。实现这一目标的核心技术是 VRRP (Virtual Router Redundancy Protocol),其工作原理如下:

  • 角色定义:在一个 VRRP 组中,多个节点(路由器或服务器)共同维护一个 VIP。在任意时刻,只有一个节点处于 MASTER 状态,其余节点均为 BACKUP 状态。
  • 心跳与选举:MASTER 节点会周期性地通过组播(默认)或单播发送 VRRP 通告(Advertisement)报文,向组内其他成员宣告自己的“存活”。BACKUP 节点则持续监听这些报文。
  • 抢占与故障转移:如果 BACKUP 节点在预设的超时时间内(通常是3倍通告间隔)没有收到 MASTER 的心跳报文,它会认为 MASTER 已经失效。此时,BACKUP 节点之间会根据预设的优先级(Priority)进行新一轮选举。优先级最高的 BACKUP 节点将转变为新的 MASTER。
  • 内核交互:当一个节点成为 MASTER 时,它会通过操作系统内核提供的接口(如 Netlink Sockets)向自己的物理网卡上添加这个 VIP 地址。当它失去 MASTER 身份时,则会移除该 VIP。这个过程对上层应用是完全透明的,它们只看到一个始终可用的 IP 地址。Keepalived 就是 VRRP 协议的一个成熟的开源实现。

负载均衡层:四层代理(L4 Load Balancing)

当客户端请求通过 VIP 到达了当前的 MASTER 节点后,我们需要将这些请求分发给后端多个健康的 API Server 实例。这就是负载均衡器的职责。负载均衡器分为四层(L4)和七层(L7)。

  • 四层负载均衡:工作在 TCP/IP 协议栈的传输层。它根据报文的目标 IP 和端口进行转发决策,而不关心报文内容。它直接修改数据包的目标 MAC/IP 地址(NAT 模式)或直接转发(DR 模式),然后由内核网络栈进行处理。这种方式性能极高,因为它只做了少量的包头改写,几乎没有应用层的数据处理开销。HAProxy 和 Nginx 都可以工作在四层模式下。
  • 七层负载均衡:工作在应用层。它可以解析 HTTP/HTTPS 等协议的内容,根据 URL 路径、请求头、Cookie 等信息进行更精细的流量分发。这对于微服务网关等场景非常有用,但对于 API Server 这种单一的、基于 mTLS 加密的 gRPC/HTTPS 流量,七层解析不仅没有必要,还会带来性能损耗和证书管理的复杂性。

因此,对于 API Server 的负载均衡,四层代理是最佳选择。它在保证高性能的同时,完美满足了流量分发和健康检查的需求。

系统架构总览

基于以上原理,我们设计的 K8s Master 节点高可用负载均衡架构如下,假设我们有三台 Master 节点(master-1, master-2, master-3):

  1. 组件部署:每台 Master 节点上都部署完整的控制平面组件:kube-apiserverkube-controller-managerkube-scheduler。此外,每台节点还需部署 keepalivedhaproxy 进程。
  2. 虚拟 IP (VIP):我们规划一个在内网中未被使用的 IP 地址作为 VIP,例如 192.168.10.100。这个 VIP 将是整个集群对外的统一访问入口。所有 Kubelet、Controller 以及用户的 kubectl 都将配置指向这个 VIP。
  3. Keepalived 集群:三台 Master 节点上的 keepalived 进程组成一个 VRRP 组,共同管理这个 VIP。通过配置不同的优先级(例如 master-1 为 102,master-2 为 101,master-3 为 100),初始时 master-1 将成为 MASTER,并在其网卡上绑定 VIP。其他两台为 BACKUP。
  4. HAProxy 实例:每台 Master 节点上的 haproxy 进程都配置为监听在本地的某个端口(例如 8443),并将流量负载均衡到所有三个 Master 节点的 API Server 实例上(例如 master-1:6443, master-2:6443, master-3:6443)。同时,HAProxy 会对后端的 API Server 进行持续的健康检查。
  5. 流量路径
    • 一个客户端请求发往 VIP:6443
    • 请求首先到达当前持有 VIP 的 MASTER 节点(例如 master-1)。
    • 该节点上的内核网络栈将请求交给监听在 VIP:6443 的 HAProxy 进程。
    • HAProxy 根据其负载均衡策略(如 `leastconn`)和健康检查结果,选择一个健康的后端 API Server 实例(可能是 master-1、master-2 或 master-3 中的任意一个)。
    • HAProxy 将请求转发给选定的 API Server。
  6. 故障转移流程
    • 假设 master-1 节点宕机。
    • master-2 和 master-3 上的 keepalived 进程会因收不到 master-1 的 VRRP 心跳而触发选举。
    • master-2 因其优先级(101)高于 master-3(100)而胜出,成为新的 MASTER。
    • master-2 的 keepalived 进程立即在其网卡上绑定 VIP。
    • 新的客户端请求现在会流向 master-2。
    • master-2 上的 HAProxy 接管流量,并发现后端 master-1:6443 健康检查失败,于是它会自动将流量仅分发给健康的 master-2 和 master-3。整个切换过程在数秒内完成,对客户端透明。

这个架构通过 VIP 漂移解决了“入口”的高可用,通过负载均衡器解决了“服务实例”的高可用和扩展性,形成了一个闭环的、健壮的解决方案。

核心模块设计与实现

理论是灰色的,生命之树常青。接下来,我们进入极客工程师的角色,直接看代码和配置,把这套架构落地。

Keepalived 配置详解

这是生产环境中一份典型的 keepalived.conf 文件(以 master-1 为例)。


global_defs {
   # 邮件通知配置,生产环境建议配置
   # smtp_server 192.168.1.1
   # smtp_from [email protected]
   # notification_email {
   #  [email protected]
   # }
   router_id K8S_MASTER_01 # 唯一标识,建议与主机名对应
}

# 关键:健康检查脚本,用于检查本地 HAProxy 进程是否存活
vrrp_script check_haproxy {
    script "/usr/bin/killall -0 haproxy" # 使用 killall -0 检查进程是否存在,比 pgrep 更高效
    interval 2                           # 每 2 秒检查一次
    weight -20                           # 如果脚本失败(haproxy 进程不存在),优先级降低 20
}

vrrp_instance VI_K8S_API {
    state MASTER             # master-1 初始状态为 MASTER
    interface ens192         # 物理网卡名称,根据实际情况修改
    virtual_router_id 51     # VRRP 组 ID,同一网段内必须唯一
    priority 102             # 优先级,master-1 最高
    advert_int 1             # VRRP 通告间隔,1 秒
    
    authentication {
        auth_type PASS
        auth_pass your_secret_password # 简单的认证密码
    }
    
    virtual_ipaddress {
        192.168.10.100/24 dev ens192 # 定义 VIP 地址和绑定的设备
    }
    
    # 追踪健康检查脚本
    track_script {
        check_haproxy
    }
}

极客坑点分析:

  • state MASTER/BACKUP: 只有一台可以设置为 MASTER,其余必须是 BACKUP。但更稳健的做法是所有节点都配置为 BACKUP,让它们通过优先级纯粹地选举,避免因网络分区恢复时出现两个 MASTER 的短暂脑裂。
  • priority: master-2 的 priority 应设为 101,master-3 设为 100。这个差值决定了抢占顺序。
  • vrrp_scripttrack_script: 这是整个配置的灵魂。没有它,Keepalived 只能感知到节点级别的存活(如 ping 通),而无法感知到关键服务(HAProxy)的健康状况。想象一下,如果 master-1 节点活着,但 HAProxy 进程崩溃了,VIP 仍然会留存在 master-1 上,导致所有流量发往一个黑洞。通过 track_script,一旦 killall -0 haproxy 执行失败,Keepalived 会立即将当前节点的优先级降低 20(从 102 降到 82),这个值低于了 master-2 的 101,从而强制触发主备切换。这是保证服务真正可用的关键。
  • authentication: 虽然简单,但在复杂的网络环境中,可以防止其他无关的 VRRP 报文干扰选举。

HAProxy 配置详解

以下是 haproxy.cfg 的配置示例,它将在所有 Master 节点上保持一致。


global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats

defaults
    mode                    tcp
    log                     global
    option                  tcplog
    option                  dontlognull
    # 超时设置非常关键,需要根据网络情况和应用特性微调
    timeout connect         5s
    timeout client          30m  # 支持 watch 等长连接
    timeout server          30m  # 支持 watch 等长连接

frontend kubernetes-api
    bind 192.168.10.100:6443 # 关键:监听在 VIP 上
    # bind :::6443 v4v6       # 如果需要支持 IPv6
    mode tcp
    default_backend         kubernetes-api-backend

backend kubernetes-api-backend
    mode tcp
    # 负载均衡算法,leastconn 对长连接更友好
    balance     leastconn
    # 后端服务器列表
    server  master-1  192.168.10.11:6443 check port 6443 inter 2s fall 3 rise 2
    server  master-2  192.168.10.12:6443 check port 6443 inter 2s fall 3 rise 2
    server  master-3  192.168.10.13:6443 check port 6443 inter 2s fall 3 rise 2

极客坑点分析:

  • bind 192.168.10.100:6443: HAProxy 必须明确地监听在 VIP 上。但是,在非 MASTER 节点上,这个 VIP 是不存在的。为了让 HAProxy 在所有节点上都能正常启动,你需要启用内核参数 net.ipv4.ip_nonlocal_bind=1,允许进程绑定一个当前不存在的 IP 地址。这是一个非常重要的 OS 层面的配置。执行 sysctl -w net.ipv4.ip_nonlocal_bind=1 并写入 /etc/sysctl.conf 使其永久生效。
  • mode tcp: 明确使用四层模式,获得最高性能。不要在这里画蛇添足地使用 mode http
  • timeout client/server: API Server 有大量的 watch 长连接,用于组件间的事件通知。如果这个超时设置得太短(如默认的 50s),会导致 watch 连接被 HAProxy 强行断开,引发客户端频繁重连,增加 API Server 压力并可能导致事件丢失。设置一个较长的时间(如 30m)是必要的。
  • balance leastconn: roundrobin(轮询)算法对于短连接非常公平,但对于长连接场景,它可能导致某些服务器积累了大量长连接而另一些很空闲。leastconn(最少连接)会把新连接发往当前活动连接数最少的后端服务器,是处理 API Server 这种混合了长短连接流量的更优选择。
  • check 参数: inter 2s 表示每 2 秒探测一次。fall 3 表示连续 3 次失败后,将该后端标记为不可用。rise 2 表示连续 2 次成功后,重新将其标记为可用。这些参数提供了健康检查的灵敏度和容错性之间的平衡,防止因瞬时网络抖动导致后端被频繁摘除。

性能优化与高可用设计

除了基础配置,在生产环境中,我们还需要考虑更多的细节来提升性能和可用性。

  • Keepalived 的抢占策略:默认情况下,当一个高优先级的节点从故障中恢复后,会立即抢占 VIP,成为新的 MASTER。这会导致一次不必要的流量切换。在某些场景下,可以通过在 vrrp_instance 中添加 nopreempt 配置来禁用抢占,让当前 MASTER 节点继续服务,直到它自己发生故障。这是一种“稳定压倒一切”的策略,减少了网络抖动。
  • HAProxy 性能调优:对于大规模集群,需要调整 global 段的 maxconn 参数,并根据 CPU 核数配置 nbprocnbthread 以充分利用多核 CPU。同时,操作系统层面的 TCP/IP 协议栈调优(如增大文件句柄数 ulimit -n,调整 net.core.somaxconnnet.ipv4.tcp_max_syn_backlog 等)也至关重要。
  • 连接状态同步:一个经典的 HAProxy+Keepalived 架构的弱点是,当发生 VIP 漂移时,已建立的 TCP 连接状态不会同步到新的 MASTER 节点。这意味着所有现存的连接(包括那些重要的 watch 连接)都会被中断,客户端需要重新发起连接。虽然 K8s 的客户端组件都有重连机制,但这在切换瞬间会给新的 API Server 带来连接风暴。更高级的方案是使用 conntrackd 等工具来同步连接跟踪表,但这极大地增加了架构的复杂性,对于大多数场景来说,接受短暂的连接重置是更务实的权衡。
  • 避免脑裂(Split-Brain):在 VRRP 中,如果 MASTER 节点与其他节点之间的网络被分割,但它自己仍然在运行,它会继续持有 VIP。而 BACKUP 节点因为收不到心跳,也会选举出一个新的 MASTER。这就导致了网络中同时存在两个节点声称拥有同一个 VIP,即“脑裂”。为缓解此问题,可以配置多个冗余的心跳网络,或者通过一个“仲裁”机制(如 ping 一个稳定的网关地址)来作为选举的附加条件。

架构演进与落地路径

一个架构的成功与否,不仅在于其设计的优劣,还在于其能否平滑地演进和落地。

  1. 阶段一:单点 Master。这是所有实验和开发环境的起点。使用 kubeadm init 默认创建的集群就是这种形态。它简单快捷,但绝不能用于生产。
  2. 阶段二:添加 Master 节点并手动切换。在不引入 Keepalived/HAProxy 的情况下,可以先搭建多个 Master 节点,让它们加入集群(使用 kubeadm join --control-plane)。此时,可以在前端通过 DNS 或者手动修改 Kubelet 配置来指向其中一个 Master。当主 Master 故障时,通过手动修改 DNS 解析或配置来完成切换。这个阶段实现了数据层的高可用(etcd 集群),但控制平面的故障恢复时间(RTO)很长,依赖人工介入。
  3. 阶段三:引入 VIP 和负载均衡。在阶段二的基础上,部署 Keepalived 和 HAProxy,将所有客户端的入口统一到 VIP。这是本文所详述的生产级高可用架构。通过此改造,RTO 可以从小时级降低到秒级,实现自动故障转移。
  4. 阶段四:多地域/多数据中心容灾。对于有极高可用性要求的系统(如金融交易、核心计费),单数据中心的高可用是不够的。此时需要将 K8s 集群扩展到多个地理位置分散的数据中心。这通常涉及到更复杂的跨数据中心网络、etcd 的跨地域部署(需要仔细评估延迟),以及上层的全局流量管理器(GTM/GSLB)来实现基于 DNS 的智能流量调度。这已经超出了本文的范围,但它是当前架构的自然演进方向。

总而言之,通过 Keepalived 实现的 VIP 漂移和 HAProxy 提供的四层负载均衡,是构建自托管、生产级 Kubernetes 集群高可用控制平面的基石。这个方案虽然“传统”,但其原理清晰、组件成熟、社区支持广泛,并且不受任何云厂商锁定。深刻理解其背后的网络原理与工程细节,是每一位致力于构建可靠分布式系统的架构师与工程师的必备技能。

延伸阅读与相关资源

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