Kubernetes API Server 高可用负载均衡架构深度剖析:从 VIP 到四层代理

本文面向需要构建生产级高可用 Kubernetes 集群的中高级工程师与架构师。我们将深入探讨 Kubernetes 控制平面的核心——API Server 的高可用性与负载均衡方案。文章将从问题的根源出发,剖析其背后的网络与分布式系统原理,对比并实现基于 Keepalived+Haproxy 的经典架构,最终给出清晰的架构演进路径,帮助你在真实业务场景中做出正确的技术决策。

现象与问题背景

Kubernetes 中,API Server 是整个集群的“大脑”和唯一的入口。所有组件,包括 `kubectl` 命令行工具、`kubelet` 节点代理、控制器管理器(`controller-manager`)以及调度器(`scheduler`),都通过调用 API Server 的 RESTful 接口来读取或修改集群状态。这些状态的唯一持久化存储是 etcd。因此,API Server 的可用性直接决定了整个集群控制平面的生死。

在一个最简单的、单 Master 节点的集群中,API Server 进程一旦崩溃或其所在物理机宕机,会立刻引发灾难性后果:

  • 集群管理中断: 任何 `kubectl` 操作都会失败,无法部署新应用、查看 Pod 状态或管理集群资源。
  • 自动化控制失效: 控制器(如 Deployment Controller)无法接收到资源变化的通知,导致自动扩缩容、故障自愈等核心功能全部瘫痪。
  • 新节点无法加入: 新的 `kubelet` 无法向 API Server 注册。
  • 服务发现可能受影响: 依赖于 API Server watch 机制的服务发现组件(如 CoreDNS)无法及时更新 DNS 记录。

虽然此时已经运行的业务 Pod 不会立即死亡,但整个集群已经进入了一种“脑死亡”状态,无法响应任何变化。这种单点故障(Single Point of Failure, SPOF)在任何生产环境中都是不可接受的。因此,为 API Server 构建高可用(High Availability, HA)架构,并实现负载均衡以分散压力,是部署生产级 Kubernetes 集群的必要前提。

关键原理拆解

在进入具体实现前,我们必须回归到计算机科学的基础原理,理解支撑高可用负载均衡方案的底层机制。这有助于我们不仅知其然,更知其所以然。

(教授视角)

虚拟 IP (VIP) 与二层网络通信

要理解 Keepalived 的工作原理,我们必须先回到 TCP/IP 协议栈的底层。在同一个局域网(二层网络)内,设备间的通信依赖于 MAC 地址。IP 地址(三层)到 MAC 地址(二层)的转换由 地址解析协议(Address Resolution Protocol, ARP) 完成。

当主机 A(IP: 192.168.1.10)想与主机 B(IP: 192.168.1.11)通信时,它会先检查自己的 ARP缓存。如果找不到 1.11 对应的 MAC 地址,它会广播一个 ARP 请求:“谁是 192.168.1.11?请告诉我你的 MAC 地址。” 局域网内所有设备都会收到此请求,但只有主机 B 会响应一个 ARP 应答,包含自己的 MAC 地址。主机 A 收到后,更新本地 ARP 缓存,然后将数据包封装成以太网帧,目标 MAC 地址设为主机 B 的 MAC 地址,发送出去。

Keepalived 利用了这一机制。它基于 虚拟路由冗余协议 (VRRP) 工作。一组服务器(例如多个 K8s Master 节点)共享一个不属于任何物理网卡的“虚拟 IP”(VIP)。这组服务器中,只有一个处于 `MASTER` 状态,其余处于 `BACKUP` 状态。`MASTER` 节点会逻辑上持有这个 VIP,并不断地向外发送 VRRP 广播包,宣告自己的“存活”。

当 `MASTER` 节点宕机,`BACKUP` 节点在预设时间内收不到广播包,它们之间会根据优先级选举出一个新的 `MASTER`。新的 `MASTER` 会立即做一件至关重要的事情:广播一个 免费 ARP (Gratuitous ARP) 包。这个包的作用是向全网宣告:“现在,持有 VIP(例如 192.168.1.100)的 MAC 地址是我的 MAC 地址!”。交换机和网络中的其他设备收到这个包后,会强制更新自己的 ARP 缓存表,将 VIP 与新的 MAC 地址关联起来。这样,所有发往 VIP 的流量就无缝地切换到了新的 `MASTER` 节点上,完成了故障转移。

