{{htmlmetatags>metatag-robots=()
metatag-keywords=(k3s,ServiceLB,LoadBalancer,DaemonSet,klipper-lb,kube-proxy,iptables,ipvs)
metatag-description=(详细介绍 K3s 中 ServiceLB 的工作原理,包括 LoadBalancer 创建流程、DaemonSet 工作机制、流量转发路径以及最佳实践)
metatag-media-og:image=(:wiki:k3s-servicelb.jpg)
metatag-og:description=(深入分析 K3s ServiceLB、DaemonSet、iptables、kube-proxy 的完整流量转发过程)
metatag-og:any=(K3s LoadBalancer 原理解析、klipper-lb 工作流程、架构图与最佳实践)
}}
====== 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 对比 =====
^Cluster^Local^
|所有节点均可访问|只有存在 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 之间各自的职责,也能在出现网络问题时快速判断问题所在。