您的足迹: 1-http流量分发

**这是本文档旧的修订版!**

K3s ServiceLB(LoadBalancer)工作原理

为什么 K3s 不需要安装 MetalLB?

很多第一次接触 K3s 的用户都会发现:

kubectl create service loadbalancer nginx --tcp=80:80

仅仅创建一个 Service,几秒钟之后,整个集群居然就可以直接访问了。

甚至执行:

kubectl get svc

可以看到:

NAME         TYPE           CLUSTER-IP      EXTERNAL-IP
nginx        LoadBalancer   10.43.120.55    192.168.1.10

而 Kubernetes 官方并没有提供 LoadBalancer 的实现。

这是因为:

K3s 默认集成了 ServiceLB(以前叫 Klipper LB)。

安装 K3s 时,这个组件已经自动安装完成。

它负责模拟云厂商 LoadBalancer 的行为。


ServiceLB 主要由哪些组件组成

整体架构如下:

+----------------+
                    |  ServiceLB     |
                    | Controller     |
                    +-------+--------+
                            |
        Watch Service(type=LoadBalancer)
                            |
                            v
              自动创建 DaemonSet
                            |
        +-------------------+-------------------+
        |                   |                   |
        v                   v                   v
+----------------+  +----------------+  +----------------+
| Node1          |  | Node2          |  | Node3          |
| svclb Pod      |  | svclb Pod      |  | svclb Pod      |
| HostNetwork    |  | HostNetwork    |  | HostNetwork    |
| Listen :80     |  | Listen :80     |  | Listen :80     |
| Listen :443    |  | Listen :443    |  | Listen :443    |
+--------+-------+  +--------+-------+  +--------+-------+
         |                   |                   |
         +---------iptables---+-------------------+
                         |
                         v
                 kube-proxy Service
                         |
                  EndpointSlice
                         |
                    Backend Pods

整个过程中实际上涉及四个组件:

  • ServiceLB Controller
  • DaemonSet
  • kube-proxy
  • EndpointSlice

创建 LoadBalancer Service 后发生了什么?

例如:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80

用户提交之后,流程如下:

kubectl apply

        │
        ▼
API Server
        │
        ▼
Service(type=LoadBalancer)
        │
        ▼
ServiceLB Controller
        │
        ▼
创建 DaemonSet
        │
        ▼
每台 Node 启动 svclb Pod
        │
        ▼
HostNetwork 监听 80/443
        │
        ▼
iptables 转发
        │
        ▼
ClusterIP
        │
        ▼
kube-proxy
        │
        ▼
Pod

整个过程全部自动完成。

用户无需创建任何 DaemonSet。


为什么每台机器都会启动一个 Pod?

ServiceLB 创建的是:

DaemonSet

DaemonSet 的特点就是:

每一个 Node 都运行一个副本。

例如:

kubectl get ds -A

NAMESPACE     NAME
kube-system   svclb-nginx

对应:

Node1
 └── svclb-nginx

Node2
 └── svclb-nginx

Node3
 └── svclb-nginx

这样意味着:

任意节点 IP 都可以访问:

http://Node1
http://Node2
http://Node3

都会进入同一个 Service。


svclb Pod 为什么能够监听宿主机 80、443?

svclb 使用:

hostNetwork: true

因此:

Pod 实际就是运行在宿主机网络空间。

例如:

Node

80
443
6443
10250

svclb 可以直接绑定:

0.0.0.0:80
0.0.0.0:443

并不是 Pod Network。

因此:

curl http://NodeIP

实际上就是访问 svclb。


流量如何进入 nginx Pod?

假设:

Node1

nginx-1
nginx-2
nginx-3

访问路径:

Browser

   │

NodeIP:80

   │

svclb

   │

iptables

   │

ClusterIP

   │

kube-proxy

   │

EndpointSlice

   │

随机选择 Pod

   │

nginx-2

注意:

svclb 不会自己实现负载均衡。

真正做负载均衡的是:

kube-proxy


kube-proxy 如何知道有哪些 Pod?

它监听:

EndpointSlice

例如:

kubectl get endpointslice

内容类似:

10.42.0.10
10.42.1.25
10.42.2.13

每一个都是 Pod IP。