四层负载均衡 vs. 七层负载均衡

负载均衡器根据其工作的网络协议层级,主要分为四层和七层。

  • 四层负载均衡 (L4 LB): 工作在传输层(TCP/UDP)。它根据报文的源/目标 IP 地址和端口号进行转发决策,而不关心应用层的数据内容。它执行的是网络地址转换(NAT)。对于 Kubernetes API Server 这种基于 TLS 加密的 HTTPS 流量,L4 LB 只是透传 TCP 数据流,无需解密,性能极高,延迟极低。
  • 七层负载均衡 (L7 LB): 工作在应用层(HTTP/HTTPS)。它可以解析应用层协议的内容,例如 HTTP 的 Header、URL 路径、Cookie 等,从而实现更精细的路由策略(如基于 URL 路径的请求分发)。要做到这一点,它必须先进行 TLS 卸载(终止 TLS 连接),这会带来额外的计算开销和延迟,并使得负载均衡器成为安全管理的重地。

对于 API Server 而言,所有后端实例提供的服务完全相同,且流量已经过 TLS 加密。我们需要的仅仅是高效、可靠地将 TCP 连接分发到健康的后端节点。因此,四层负载均衡 是最适合的方案,它在性能、简单性和安全性上都具有明显优势。

系统架构总览

一个经典的、自建的(On-Premise)Kubernetes 高可用控制平面架构通常如下描述:

  • Master 节点 (3个或5个): 运行奇数个 Master 节点以满足 etcd 集群的容错需求。每个节点上都运行着 `kube-apiserver`, `kube-controller-manager`, `kube-scheduler` 和 `etcd` 实例。
  • etcd 集群: 3个或5个 `etcd` 实例组成一个 Raft 集群,确保数据的一致性与高可用。API Server 是无状态的,所有状态都存储在 etcd 中。
  • 高可用负载均衡层: 这是外部流量进入控制平面的入口。它由两个核心组件构成:
    1. Keepalived: 在所有 Master 节点上运行,共同管理一个 VIP。这保证了即使某个 Master 节点宕机,VIP 也能漂移到健康的节点上,确保入口地址的可用性。
    2. Haproxy: 在每个 Master 节点上运行一个本地 Haproxy 实例。所有发往本地 VIP 的流量,首先被内核的 IPVS/Netfilter 捕获,然后交给本地的 Haproxy。
  • 流量路径:
    1. 所有客户端 (`kubectl`, `kubelet` 等) 都配置为连接 VIP。
    2. 流量到达当前持有 VIP 的 `MASTER` 节点。
    3. 该节点的 Haproxy 接收到流量。
    4. Haproxy 根据其配置的负载均衡策略(如轮询)和健康检查结果,将流量转发给其中一个健康的 API Server 实例(可以是本地的,也可以是其他 Master 节点上的)。

这种架构的精妙之处在于,它将 VIP 提供的“入口高可用”与 Haproxy 提供的“服务负载均衡”结合了起来。即使 VIP 只在一个节点上生效,通过本地 Haproxy 的转发,实际的 API Server 负载可以被均匀地分发到所有健康的 Master 节点上,实现了真正的 Active-Active 负载均衡。

核心模块设计与实现

(极客工程师视角)

空谈理论没意思,我们直接上手配置。假设我们有三个 Master 节点,IP 分别为 `10.0.0.11`, `10.0.0.12`, `10.0.0.13`。我们计划使用的 VIP 是 `10.0.0.10`。API Server 监听在 `6443` 端口。

Keepalived 配置

在所有三个 Master 节点上,安装 Keepalived,并创建配置文件 `/etc/keepalived/keepalived.conf`。配置大同小异,主要区别在于 `priority` 和 `state`。

一个致命的坑点是:绝对不要只依赖 Keepalived 进程的死活来判断节点健康。你需要一个脚本来真实地探测 API Server 的健康状况。如果 API Server 挂了但 Keepalived 进程还在,VIP 就不会漂移,你的集群入口就黑洞了。

首先,创建一个健康检查脚本 `/etc/keepalived/check_apiserver.sh`:


#!/bin/bash
APISERVER_HEALTHZ_URL="https://127.0.0.1:6443/healthz"
APISERVER_DEST_IP="10.0.0.10" # VIP

