Kubernetes 网络服务暴露:从 Service 到 Ingress 与 BGP/DSR
引言:Kubernetes 网络的核心挑战与解决方案
Kubernetes 极大地简化了容器化应用的部署和管理,但在网络层面,它引入了新的挑战。Pods 是短暂的,其 IP 地址是动态分配的,这使得直接访问 Pod 变得不可靠。为了向集群内部和外部的客户端提供稳定、可靠的服务访问入口,并有效地将流量分发到健康的后端 Pod 实例,Kubernetes 提供了一系列网络抽象和机制。
本文将深入探讨 Kubernetes 中实现网络负载均衡和服务暴露的关键技术,从基础的 L4 Service 到更高级的 L7 Ingress,再到适用于特定场景(如裸金属环境或高性能需求)的 BGP 和 DSR 技术。我们将剖析它们的工作原理、解决的问题以及如何在实际场景中应用。
Kubernetes 网络负载均衡基础
1. 为何需要负载均衡?
- Pod 的动态性: Pod IP 地址是动态分配的,且 Pod 实例可能随时被创建或销毁。直接依赖 Pod IP 进行访问是不可靠的。
- 服务发现与稳定性: 需要一个稳定的访问点(IP 地址和端口),客户端可以通过这个固定入口访问服务,而无需关心后端 Pod 的具体变化。
- 流量分发: 需要将传入的请求有效地分发到多个健康的后端 Pod 实例上,以实现负载均衡和高可用。
- 自动化: 后端 Pod 实例的变化(增减、健康状态)需要被自动感知,并实时更新路由规则。
2. L4 负载均衡: Service
核心对象: Service
(Kubernetes API Object)
作用: Service 是 Kubernetes 中实现 L4 (TCP/UDP) 负载均衡和集群内部服务发现的基础。它为一组功能相同的 Pod(通过标签选择器 selector
关联)提供了一个统一、稳定的虚拟 IP 地址(ClusterIP
)和端口。客户端(集群内的其他 Pod 或进程)只需访问 Service 的 ClusterIP:Port
,流量就会被自动转发到后端某个健康的 Pod 上。
实现者: kube-proxy
kube-proxy
是一个运行在 Kubernetes 集群中每个 Node 上的网络代理和负载均衡器。它并不是用户流量的直接代理,而是通过修改节点上的网络规则(iptables 或 IPVS)来实现 Service 的虚拟 IP 转发逻辑。
- 工作模式:
- 监视 API Server:
kube-proxy
持续监听(Watch)API Server 上Service
和EndpointSlice
(或旧版的Endpoints
)资源的变化。EndpointSlice
包含了 Service 关联的所有健康 Pod 的实际 IP 地址和端口。 - 更新网络规则: 当检测到变化时,
kube-proxy
会根据其配置的模式(iptables 或 IPVS)在当前节点上更新相应的网络规则。
- 监视 API Server:
kube-proxy
模式详解
iptables
模式 (曾经的默认,现在 IPVS 更常见)- 原理: 利用 Linux 内核的
netfilter
框架和iptables
工具。为每个 Service 创建一系列iptables
规则。当数据包到达节点,目标是 Service 的ClusterIP:Port
时,iptables
规则(通常在nat
表的PREROUTING
或OUTPUT
链触发,跳转到KUBE-SERVICES
链,再到具体的KUBE-SVC-*
链,最后到KUBE-SEP-*
链)会执行 DNAT (Destination Network Address Translation),将数据包的目标 IP 和端口修改为选中的后端 Pod 的 IP 和端口。同时,iptables
也会进行连接跟踪 (conntrack
) 来确保同一连接的后续包被转发到同一个 Pod。负载均衡策略通常是随机选择一个后端 Pod。 - 优点: 成熟稳定,几乎所有 Linux 发行版都支持。
- 缺点:
- 性能: 当 Service 和 Endpoints 数量巨大时,
iptables
规则链会变得非常长,内核需要线性查找匹配规则,导致性能下降,尤其是在高并发连接场景下。 - 规则更新: 更新大量规则时可能引入短暂的延迟或性能抖动。
- 性能: 当 Service 和 Endpoints 数量巨大时,
- 原理: 利用 Linux 内核的
IPVS
(IP Virtual Server) 模式 (当前推荐和许多发行版的默认)- 原理: 利用 Linux 内核内置的高性能 L4 负载均衡模块
IPVS
。kube-proxy
会为每个 Service 创建一个 IPVS 虚拟服务器 (Virtual Server, VS),并将后端 Pod IP:Port 配置为真实服务器 (Real Server, RS)。IPVS 使用高效的哈希表来存储和查找服务规则,而不是线性规则链。当数据包到达时,IPVS 直接在内核中进行高效的查找和转发决策(DNAT)。 - 优点:
- 高性能: 哈希查找使得即使在大量 Service 的情况下也能保持接近 O(1) 的查找效率,性能远超
iptables
模式。 - 更多负载均衡算法: 支持多种负载均衡算法,如轮询 (rr)、最少连接 (lc)、加权轮询 (wrr)、源哈希 (sh) 等,可以通过 Service 注解进行配置。
- 连接跟踪优化: IPVS 的连接同步机制通常更优。
- 高性能: 哈希查找使得即使在大量 Service 的情况下也能保持接近 O(1) 的查找效率,性能远超
- 缺点: 需要节点内核支持 IPVS 模块(现代 Linux 内核通常都支持)。
- 原理: 利用 Linux 内核内置的高性能 L4 负载均衡模块
Service 类型
Kubernetes Service 有多种类型,决定了服务如何被暴露:
ClusterIP
: 默认类型。为 Service 分配一个集群内部唯一的虚拟 IP 地址。这个 IP 地址只能在集群内部访问(Pod 到 Service,或 Node 到 Service)。这是实现集群内部服务间通信的基础。NodePort
: 在ClusterIP
的基础上,额外在每个集群节点上暴露一个静态端口(范围通常是 30000-32767)。外部客户端可以通过访问任意节点IP:NodePort
来访问该 Service。流量到达节点后,会被转发给 Service 的 ClusterIP,最终由kube-proxy
(iptables/IPVS) 转发给后端 Pod。主要用于开发测试或需要直接暴露 TCP/UDP 服务给外部的简单场景。LoadBalancer
: 通常用于公有云环境 (AWS, GCP, Azure 等)。在NodePort
的基础上,请求云平台自动创建一个外部负载均衡器 (ELB)。这个 ELB 会获得一个公网 IP 地址,并将流量转发到所有集群节点的NodePort
上。这是将服务标准地暴露给公网用户的方式。云平台的cloud-controller-manager
组件负责与云 API 交互来创建和管理 ELB。对于裸金属或私有云环境,需要像 MetalLB 这样的附加组件来提供此功能。ExternalName
: 一个特例,它不是提供负载均衡,而是将 Service 名称映射到集群外部的一个 DNS 名称。当查询这个 Service 时,集群 DNS (如 CoreDNS) 会返回配置的外部 DNS 名称的 CNAME 记录。
3. 网络五元组 (Network 5-Tuple)
理解 L4 负载均衡离不开网络基础。网络五元组是唯一标识一个 TCP/IP 网络连接的关键信息:
- 源 IP 地址 (Source IP)
- 源端口号 (Source Port)
- 目的 IP 地址 (Destination IP)
- 目的端口号 (Destination Port)
- 协议号 (Protocol) (例如,TCP 协议号为 6, UDP 协议号为 17)
重要性:
- 唯一标识连接: 网络设备(路由器、防火墙、负载均衡器)使用五元组来区分不同的网络会话。
- 连接跟踪 (
conntrack
): Linux 内核(以及iptables
和IPVS
)使用五元组来跟踪 TCP/UDP 连接的状态(如 NEW, ESTABLISHED, FIN_WAIT),确保同一连接的包被正确处理。 - NAT (网络地址转换): NAT 设备(包括
kube-proxy
执行 DNAT 时)需要维护五元组的映射关系,以便正确地转换地址和端口,并将返回的流量送回正确的源。 - 状态防火墙: 基于连接状态(通过跟踪五元组)进行访问控制。
- L4 负载均衡: 负载均衡器根据五元组(特别是目的 IP/Port)将流量分发到后端服务器,并可能基于源 IP/Port(如源哈希算法)来维持会话一致性。
L7 负载均衡: Ingress
虽然 Service 提供了基础的 L4 负载均衡,但对于现代 Web 应用和微服务架构中常见的 HTTP/HTTPS 流量,它存在一些局限性:
- 端口管理复杂: 每个需要暴露的 HTTP/S 服务如果都用
NodePort
或LoadBalancer
Service,会消耗大量端口或外部 IP,管理困难且成本高。 - 缺乏应用层路由: Service 无法理解 HTTP 协议,不能基于域名 (Host header) 或 URL 路径 (Path) 来进行智能路由。
- TLS 管理分散: 如果需要 HTTPS,TLS 证书和加解密逻辑需要在每个后端应用 Pod 内部处理,管理和更新证书非常麻烦。
为了解决这些问题,Kubernetes 引入了 Ingress。
核心对象: Ingress
(Kubernetes API Object)
作用: Ingress 资源定义了一系列规则,描述了 HTTP/HTTPS 流量应该如何从集群外部路由到集群内部的 Service
。它充当了集群流量的“智能”入口,提供了更丰富的应用层路由能力。你可以把它想象成一个虚拟主机或反向代理的配置蓝图。
关键点: Ingress
资源本身只是规则的集合,不具备处理流量的能力。
实现者: Ingress Controller
Ingress Controller 是一个实际运行在集群中的应用程序(通常以 Deployment 或 DaemonSet 的形式部署),它负责读取并实现 Ingress
资源中定义的规则。
- 不是 K8s 内置组件: 你需要单独选择并部署一个 Ingress Controller,常见的有:
ingress-nginx
(基于 Nginx,社区维护最广泛)Traefik Ingress Controller
(基于 Traefik Proxy)
HAProxy Ingress
(基于 HAProxy)- 云厂商提供的 Ingress Controller (如 AWS Load Balancer Controller, GKE Ingress Controller),它们通常能更紧密地集成云平台的 L7 负载均衡器。
- 其他如 APISIX Ingress, Contour (基于 Envoy) 等。
- 工作原理 (控制循环 - Control Loop):
- Observe (监听): Ingress Controller 持续监听(Watch)Kubernetes API Server 上的
Ingress
,Service
,EndpointSlice
,Secret
(用于 TLS 证书) 等资源的变化。 - Compare (比较): 获取集群中定义的 Ingress 规则(期望状态),并与自身当前管理的底层反向代理(如 Nginx)的配置(实际状态)进行比较。
- Act (行动): 如果期望状态与实际状态不符(例如,用户创建了一个新的 Ingress 规则),Ingress Controller 会动态地生成新的代理配置文件(如
nginx.conf
),并应用这些配置(例如,通过nginx -s reload
平滑地重新加载 Nginx 配置)。
- Observe (监听): Ingress Controller 持续监听(Watch)Kubernetes API Server 上的
- 流量处理:
- Ingress Controller 通常通过一个
LoadBalancer
或NodePort
类型的 Service 将自身暴露给外部网络。 - 外部 HTTP/S 流量到达 Ingress Controller 的 Pod。
- Ingress Controller 根据请求的 Host header 和 URL Path,匹配
Ingress
规则。 - 将请求代理 (Proxy) 到规则指定的后端
Service
(的 ClusterIP)。 - 后续从
Service
到最终 Pod 的 L4 转发仍然由kube-proxy
(iptables/IPVS) 处理。
- Ingress Controller 通常通过一个
Ingress 关键特性
- Host-based Routing (基于主机的路由): 根据请求头中的
Host
字段(域名)将流量转发到不同的后端服务。例如,api.example.com
指向 API 服务,shop.example.com
指向商店服务。 - Path-based Routing (基于路径的路由): 根据请求 URL 中的路径将流量转发到不同的后端服务。例如,
example.com/api
指向 API 服务,example.com/ui
指向 UI 服务。 - TLS Termination (TLS 终止): Ingress Controller 可以配置 TLS 证书(存储在 K8s
Secret
中),处理传入的 HTTPS 请求(解密),然后将未加密的 HTTP 流量转发给后端服务。这使得证书管理集中化,并减轻了后端应用的负担。 - 资源共享与成本效益: 多个不同的 Service 可以共享同一个 Ingress Controller 和同一个外部入口点(通常是一个
LoadBalancer
Service),极大地节省了公网 IP 和负载均衡器的成本。 - 高级功能 (依赖 Controller 实现): 不同的 Ingress Controller 提供不同的扩展功能,例如 URL 重写 (
rewrite-target
)、请求/响应头修改、认证集成 (OAuth2, Basic Auth)、速率限制、灰度发布 (Canary Release) 等,通常通过Ingress
资源的annotations
来配置。
工作流程与配置示例

