本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 {{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 的用户都会发现: <code bash> kubectl create service loadbalancer nginx --tcp=80:80 </code> 仅仅创建一个 Service,几秒钟之后,整个集群居然就可以直接访问了。 甚至执行: <code bash> kubectl get svc </code> 可以看到: <code> NAME TYPE CLUSTER-IP EXTERNAL-IP nginx LoadBalancer 10.43.120.55 192.168.1.10 </code> 而 Kubernetes 官方并没有提供 LoadBalancer 的实现。 这是因为: **K3s 默认集成了 ServiceLB(以前叫 Klipper LB)。** 安装 K3s 时,这个组件已经自动安装完成。 它负责模拟云厂商 LoadBalancer 的行为。 ---- ===== ServiceLB 主要由哪些组件组成 ===== 整体架构如下: <code> 流程如下 +----------------+ | 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 </code> 整个过程中实际上涉及四个组件: * ServiceLB Controller * DaemonSet * kube-proxy * EndpointSlice ---- ===== 创建 LoadBalancer Service 后发生了什么? ===== 例如: <code yaml> apiVersion: v1 kind: Service metadata: name: nginx spec: type: LoadBalancer selector: app: nginx ports: - port: 80 targetPort: 80 </code> 用户提交之后,流程如下: <code> kubectl apply │ ▼ API Server │ ▼ Service(type=LoadBalancer) │ ▼ ServiceLB Controller │ ▼ 创建 DaemonSet │ ▼ 每台 Node 启动 svclb Pod │ ▼ HostNetwork 监听 80/443 │ ▼ iptables 转发 │ ▼ ClusterIP │ ▼ kube-proxy │ ▼ Pod </code> 整个过程全部自动完成。 用户无需创建任何 DaemonSet。 ---- ===== 为什么每台机器都会启动一个 Pod? ===== ServiceLB 创建的是: <code> DaemonSet </code> DaemonSet 的特点就是: **每一个 Node 都运行一个副本。** 例如: <code> kubectl get ds -A NAMESPACE NAME kube-system svclb-nginx </code> 对应: <code> Node1 └── svclb-nginx Node2 └── svclb-nginx Node3 └── svclb-nginx </code> 这样意味着: 任意节点 IP 都可以访问: <code> http://Node1 http://Node2 http://Node3 </code> 都会进入同一个 Service。 ---- ===== svclb Pod 为什么能够监听宿主机 80、443? ===== svclb 使用: <code yaml> hostNetwork: true </code> 因此: Pod 实际就是运行在宿主机网络空间。 例如: <code> Node 80 443 6443 10250 </code> svclb 可以直接绑定: <code> 0.0.0.0:80 0.0.0.0:443 </code> 并不是 Pod Network。 因此: <code> curl http://NodeIP </code> 实际上就是访问 svclb。 ---- ===== 流量如何进入 nginx Pod? ===== 假设: <code> Node1 nginx-1 nginx-2 nginx-3 </code> 访问路径: <code> Browser │ NodeIP:80 │ svclb │ iptables │ ClusterIP │ kube-proxy │ EndpointSlice │ 随机选择 Pod │ nginx-2 </code> 注意: svclb **不会自己实现负载均衡。** 真正做负载均衡的是: <code> kube-proxy </code> ---- ===== kube-proxy 如何知道有哪些 Pod? ===== 它监听: <code> EndpointSlice </code> 例如: <code> kubectl get endpointslice </code> 内容类似: <code> 10.42.0.10 10.42.1.25 10.42.2.13 </code> 每一个都是 Pod IP。 kube-proxy 根据这些 Endpoint 自动生成: * iptables * IPVS(如果开启) 规则。 ---- ===== 后端 Pod 如何选择? ===== 默认情况下: iptables 模式: <code> 随机概率 </code> 例如: <code> Pod1 33% Pod2 33% Pod3 34% </code> IPVS 模式: 支持: * rr * lc * wrr * sed * sh 等算法。 默认通常是: <code> Round Robin </code> ---- ===== 粘性 Session(Session Affinity)如何实现? ===== 如果配置: <code yaml> sessionAffinity: ClientIP </code> 例如: <code yaml> spec: sessionAffinity: ClientIP </code> 则: 同一个 Client IP: <code> 192.168.1.100 ↓ 永远进入 nginx-2 </code> 直到: * Pod 消失 * 超时 * Session 失效 实现方式: iptables/IPVS 会维护 ClientIP 与 Backend 的映射关系。 并不是 ServiceLB 自己维护。 ---- ===== 是否支持 IP Hash? ===== 很多用户会想到 Nginx 的: <code> ip_hash; </code> 实际上: Kubernetes Service: **没有 IP Hash 配置。** 只有: <code> sessionAffinity=ClientIP </code> 如果需要真正: * consistent hash * ip hash * uri hash 一般放在: * Nginx Ingress * Traefik * Envoy * HAProxy 而不是 Service。 ---- ===== 如果访问 Node2,但 Pod 在 Node3,会发生什么? ===== 例如: <code> Client │ Node2 │ svclb │ ClusterIP │ kube-proxy │ Pod(Node3) </code> kube-proxy 会自动跨节点转发。 因此: Node2 ↓ Node3 ↓ Pod 整个过程用户无感知。 ---- ===== 如何避免跨节点转发? ===== 很多情况下: 跨节点会多走一次 VXLAN。 例如: <code> Client ↓ Node1 ↓ Node3 ↓ Pod </code> 如果希望: **尽量访问本地 Pod。** 可以配置: <code yaml> externalTrafficPolicy: Local </code> 例如: <code yaml> spec: externalTrafficPolicy: Local </code> 此时: Node1 只会转发: Node1 上面的 Pod。 如果: Node1 没有 Pod: 直接返回: <code> No Endpoint </code> 不会跨节点。 优点: * 保留真实 Client IP * 不经过二次 NAT * 延迟更低 * 避免 VXLAN ---- ===== externalTrafficPolicy=Cluster 与 Local 对比 ===== ^Cluster^Local^ |所有节点均可访问|只有存在 Pod 的节点可访问| |允许跨节点转发|禁止跨节点| |可能 SNAT|保留 Client IP| |负载均衡更均匀|效率更高| 生产环境推荐: 如果: Deployment 每个 Node 都有副本: 建议: <code> externalTrafficPolicy: Local </code> 这样: 基本可以做到: Client ↓ Node ↓ Local Pod 避免: Node ↓ Overlay ↓ 另一台 Node 的二次网络跳转。 ---- ===== 查看 ServiceLB 创建的 DaemonSet ===== 查看: <code bash> kubectl get ds -A </code> 查看对应 Pod: <code bash> kubectl get pod -A -o wide | grep svclb </code> 查看监听端口: <code bash> ss -lntp </code> 查看 iptables: <code bash> iptables-save </code> 可以观察到: ServiceLB 主要负责: **把宿主机端口接入 Kubernetes Service。** 真正决定: * 选择哪个 Pod * 如何负载均衡 * Session 保持 * Endpoint 更新 全部由 kube-proxy 完成。 ---- ===== 总结 ===== ServiceLB 可以理解为 Kubernetes 集群最外层的一层"入口适配器"。 它负责: * 自动监听宿主机端口 * 自动创建 DaemonSet * 接收外部流量 * 转发至 ClusterIP 而真正的 Service 负载均衡、Endpoint 管理、Pod 选择则由 kube-proxy 和 Kubernetes 控制平面完成。 因此,K3s 的 LoadBalancer 实际上是: <code> Client │ NodeIP │ ServiceLB(DaemonSet) │ ClusterIP(Service) │ kube-proxy │ EndpointSlice │ Backend Pods </code> 理解这一层关系之后,就能够清晰定位 K3s 中 LoadBalancer、DaemonSet、Service、kube-proxy 之间各自的职责,也能在出现网络问题时快速判断问题所在。