# 使用curl探测本地API Server的健康检查端点
# --cacert, --cert, --key 指向用于apiserver通信的证书
# 如果健康(返回200 OK), 脚本退出码为0, Keepalived认为检查通过
curl --silent --show-error --cacert /etc/kubernetes/pki/ca.crt \
    --cert /etc/kubernetes/pki/apiserver-kubelet-client.crt \
    --key /etc/kubernetes/pki/apiserver-kubelet-client.key \
    $APISERVER_HEALTHZ_URL > /dev/null 2>&1

# 如果curl命令失败, 尝试ping VIP, 如果VIP还在本节点, 说明keepalived还未切换
# 此时应该让健康检查失败, 触发keepalived failover
if [ $? -ne 0 ]; then
    if ip addr show | grep -q $APISERVER_DEST_IP; then
        exit 1 # 本地API Server挂了, 且VIP还在, 强制触发切换
    else
        exit 0 # 本地API Server挂了, 但VIP已经不在了, 无需操作
    fi
else
    exit 0 # API Server 健康
fi

然后是 `keepalived.conf` 的配置。以 Master-1 (`10.0.0.11`) 为例:


global_defs {
   router_id K8S_MASTER_01
}

vrrp_script check_apiserver {
    script "/etc/keepalived/check_apiserver.sh"
    interval 3  # 每3秒检查一次
    weight -20  # 如果脚本失败,优先级降低20
    fall 2      # 连续2次失败,才认为服务故障
    rise 2      # 连续2次成功,才认为服务恢复
}

vrrp_instance VI_1 {
    state MASTER          # Master-1 初始为 MASTER
    interface ens192        # 根据你的网卡名修改
    virtual_router_id 51  # VRRP组ID,所有Master节点必须一致
    priority 101          # 优先级,MASTER最高

    authentication {
        auth_type PASS
        auth_pass your_secret_password # 简单的认证密码
    }

    virtual_ipaddress {
        10.0.0.10/24      # 定义VIP
    }

    track_script {
        check_apiserver
    }
}

对于 Master-2 和 Master-3,配置几乎一样,只需修改 `router_id`,将 `state` 改为 `BACKUP`,并降低 `priority`(例如,Master-2 为 100,Master-3 为 99)。这样,当 Master-1 的 `check_apiserver` 脚本失败时,它的有效优先级会从 101 降到 81,低于 Master-2 的 100,VIP 就会自动漂移到 Master-2。

Haproxy 配置

在所有 Master 节点上安装 Haproxy,配置文件 `/etc/haproxy/haproxy.cfg` 如下:


global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    mode    tcp             # 关键:设置为TCP模式,即四层负载均衡
    option  tcplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000

frontend kubernetes-apiserver
    bind *:8443            # Haproxy监听在8443端口,接收来自VIP的流量
    mode tcp
    default_backend kubernetes-master-nodes

backend kubernetes-master-nodes
    mode tcp
    balance roundrobin      # 负载均衡算法:轮询
    server master1 10.0.0.11:6443 check port 6443
    server master2 10.0.0.12:6443 check port 6443
    server master3 10.0.0.13:6443 check port 6443

这里的配置非常直接:

  • `mode tcp`:明确告诉 Haproxy 工作在四层,只做 TCP 流量转发,性能最高。
  • `balance roundrobin`:最简单的轮询算法,将请求依次分发给后端服务器。
  • `check port 6443`:Haproxy 会主动探测后端 `6443` 端口是否可达。如果某个 API Server 进程崩溃,端口不通,Haproxy 会自动将其从可用后端池中移除,不再向其转发流量。这是另一层核心的健康检查。

注意,我们需要将内核参数 `net.ipv4.ip_nonlocal_bind` 设置为 `1`,允许进程绑定不属于本地的 IP 地址。这是因为 Haproxy 需要在 VIP 漂移过来之前就启动并监听在 VIP 的端口上。

性能优化与高可用设计

我们已经有了一个可工作的架构,但魔鬼在细节里。一个健壮的生产系统需要考虑更多对抗性问题。

Trade-off 分析:Keepalived vs. 专用负载均衡器