结合图片中的流程,一个典型的使用 Ingress 的工作流如下:
创建应用 Deployment: 定义你的应用程序容器和所需的副本数量。
1
2
3
4# 示例:创建一个名为 my-app 的 Deployment,使用 nginx 镜像,3个副本
kubectl create deployment my-app --image=nginx --replicas=3
# (确保 Pod 有合适的标签,例如 app=my-app)
kubectl label deployment my-app app=my-app创建 Service: 为 Deployment 创建一个
ClusterIP
类型的 Service。因为流量将通过 Ingress Controller 代理进来,通常不需要直接从外部访问 Service IP。Service 的作用是提供一个稳定的内部 IP 和端口,并让 Ingress Controller 和kube-proxy
能够发现后端 Pods。1
2# 暴露 Deployment 'my-app' 的容器端口 80,创建名为 'my-app-service' 的 ClusterIP Service
kubectl expose deployment my-app --port=80 --target-port=80 --name=my-app-service --type=ClusterIP对应的 Service YAML 可能如下:
1
2
3
4
5
6
7
8
9
10
11
12apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app # 确保这里的标签与 Deployment Pod 的标签匹配
ports:
- protocol: TCP
port: 80 # Service 监听的端口 (供 Ingress Controller 指向)
targetPort: 80 # Pod 容器实际监听的端口
type: ClusterIP部署 Ingress Controller: 如果集群中还没有部署,需要先部署一个 Ingress Controller (例如,使用 Helm 安装
ingress-nginx
)。这一步通常只需要做一次。定义 Ingress 资源: 创建一个 Ingress 对象,定义路由规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35apiVersion: networking.k8s.io/v1 # 使用当前推荐的 API 版本
kind: Ingress
metadata:
name: example-ingress
# Annotations 用于向特定的 Ingress Controller 提供额外的配置指令
annotations:
# 示例:对于 nginx-ingress,指定 URL 重写规则
nginx.ingress.kubernetes.io/rewrite-target: /$2
# 示例:如果集群中有多个 Ingress Controller,明确指定使用哪一个
# kubernetes.io/ingress.class: "nginx"
spec:
# ingressClassName: nginx # (v1.18+) 另一种更标准的方式指定 Ingress Controller
rules:
- host: "myapp.example.com" # 基于域名的路由
http:
paths:
- path: /foo(/|$)(.*) # 基于路径的路由 (匹配 /foo, /foo/, /foo/...)
# pathType 定义路径匹配方式:
# - Prefix: 前缀匹配 (常用)
# - Exact: 精确匹配
# - ImplementationSpecific: 由 Ingress Controller 决定
pathType: Prefix
backend:
service:
# 指向上面创建的 Service
name: my-app-service
port:
# 指向 Service 暴露的端口 (spec.ports.port)
number: 80
# (可选) 添加 TLS 配置来启用 HTTPS
tls:
- hosts:
- myapp.example.com
# secretName 引用一个包含 TLS 证书和私钥的 Kubernetes Secret
secretName: myapp-tls-secret关键字段解释:
apiVersion: networking.k8s.io/v1
: 当前推荐使用的 API 版本。kind: Ingress
: 资源类型。metadata.annotations
: 为特定的 Ingress Controller 提供额外配置。键名通常包含 Controller 的名称(如nginx.ingress.kubernetes.io/
)。spec.ingressClassName
: (v1.18+) 显式指定处理此 Ingress 的 Controller 名称,是比 annotation 更标准的方式。spec.rules
: 包含路由规则的列表。spec.rules[].host
: 可选,用于基于 Host header 进行路由。如果省略,规则适用于所有到达 Ingress Controller IP 的请求。spec.rules[].http.paths
: 定义路径及其对应的后端服务。spec.rules[].http.paths[].path
: URL 路径。可以使用正则表达式(取决于 Controller 支持)。spec.rules[].http.paths[].pathType
: 路径匹配类型 (Prefix
,Exact
,ImplementationSpecific
)。Prefix
是最常用的。spec.rules[].http.paths[].backend.service.name
: 后端 Service 的名称。spec.rules[].http.paths[].backend.service.port.number
: 后端 Service 暴露的端口号。spec.tls
: 可选,用于配置 TLS 终止。需要指定域名列表 (hosts
) 和包含证书/私钥的secretName
。
当这个 Ingress 资源被创建后,运行中的 Ingress Controller (假设是 Nginx Ingress Controller 且它被配置为处理这个 Ingress 或默认处理所有 Ingress) 会:
- 检测到新的 Ingress 资源。
- 解析规则,发现需要将
myapp.example.com/foo/*
的流量路由到my-app-service
的 80 端口。 - 如果配置了 TLS,会从
myapp-tls-secret
Secret 中获取证书和私钥。 - 生成新的 Nginx 配置(包含 server block, location block, SSL 配置等)。
- 重新加载 Nginx 配置 (
nginx -s reload
)。
现在,当外部客户端访问 https://myapp.example.com/foo/bar
时:
- DNS 将
myapp.example.com
解析到 Ingress Controller 的外部 IP (来自LoadBalancer
Service)。 - 请求到达 Ingress Controller Pod。
- Nginx 根据 Host (
myapp.example.com
) 和 Path (/foo/bar
) 匹配到对应的location
块。 - Nginx 执行 TLS 解密。
- Nginx 根据
rewrite-target
注解(如果配置了)可能修改 URL 路径。 - Nginx 将请求通过 HTTP 代理到
my-app-service
的 ClusterIP 的 80 端口。 - 节点上的
kube-proxy
(IPVS/iptables) 拦截到发往my-app-service
ClusterIP 的流量。 kube-proxy
将流量 DNAT 到my-app
Deployment 的某个健康 Pod 的 IP 的 80 端口。- Pod 内的 Nginx 应用处理请求。
对比: L4 Service (LoadBalancer) vs. L7 Ingress

特性 | L4 Service (LoadBalancer 类型) | L7 Ingress (通过 Ingress Controller) |
---|---|---|
核心层级 | L4 (传输层 - TCP/UDP) | L7 (应用层 - HTTP/HTTPS) |
架构 | 每个 Service 独占一个外部 LB -> NodePort -> Pod | 共享外部 LB -> NodePort -> Ingress Controller -> Service -> Pod |
资源成本 | 高 (每个 Service 一个外部 LB 实例,成本昂贵) | 低 (多个 Service 共享一个外部 LB 和 Ingress Controller) |
IP/DNS 管理 | 复杂 (每个 LB 一个公网 IP/DNS 记录) | 简单 (少数公网 IP/DNS 指向共享 LB/Ingress Controller) |
TLS 处理 | 分散 (通常在后端 Pod 应用内部处理) | 集中 (在 Ingress Controller 层处理 TLS Termination) |
路由能力 | 基础 IP/Port 路由 | 丰富 L7 路由 (基于 Host, Path, Header 等) |
协议支持 | TCP/UDP | 主要 HTTP/HTTPS (部分 Controller 通过 TCP/UDP Service 暴露自身,或支持 L4 代理) |
网络跳数 | Client -> LB -> Node -> Pod | Client -> LB -> Node -> Ingress Controller Pod -> Node -> Pod (增加一跳) |
系统复杂性 | 相对简单 (依赖云厂商或 MetalLB) | 增加 (需要额外部署、配置和维护 Ingress Controller) |
功能丰富度 | 有限 | 高 (重写、认证、限流、WAF 集成等,取决于 Controller) |
结论:
- 对于 HTTP/HTTPS 服务,L7 Ingress 通常是更优、更经济、更灵活的选择,尤其是在需要暴露多个 Web 服务或 API 时。
- 对于 非 HTTP/HTTPS 的 TCP/UDP 服务(如数据库、消息队列、游戏服务器),或者对网络延迟极其敏感、不需要 L7 功能的场景,L4 LoadBalancer Service (配合云 LB 或 MetalLB) 可能更直接、更合适。
高级服务暴露技术
除了标准的 Service 和 Ingress,Kubernetes 生态系统还发展出更高级的技术来应对特定场景的需求,例如在裸金属环境中提供 LoadBalancer 功能,或者追求极致的 L4 性能。
1. 传统应用网络拓扑与演进到 Kubernetes Ingress

传统分层负载均衡架构
在 Kubernetes 出现之前,典型的 Web 应用部署通常采用多层负载均衡架构:
- 全局层 (GTM/DNS): 使用智能 DNS 或全局流量管理器 (GTM) 进行跨地域/跨数据中心的流量调度,基于用户位置、延迟、健康状况等将用户导向最近或最合适的区域入口。
- 区域接入层 (Web Tier LB): 每个区域部署面向公网的 L4/L7 负载均衡器,负责接收外部流量、SSL/TLS 卸载、基础安全防护 (WAF)、并将流量分发给下一层。
- 应用层 (App Tier LB): 更靠近应用实例的负载均衡器,负责更细粒度的服务发现、负载均衡策略(轮询、最少连接等)、会话保持。
- 后端实例 (Instances): 实际运行业务逻辑的应用服务器。
痛点: 这种架构虽然成熟,但在动态、快速迭代的环境中显得笨重。配置复杂、变更流程长、弹性不足,且与应用的生命周期(部署、扩缩容)解耦,需要大量手动操作或复杂的自动化脚本。
Kubernetes Ingress Controller:云原生网关
Kubernetes Ingress 和 Ingress Controller 的出现,旨在以云原生的方式解决传统 LB 的痛点:
- 动态配置与自动化: Ingress Controller 通过 K8s API 自动感知应用和路由规则的变化,动态更新代理配置,无需手动干预,紧密贴合应用的生命周期。
- 成本效益: 通过共享 Ingress Controller 和外部入口点,避免为每个服务创建昂贵的外部 LB。
- 统一入口与标准化: 提供标准的 API (
Ingress
资源) 来定义 L7 路由规则,简化管理。 - 丰富路由能力: 支持 Host/Path 路由、TLS 终止等 L7 特性。
Ingress Controller 的本质: 它是一个运行在 K8s 集群内的反向代理/负载均衡器,它将 K8s 的声明式配置 (Ingress 资源) 转化为底层代理引擎 (Nginx, Envoy, HAProxy 等) 的具体配置。
Ingress Controller 的构建与工作原理(回顾)
图片下半部分展示了构建一个功能完备的 Ingress Controller(特别是与云平台或底层网络集成时)可能涉及的关键步骤:
a. 复用/管理外部负载均衡器接口 (获取入口 VIP)
- 目标: 为 Ingress Controller Pods 创建一个稳定的、可从外部访问的入口点 (VIP)。
- 机制:
- 云环境: Ingress Controller 通常会创建一个
Service
类型为LoadBalancer
。K8s 的cloud-controller-manager
会调用云厂商 API 创建一个外部 LB,并将其公网 IP 写回 Service 状态。Ingress Controller Pod 接收来自这个云 LB 的流量。 - 裸金属/私有云: 需要像 MetalLB 这样的组件。MetalLB 会负责分配 VIP,并通过 Layer 2 (ARP/NDP) 或 BGP 模式将 VIP 宣告出去,使得流量能够到达运行 Ingress Controller 的节点上。
- 云环境: Ingress Controller 通常会创建一个
- 接口抽象: 图片中提到的
EnsureLoadBalancer
,GetLoadBalancer
,UpdateLoadBalancer
等函数签名,代表了与底层 LB(云 LB 或 MetalLB管理的 VIP)交互的接口抽象。Ingress Controller(或相关组件)通过这些接口确保外部流量能正确导入。
b. 定义 Informer,监控 K8s 资源变化 (感知期望状态)
- 目标: 实时感知
Ingress
,Service
,EndpointSlice
,Secret
等资源的变化。 - 机制: 使用
client-go
库的 Informer 机制。- Informer 与 API Server 建立 Watch 连接,高效地接收资源变更事件。
- 维护本地缓存 (Store),减少对 API Server 的直接请求。
- 注册事件处理函数 (
AddFunc
,UpdateFunc
,DeleteFunc
)。 - 事件触发时,通常将变更对象的 key 放入工作队列 (Work Queue),实现解耦和缓冲。
c. 启动 Worker,处理队列中的变更事件 (调和实际状态)
- 目标: 从工作队列中取出变更项,执行实际的配置更新逻辑(Reconciliation Loop)。
- 机制:
- Worker Goroutine 从队列获取 key。
- 从 Informer 缓存中获取最新的资源对象。
- 根据资源对象(特别是 Ingress 规则)计算出底层代理(如 Nginx)的期望配置。
- 生成新配置文件(如
nginx.conf
)。 - 应用配置到代理实例(如
nginx -s reload
)。 - (可选) 更新 DNS: 如果集成了 ExternalDNS,调用 DNS API 更新域名指向 VIP 的记录。
- (可选) 更新 Ingress 状态: 将 VIP 或主机名写回 Ingress 资源的
status.loadBalancer.ingress
字段。
这个持续的“监听-入队-处理-更新”循环,确保了 Ingress Controller 管理的代理配置始终与 Kubernetes 集群中定义的 Ingress 资源状态保持一致。
2. BGP 在 Kubernetes 数据中心的应用
在裸金属 (Bare Metal) 或私有云环境中,没有现成的云厂商 LoadBalancer 服务。同时,在追求高性能、低延迟、与现有网络深度集成的场景下,即使在云环境,也可能希望避免或优化 Kubernetes 默认的网络模式(如 Overlay 网络和 NAT)。BGP (Border Gateway Protocol) 在这些场景下扮演着关键角色。
BGP 解决了什么问题?
- 提供
LoadBalancer
Service 实现: 像 MetalLB 可以使用 BGP 模式,将分配给LoadBalancer
Service 的 External IP 地址直接宣告到物理网络中。物理路由器学习到这些路由后,可以将外部流量直接路由到能够处理该 Service 的 Kubernetes 节点上。 - 消除或减少 NAT: 当 Service IP 或 Pod IP 被直接宣告到物理网络后,外部流量可以保留原始客户端源 IP 地址到达目标节点或 Pod,避免了
kube-proxy
或 CNI 可能引入的 SNAT,简化了网络路径,便于审计和访问控制。 - 高性能 Pod 网络 (配合 CNI): CNI 插件如 Calico 可以配置为 BGP 模式。在这种模式下,每个节点将其负责的 Pod CIDR(地址段)通过 BGP 宣告给物理网络(通常是 ToR 交换机)。这使得:
- 节点间 Pod 通信可以直接使用 Pod IP 进行路由,无需 IPIP 或 VXLAN 封装,降低了网络开销和延迟。
- Pod 访问外部网络时,其源 IP (Pod IP) 在物理网络中是可路由的,可以在数据中心边界进行集中 NAT,而不是在每个 K8s 节点上进行 SNAT。
- 与物理网络策略集成: 物理网络设备(路由器、防火墙)可以直接“看到”Kubernetes 的 Service IP 和 Pod IP,可以基于这些真实的 IP 地址应用更精细化的网络策略(QoS, ACL 等)。
BGP 路由宣告的原理与过程
BGP 是互联网和大型数据中心的核心路由协议,用于在不同的自治系统 (AS - Autonomous System) 之间或内部交换网络可达性信息 (NLRI),即 IP 地址前缀。
在 Kubernetes 场景下,宣告过程通常如下:
部署 BGP Agent/Speaker: 在 Kubernetes 集群中部署能够运行 BGP 协议的组件。
- MetalLB: 运行
speaker
DaemonSet,每个(或部分)节点上的 Speaker Pod 负责宣告分配给本节点的 Service VIP。 - Calico:
calico-node
DaemonSet 中通常包含 BGP 功能(基于 Bird 或 GoBGP),负责宣告本节点的 Pod CIDR 和可能的 Service IP (如果配置了)。
- MetalLB: 运行
配置 BGP Peering (对等关系): 管理员需要配置 K8s 节点上的 BGP Speaker 与物理网络中的路由器(通常是 ToR - Top of Rack 交换机)建立 BGP 会话。关键配置包括:
- 自治系统号 (ASN): 为 K8s 集群(或每个节点)和物理路由器分配合适的 ASN。可以在同一 ASN 内建立 iBGP (内部 BGP) 会话,或在不同 ASN 间建立 eBGP (外部 BGP) 会话。数据中心内部常用 iBGP 或 eBGP。
- Neighbor (邻居) 地址: 在 K8s BGP Agent 上配置 ToR 路由器的 IP 地址,反之亦然。
- 认证 (可选): 配置 MD5 密码增强安全性。
触发宣告:
- Service IP (MetalLB): 当
LoadBalancer
Service 被分配 External IP 后,MetalLB Controller 会选择一个或多个节点承载该 VIP,并通知这些节点上的 Speaker 宣告此/32
的主机路由。 - Pod IP (Calico): 当 Calico 为节点分配 Pod CIDR 后,该节点上的 BGP Agent 会自动宣告这个前缀(如
/24
或/26
)。
- Service IP (MetalLB): 当
发送 BGP UPDATE 消息: K8s 节点上的 BGP Speaker 向其 Peer (ToR 路由器) 发送 BGP UPDATE 消息,包含:
- NLRI: 要宣告的 IP 前缀 (e.g.,
198.51.100.5/32
或10.244.1.0/24
)。 - Path Attributes (路径属性):
- AS_PATH: 路由经过的 AS 列表,用于防环和选路。
- NEXT_HOP: 极其重要。指示到达目标前缀的下一跳路由器 IP 地址。当 K8s 节点宣告 Service IP 或 Pod CIDR 时,它会将 NEXT_HOP 设置为自身(节点)的 IP 地址。
- ORIGIN: 路由来源。
- MED, Local Preference (可选): 用于影响路径选择。
- NLRI: 要宣告的 IP 前缀 (e.g.,
物理路由器处理宣告: ToR 路由器收到 UPDATE 消息后:
- 验证并根据路由策略 (Route Policy / Route Map) 决定是否接受。
- 如果接受,将路由(例如
198.51.100.5/32 via <K8s_Node_IP>
)添加到 BGP 路由表。 - 通过 BGP 决策过程选出最优路径,并可能将其安装到全局路由表 (FIB - Forwarding Information Base) 中。
- 路由器可能将此路由再次宣告给其他 BGP Peer(如上行 Spine 交换机),使整个网络知道如何到达 K8s 的资源。
核心机制: 物理路由器通过 BGP 学习到,要想到达某个 K8s Service IP 或 Pod IP,需要将数据包直接发送到宣告该路由的那个 K8s 节点的 IP 地址 (NEXT_HOP)。
边缘路由器配置示例 (概念性)
以下是在 ToR 交换机上配置与 K8s 节点建立 BGP Peering 的通用示例 (类 Cisco NX-OS/Arista EOS 语法):
1 |
|
关键配置: neighbor <IP> remote-as <ASN>
是核心。确保 K8s 端(MetalLB/Calico 配置)也做了相应的 Peer 配置。
DNS 与 BGP 宣告的 IP
BGP 负责让 Service 的 External IP 在网络层面可达。但用户通常通过域名访问服务。因此,你需要在 DNS 服务中创建记录(如 A 记录),将域名指向这个通过 BGP 宣告的 IP 地址。
自动化: ExternalDNS 这个 Kubernetes 控制器可以自动完成这个过程。它监控 Service (或 Ingress) 资源,当发现带有 External IP 和特定注解的 Service 时,会自动调用 DNS 提供商(如 Route 53, Cloudflare, CoreDNS)的 API 来创建或更新 DNS 记录。
总结: BGP 使得 IP 可路由,DNS 使得域名可解析到该 IP。两者协同工作。
3. L4 DSR (Direct Server Return) 负载均衡架构

在需要处理极大 L4 流量(特别是响应流量远大于请求流量的场景,如视频流、大文件下载)时,传统的负载均衡器(包括 kube-proxy
或标准云 LB)可能会因为需要同时处理进出流量而成为性能瓶颈。DSR (Direct Server Return) 是一种优化技术,旨在解决这个问题。
DSR 核心思想
非对称路由:
- 请求路径: Client -> Load Balancer (Director) -> Real Server (Backend Pod)
- 响应路径: Real Server (Backend Pod) -> Client (直接返回,绕过 Load Balancer)
负载均衡器 (Director) 只负责接收客户端请求,进行调度决策,并将请求转发给后端服务器。而后端服务器在处理完请求后,直接将响应数据包发送回客户端,不再经过负载均衡器。
Kubernetes 中的 L4 DSR 架构
该图展示了一种在 Kubernetes 中实现 L4 DSR 的架构,通常依赖 IPVS 的 DSR 模式(也称为 DR - Direct Routing 模式)。
架构组件:
- Client: 发起请求。
- VIP (Virtual IP Address): 暴露给客户端的服务入口 IP。
- Router: 物理网络路由器,通常配置 ECMP (Equal-Cost Multi-Path),将发往 VIP 的流量负载均衡到多个 L4 LB 节点。通过 BGP 从 L4 LB 节点学习 VIP 的可达性。
- k8s minion node (L4 LB 层 - Director): 运行 DSR Director 角色的 K8s 节点。
- Elb-Director (k8s pod): 控制平面组件,监听 K8s Service/Endpoints,获取 VIP 和后端 Pod IP 列表。通过 Netlink 接口配置本节点的 Linux 内核 IPVS 模块。
- Linux Kernel / IPVS: 实际处理请求转发。收到目标为 VIP 的包后,根据调度算法选择一个后端 Pod (Real Server)。关键操作: 在 DSR/DR 模式下,IPVS 不修改 IP 头部 (源 IP=ClientIP, 目标 IP=VIP),而是仅修改 L2 帧头,将目标 MAC 地址改为选定后端 Pod/节点 的 MAC 地址,然后将帧转发出去。
- k8s minion node (Backend Service 层 - Real Server): 运行实际业务 Pod 的 K8s 节点。
- Backend Pod (k8s pod): 接收来自 L4 LB 节点转发的、目标 IP 仍为 VIP 的数据包。
控制路径 (配置同步):
- 用户创建/更新 Service (带有特定配置启用 DSR)。
- Elb-Director Pod 监听到变化。
- Elb-Director 通过 Netlink 配置 L4 LB 节点上的 IPVS:
- 添加 VIP 作为 Virtual Service。
- 添加健康的 Backend Pod IP 作为 Real Server,并指定转发模式为 DSR/DR (
-g
选项 inipvsadm
)。
数据路径 (核心流程):
请求: Client -> Router (ECMP) -> L4 LB Node (IPVS) -> Backend Node
- Client 发送包:
[ IP(Src:ClientIP, Dst:VIP) | TCP | Payload ]
- Router 通过 ECMP 选择一个 L4 LB 节点,转发 L2 帧。
- L4 LB 节点 IPVS 模块收到包,选择后端 Pod (RS_IP, RS_MAC)。
- IPVS 重写 L2 帧头:
[ Eth(Src:LB_MAC, Dst:RS_MAC) | IP(Src:ClientIP, Dst:VIP) | TCP | Payload ]
(IP 头不变!) - 帧发送给后端节点。
- Client 发送包:
后端节点处理请求:
- 后端节点网卡收到帧,解开 L2,得到 IP 包
[ IP(Src:ClientIP, Dst:VIP) | ... ]
。 - 关键配置: 为了让后端节点内核接受这个目标 IP 是 VIP 的包,必须在所有后端节点(或 Pod 网络命名空间内)配置 VIP 地址,通常配置在
lo
(loopback) 接口上,并抑制该接口对 VIP 的 ARP 响应(见下文 ARP 问题)。 - 内核将包路由给监听相应端口的 Backend Pod。Pod 内的应用看到请求的源 IP 是真实的 ClientIP。
- 后端节点网卡收到帧,解开 L2,得到 IP 包
响应: Backend Node -> Router -> Client (绕过 L4 LB)
- Backend Pod 处理完毕,生成响应包。
- 构建响应 IP 包:Source IP = VIP, Destination IP = ClientIP。 (源 IP 必须是 VIP!)
[ IP(Src:VIP, Dst:ClientIP) | TCP | Response Payload ]
- 后端节点内核根据 Destination IP (ClientIP) 查询路由表,找到通往客户端的路径(通常是默认网关 Router)。
- 构建 L2 帧头:
[ Eth(Src:Backend_MAC, Dst:Router_MAC) | IP(Src:VIP, Dst:ClientIP) | TCP | Response Payload ]
- 帧直接发送给 Router。
- Router 将响应包路由回 Client。
DSR 的核心挑战:The ARP Problem
如果后端服务器节点响应了网络上对 VIP 地址的 ARP 请求,那么路由器或其他设备可能会错误地学习到 VIP 对应的 MAC 地址是某个后端服务器的 MAC 地址。这会导致后续发往 VIP 的流量被直接发送到该后端服务器,绕过了 L4 LB Director,破坏了负载均衡。
解决方案: 必须在所有后端服务器节点上进行内核参数调整,以阻止它们响应对配置在 lo
接口上的 VIP 的 ARP 请求。
1 |
|
必须确保所有 Real Server 都应用了这些配置。 在 Kubernetes 中,这通常通过运行一个特权的 DaemonSet 来自动化完成。
DSR 优缺点
- 优点:
- 极高吞吐量: LB 节点只处理请求流量,性能瓶颈大大缓解。
- 良好伸缩性: 后端服务实例增加对 LB 节点压力影响小。
- 保持客户端源 IP: 后端服务可见真实客户端 IP。
- 缺点:
- 配置复杂: 后端节点需要特殊配置(VIP on lo, ARP 抑制)。
- 网络环境要求: 需要后端节点可以直接路由回客户端。
- 故障排查难度增加: 非对称路由可能使问题定位更复杂。
4. L7 集群架构与 IPIP 隧道

这张图展示了一个典型的 Kubernetes L7 架构,其中 L7 Proxy Pods (如 Nginx Ingress Controller) 作为流量入口,并将请求转发给后端的 Application Pods。图中还涉及到了 VIP 的管理和跨节点通信可能使用的 IPIP 隧道技术。
L7 架构回顾
- 适用场景: 微服务网关、Web 应用接入层、Kubernetes Ingress/Gateway API 实现。
- 核心组件: L7 Proxy Pods (运行 Nginx, Envoy 等), Application Pods, Virtual IPs (VIPs)。
- VIP 管理: VIP 是稳定的逻辑访问点。在 K8s 中,可以通过
Service Type=LoadBalancer
(云环境或配合 MetalLB)、MetalLB (裸金属)、或 Ingress Controller 自身的 Service 来实现 VIP 的分配和路由。最终目标是让发往 VIP 的流量能够负载均衡地到达健康的 L7 Proxy Pod 实例。kube-proxy
(特别是 IPVS 模式) 在节点层面负责将到达节点的 VIP 流量转发给本地或远程的 L7 Proxy Pod。
IPIP 隧道与跨节点 Pod 通信
当 L7 Proxy Pod (运行在 Node A) 需要将请求转发给一个运行在不同节点 (Node B) 上的 App Pod 时,就需要跨节点 Pod 通信。如果底层网络不支持直接路由 Pod IP,或者需要构建 Overlay 网络,IPIP (IP in IP) 隧道是一种常用的技术。
为何需要隧道?
- 解决 Pod IP 在物理网络中不可路由的问题。
- 简化物理网络配置,只需路由节点 IP。
- 跨越 L2/L3 边界。
IPIP 封包 (Encapsulation) - Node A 发送给 Node B 上的 Pod:
- 原始 IP 包 (Inner):
[ IP(Src:Proxy_Pod_IP, Dst:App_Pod_IP) | TCP | Payload ]
- Node A 内核决定通过 IPIP 隧道发送。
- 添加外部 IP 头 (Outer):
- Outer SrcIP:
NodeA_IP
- Outer DstIP:
NodeB_IP
- Outer Protocol:
4
(IP-in-IP)
- Outer SrcIP:
- 封装: 原始包成为外部包的 Payload。
[ OuterIP(Src:NodeA, Dst:NodeB, Proto:4) | InnerIP(Src:ProxyPod, Dst:AppPod) | TCP | Payload ]
- 封装后的包通过物理网络发送给 Node B。
IPIP 解包 (Decapsulation) - Node B 接收:
- Node B 的物理网卡收到一个 IP 数据包,其目的 IP 是
NodeB_IP
。 - 协议检查: Node B 的 Linux 内核检查收到的 IP 包的 IP Header 中的 Protocol 字段。发现其值为
4
。 - IPIP 解封装: 内核识别出这是一个 IPIP 封装的数据包,于是:
- 剥离 (Strip) 掉外部 IP Header。
- 将内部载荷(即原始的 IP 数据包)提取出来。
- 内部包路由: 内核现在处理这个解封装后的原始 IP 数据包。根据其 内部目的 IP (
App_Pod_IP
),查询本地的路由表,将数据包转发给本地的目标 App Pod。
Kubernetes CNI 与 IPIP:
CNI 插件如 Calico (在 IPIP 模式下) 或 Flannel (也可配置为 IPIP) 会自动管理 IPIP 隧道的创建和路由规则配置,使得跨节点 Pod 通信对应用透明。它们通常利用 Linux 内核的 ipip
模块或 XFRM/FOU 机制来实现封装和解封装。
数据流综合示例
让我们通过一个更具体的例子,结合多种技术,来追踪数据包的旅程。

场景: 外部客户端 (66.0.0.1
) 访问 K8s 集群中的服务,入口 VIP 为 10.0.0.1
。流量最终由运行在 Gateway Pod (10.0.3.48
) 内的 Envoy 代理处理。集群使用 Calico CNI,配置了 BGP 和 IP-in-IP 隧道,并使用 IPVS 模式的 kube-proxy
。
数据包流向:
外部请求与路由:
- Client (
s:66.0.0.1
) 发送请求到 VIP (d:10.0.0.1
)。 - 数据包经互联网/企业网到达数据中心边界,最终抵达 ToR 交换机 (
10.0.2.1
)。 - ToR 根据其路由表(可能通过 BGP 从 K8s 节点学习到
10.0.0.1
的路由,或者10.0.0.1
是配置在外部 LB 上的 VIP,LB 再将流量导向节点)将数据包转发给 K8s 节点 (10.0.2.46
)。
- Client (
节点入口与 Kube-Proxy/IPVS:
- 数据包到达节点
10.0.2.46
的eth0
接口。 - 内核网络栈处理。
kube-proxy
(IPVS 模式) 拦截到目标为 VIP10.0.0.1
的流量。 - IPVS 查找规则,根据负载均衡算法选择后端 Pod,即 Gateway Pod (
10.0.3.48
)。 - IPVS 执行 DNAT,准备将包转发给
10.0.3.48
。由于目标 Pod 在不同子网的另一个节点上,IPVS (或后续的路由决策) 知道需要通过隧道。
- 数据包到达节点
Calico BGP/IP-in-IP 封装与转发:
- 节点
10.0.2.46
需要将包发送给10.0.3.48
。 - Calico BGP: 节点上的 Bird/BGP 客户端已经通过 BGP 学习到
10.0.3.x
Pod 子网的可达性信息,知道目标 Pod 位于哪个节点(我们称之为 Node B)。 - Calico IP-in-IP: 由于跨子网,Calico 配置为使用 IP-in-IP 隧道。内核执行封装:
- Inner Packet:
[ IP(Src:66.0.0.1, Dst:10.0.0.1) | ... ]
(注意:这里 Dst 仍是 VIP,DNAT 可能发生在封装后或解封装前,具体取决于 IPVS 和 CNI 的交互细节) 或[ IP(Src:66.0.0.1, Dst:10.0.3.48) | ... ]
(如果 DNAT 在封装前完成)。图中似乎暗示 DNAT 发生在 IPVS 阶段。 - Outer Header:
[ IP(Src:NodeA_TunnelIP=10.0.2.24, Dst:NodeB_IP or PodIP=10.0.3.48, Proto:4) ]
(Calico 的 IPIP 模式可以直接将 Outer Dst 设为 Pod IP)。
- Inner Packet:
- 封装后的包
[ OuterIP | InnerIP | ... ]
根据 Outer Dst IP (10.0.3.48
) 进行路由,通过 ToR10.0.2.1
发往目标节点所在的网络。
- 节点
目标节点解封装与 Pod 交付:
- 封装包到达目标节点 (Node B)。
- Node B 内核检查 Outer IP Header,发现 Proto=4,执行 IP-in-IP 解封装,剥离 Outer Header。
- 得到 Inner Packet (
[ IP(Src:66.0.0.1, Dst:10.0.0.1 or 10.0.3.48) | ... ]
)。 - 内核将 Inner Packet 通过 veth pair (
caliXXX
<->eth0
in Pod) 路由到 Gateway Pod (10.0.3.48
) 的网络命名空间。
Envoy 处理与响应:
- 数据包到达 Gateway Pod 内的
eth0
接口。 - Pod 内运行的 Envoy 代理监听端口 80,接收请求。
- Envoy 根据其配置(Listener, Route, Cluster)处理请求,可能包括路由、限流、认证等,并将请求转发给真正的后端应用服务(图中未画出后端服务)。
- Envoy 收到后端响应后,构建响应包:
[ IP(Src:PodIP=10.0.3.48 or VIP=10.0.0.1, Dst:ClientIP=66.0.0.1) | ... ]
。 - 响应包通过 Pod 的
eth0
-> veth pair -> 宿主机网络 -> ToR -> 外部网络,直接返回给客户端(通常无需再次封装)。
- 数据包到达 Gateway Pod 内的
总结
Kubernetes 提供了灵活多样的网络服务暴露机制:
- Service (
ClusterIP
,NodePort
): 集群内部服务发现和基础 L4 暴露。 - Service (
LoadBalancer
): 云环境或配合 MetalLB 实现标准外部 L4 暴露。 - Ingress: 成本效益高、功能丰富的 L7 HTTP/S 流量入口管理。
- BGP (配合 MetalLB/Calico): 适用于裸金属环境,提供高性能、原生 IP 路由的 L4/Pod 网络集成。
- DSR (配合 IPVS): 针对超高 L4 吞吐量场景的性能优化技术。
选择哪种技术取决于具体的应用场景、性能需求、成本考虑以及运维复杂度。理解这些技术的工作原理和它们之间的关系,对于构建健壮、可扩展的 Kubernetes 应用至关重要。