kube-proxy 根据这些 Endpoint 自动生成:

  • iptables
  • IPVS(如果开启)

规则。


后端 Pod 如何选择?

默认情况下:

iptables 模式:

随机概率

例如:

Pod1

33%

Pod2

33%

Pod3

34%

IPVS 模式:

支持:

  • rr
  • lc
  • wrr
  • sed
  • sh

等算法。

默认通常是:

Round Robin


粘性 Session(Session Affinity)如何实现?

如果配置:

sessionAffinity: ClientIP

例如:

spec:
  sessionAffinity: ClientIP

则:

同一个 Client IP:

192.168.1.100

↓

永远进入

nginx-2

直到:

  • Pod 消失
  • 超时
  • Session 失效

实现方式:

iptables/IPVS 会维护 ClientIP 与 Backend 的映射关系。

并不是 ServiceLB 自己维护。


是否支持 IP Hash?

很多用户会想到 Nginx 的:

ip_hash;

实际上:

Kubernetes Service:

没有 IP Hash 配置。

只有:

sessionAffinity=ClientIP

如果需要真正:

  • consistent hash
  • ip hash
  • uri hash

一般放在:

  • Nginx Ingress
  • Traefik
  • Envoy
  • HAProxy

而不是 Service。


如果访问 Node2,但 Pod 在 Node3,会发生什么?

例如:

Client

    │

Node2

    │

svclb

    │

ClusterIP

    │

kube-proxy

    │

Pod(Node3)

kube-proxy 会自动跨节点转发。

因此:

Node2

Node3

Pod

整个过程用户无感知。


如何避免跨节点转发?

很多情况下:

跨节点会多走一次 VXLAN。

例如:

Client

↓

Node1

↓

Node3

↓

Pod

如果希望:

尽量访问本地 Pod。

可以配置:

externalTrafficPolicy: Local

例如:

spec:
  externalTrafficPolicy: Local

此时:

Node1

只会转发:

Node1 上面的 Pod。

如果:

Node1 没有 Pod:

直接返回:

No Endpoint

不会跨节点。

优点:

  • 保留真实 Client IP
  • 不经过二次 NAT
  • 延迟更低
  • 避免 VXLAN

externalTrafficPolicy=Cluster 与 Local 对比

ClusterLocal
所有节点均可访问只有存在 Pod 的节点可访问
允许跨节点转发禁止跨节点
可能 SNAT保留 Client IP
负载均衡更均匀效率更高

生产环境推荐:

如果:

Deployment 每个 Node 都有副本:

建议:

externalTrafficPolicy: Local

这样:

基本可以做到:

Client

Node

Local Pod

避免:

Node

Overlay

另一台 Node

的二次网络跳转。


查看 ServiceLB 创建的 DaemonSet

查看:

kubectl get ds -A

查看对应 Pod:

kubectl get pod -A -o wide | grep svclb

查看监听端口:

ss -lntp

查看 iptables:

iptables-save

可以观察到:

ServiceLB 主要负责:

把宿主机端口接入 Kubernetes Service。

真正决定:

  • 选择哪个 Pod
  • 如何负载均衡
  • Session 保持
  • Endpoint 更新

全部由 kube-proxy 完成。


总结

ServiceLB 可以理解为 Kubernetes 集群最外层的一层“入口适配器”。

它负责:

  • 自动监听宿主机端口
  • 自动创建 DaemonSet
  • 接收外部流量
  • 转发至 ClusterIP

而真正的 Service 负载均衡、Endpoint 管理、Pod 选择则由 kube-proxy 和 Kubernetes 控制平面完成。

因此,K3s 的 LoadBalancer 实际上是:

Client
    │
NodeIP
    │
ServiceLB(DaemonSet)
    │
ClusterIP(Service)
    │
kube-proxy
    │
EndpointSlice
    │
Backend Pods

理解这一层关系之后,就能够清晰定位 K3s 中 LoadBalancer、DaemonSet、Service、kube-proxy 之间各自的职责,也能在出现网络问题时快速判断问题所在。

评论

请输入您的评论. 可以使用维基语法:
 
虚拟化/k8s/k3s架构/1-http流量分发.1783040370.txt.gz · 最后更改: 2026/07/03 00:59