我们选择的 Keepalived+Haproxy 方案是一种“去中心化”的负载均衡。它的优点是架构简单,不依赖外部硬件负载均衡器。但它也有权衡:

  • 优点:
    • 成本低: 无需购买昂贵的 F5 等硬件负载均衡器。
    • 无额外单点: 负载均衡能力与 Master 节点生命周期绑定,Master 节点本身就是高可用的。
  • 缺点:
    • 抢占与脑裂风险: VRRP 依赖网络广播。在复杂的网络环境中(例如网络抖动),可能出现短暂的“脑裂”,即多个节点都认为自己是 MASTER,导致 VIP 冲突。虽然 Keepalived 有保护机制,但这始终是一个潜在风险。
    • 性能耦合: 负载均衡器(Haproxy)与 API Server 运行在同一节点,共享 CPU 和网络资源,存在资源竞争。

替代方案是使用一组独立的、专用的 L4 负载均衡服务器。例如,两台服务器运行 Haproxy,并使用 Keepalived 在它们之间实现主备,这两台服务器再将流量分发给后端的 K8s Master 集群。这种方案将负载均衡层与计算层解耦,架构更清晰,性能隔离性更好,但增加了额外的服务器成本和运维复杂度。

etcd 的关键角色

API Server 本身是无状态的,这意味着任何一个 API Server 实例都可以处理任何请求。真正的状态存储在 etcd 中。因此,etcd 集群的健康和性能是整个控制平面性能的基石

etcd 使用 Raft 一致性算法,写操作必须经过 Leader 并同步到多数节点,这是一个对磁盘 I/O 非常敏感的过程。如果 etcd 部署在机械硬盘上,或者与其他高 I/O 应用共享磁盘,其性能会急剧下降,直接导致 API Server 的请求延迟飙升,整个集群响应缓慢。生产环境中,必须为 etcd 使用高性能的 SSD,最好是 NVMe SSD,并保证磁盘 I/O 隔离。

客户端行为

所有连接到 API Server 的客户端,如 `kubelet`,都应该配置连接 VIP 地址。当 Keepalived 发生主备切换时,现有的长连接(例如 `watch` 请求)会中断。Kubernetes 的客户端组件都内置了良好的重试机制,它们会自动重新连接到 VIP,新的连接将被路由到新的 `MASTER` 节点,服务得以恢复。这个中断时间通常在1-3秒,在大多数场景下是可以接受的。

架构演进与落地路径

一个技术方案的落地并非一蹴而就,合理的演进路径能有效控制风险和成本。

  1. 阶段一:单 Master 节点(开发/测试环境)

    对于个人实验、开发或非关键的测试环境,单 Master 节点是最简单快捷的部署方式。所有组件都在一台机器上。这个阶段的重点是快速验证功能,而非高可用。

  2. 阶段二:基于 VIP 的主备切换(入门级生产)

    这是向高可用迈出的第一步。部署多个 Master 节点,但只使用 Keepalived 实现 VIP 漂移。这种架构下,只有一个 API Server 在工作(Active-Passive),其他的处于热备状态。它解决了单点故障问题,但没有实现负载均衡,无法提升整个控制平面的吞吐量。适用于负载不高但对可用性有基本要求的场景。

  3. 阶段三:VIP + 本地负载均衡(推荐的自建方案)

    即本文详细介绍的 Keepalived + Haproxy 方案。它在实现入口高可用的同时,通过本地 Haproxy 将负载分发到所有 Master 节点,实现了 Active-Active 模式。这是自建数据中心(On-Premise)场景下,平衡了成本、复杂度和可靠性的甜点方案。

  4. 阶段四:云厂商托管负载均衡器(公有云环境)

    如果在 AWS、GCP、Azure 等公有云上部署 K8s,最佳实践是使用云厂商提供的托管负载均衡器服务(如 AWS NLB, GCP Internal TCP/UDP Load Balancing)。你只需创建一个内部负载均衡器,将其后端目标组指向所有的 Master 节点实例。云平台会负责 LB 自身的高可用和健康检查。这种方式极大地简化了运维,将专业问题交给专业平台,但会与特定云厂商产生绑定。

总结而言,为 Kubernetes API Server 构建高可用和负载均衡体系,是一个从理解底层网络协议,到精通上层组件配置,再到权衡不同架构优劣的系统工程。本文所介绍的 Keepalived+Haproxy 方案,是经受了大量生产环境考验的经典模型,掌握其原理与实现,将是你构建稳固 Kubernetes 基石的关键一步。

延伸阅读与相关资源

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