news 2026/9/14 13:27:41

Cilium Proxy Load Balancing:用 Envoy 七层注解为 Kubernetes 服务做 L7 负载均衡(beta)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cilium Proxy Load Balancing:用 Envoy 七层注解为 Kubernetes 服务做 L7 负载均衡(beta)

Cilium Proxy Load Balancing:用 Envoy 七层注解为 Kubernetes 服务做 L7 负载均衡(beta)

【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium

本文围绕 Cilium 官方指南《Proxy Load Balancing for Kubernetes Services (beta)》展开,讲解如何通过 Service 注解将 Kubernetes Service 的流量重定向到 Cilium 托管的 Envoy 代理,从而获得七层负载均衡能力(典型场景是 gRPC 负载均衡)。读完本文,你将掌握完整的部署与验证流程(含 Hubble 流量观测对比),理解service.cilium.io/lb-l7service.cilium.io/lb-l7-algorithm两个注解的取值语义,并能从源码层面弄清 Cilium 是如何把这些注解翻译成 Envoy Cluster 配置并写回 BPF 服务表的。

一、什么是 Proxy Load Balancing

Proxy Load Balancing 是 Cilium 面向 Kubernetes Service 的一项beta功能(文档中明确标注:This is a beta feature. Please provide feedback and file a GitHub issue if you experience any problems.,见 beta 声明文件)。其核心行为是:

  • 启用后,发往某个 Kubernetes Service 的流量会被重定向到Cilium 托管的 Envoy 代理进行负载均衡,再转发到后端 Pod;
  • 它特别适用于需要七层能力的场景,例如gRPC 负载均衡
  • 该功能独立于 Cilium 的 Ingress 功能(文档中明确声明 "independent of the ingress feature",Ingress 相关指南见 ingress.rst)。

从源码结构看,这个功能由 Operator 侧的ciliumenvoyconfig控制器驱动:控制器 watch Service 资源,一旦发现目标 Service 携带 L7 注解,就生成一份 CiliumEnvoyConfig(包含 Listener、RouteConfiguration、Cluster 等 XDS 资源),交由本机嵌入的 Envoy 代理完成真正的七层负载均衡。关键入口在 Reconcile 函数:

if isLBProtocolAnnotationEnabled(svc) || hasAnyPort(svc, r.ports) { if err := r.createOrUpdateEnvoyConfig(ctx, svc); err != nil { return ctrl.Result{}, err } } else { if err := r.deleteEnvoyConfig(ctx, svc); err != nil { return ctrl.Result{}, err } }

也就是说,Service 满足任一条件时都会生成/更新 Envoy 配置:注解service.cilium.io/lb-l7=enabled开启,或 Service 端口命中全局配置的自动重定向端口列表(hasAnyPort)。移除注解后则会删除对应的 Envoy 配置。

二、前置配置:Helm 参数loadBalancer.l7

在使用注解之前,通常需要通过 Helm 参数在集群层面启用该能力的开关与默认值。在 install/kubernetes/cilium/values.yaml 中可以找到完整定义:

loadBalancer: # -- L7 LoadBalancer l7: # -- Enable L7 service load balancing via envoy proxy. # The request to a k8s service, which has specific annotation e.g. service.cilium.io/lb-l7, # will be forwarded to the local backend proxy to be load balanced to the service endpoints. # Please refer to docs for supported annotations for more configuration. # # Applicable values: # - envoy: Enable L7 load balancing via envoy proxy. This will automatically set enable-envoy-config as well. # - disabled: Disable L7 load balancing by way of service annotation. backend: disabled # -- List of ports from service to be automatically redirected to above backend. # Any service exposing one of these ports will be automatically redirected. # Fine-grained control can be achieved by using the service annotation. ports: [] # -- Default LB algorithm # The default LB algorithm to be used for services, which can be overridden by the # service annotation (e.g. service.cilium.io/lb-l7-algorithm) # Applicable values: round_robin, least_request, random algorithm: round_robin

三个参数说明:

Helm 参数默认值说明
loadBalancer.l7.backenddisabled设为envoy表示通过 Envoy 代理做 L7 负载均衡,且会自动连带设置enable-envoy-config
loadBalancer.l7.ports[]暴露这些端口的 Service 会被自动重定向到 L7 backend;更细粒度的控制则用 Service 注解
loadBalancer.l7.algorithmround_robin默认 LB 算法,可被 Service 注解service.cilium.io/lb-l7-algorithm覆盖;取值round_robin/least_request/random

代码侧的默认值与之一致:Operator 的 cell 定义中写死了LoadBalancerL7Algorithm: "round_robin"(见 cell.go)。

三、部署测试应用

按照 官方指南 的步骤,部署测试应用:

$ kubectl apply -f examples/kubernetes/servicemesh/envoy/test-application-proxy-loadbalancing.yaml

该清单(test-application-proxy-loadbalancing.yaml)包含三类工作负载:

  • 一个clientDeployment:单副本,使用quay.io/cilium/alpine-curl镜像,提供 curl 客户端;
  • 一个echo-serviceDeployment:2 副本,使用quay.io/cilium/json-mock镜像作为回显后端,并通过podAffinity要求与 client 调度在同一节点(topologyKey: kubernetes.io/hostname),同时每副本内置一个 CoreDNS 测试容器;
  • 一个echo-serviceService:端口 8080(TCP),selector 为name=echo-service,初始没有任何 Cilium 注解

查看部署后的 Pod:

$ kubectl get pods --show-labels -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS client-7dccb64ff6-t5gc7 1/1 Running 0 39s 10.244.0.125 minikube <none> <none> kind=client,name=client,pod-template-hash=7dccb64ff6 echo-service-744b6dd45b-487tn 2/2 Running 0 39s 10.244.0.71 minikube <none> <none> kind=echo,name=echo-service,other=echo,pod-template-hash=744b6dd45b echo-service-744b6dd45b-mdjc2 2/2 Running 0 39s 10.244.0.213 minikube <none> <none> kind=echo,name=echo-service,other=echo,pod-template-hash=744b6dd45b

并记下 client Pod 名称,便于后续 exec:

$ CLIENT=$(kubectl get pods -l name=client -o jsonpath='{.items[0].metadata.name}')

四、用 Hubble 观测流量(基线:纯 BPF 四层负载均衡)

按照 Hubble 部署指南 在集群中启用 Hubble,然后在第二个终端开启端口转发并持续观测echo-service的流量:

$ kubectl -n kube-system port-forward deployment/hubble-relay 4245:4245 & $ hubble observe --service echo-service -f

从 client 向服务发起请求:

$ kubectl exec -it $CLIENT -- curl -v echo-service:8080/

此时可以分别从两个后端 Pod 逐个收到响应(由 BPF 数据面完成四层负载均衡)。Hubble 中看到的流量是穿越 xlate的直接转发路径:

Jan 16 04:28:10.690: default/client-7dccb64ff6-t5gc7 (ID:5152) <> default/echo-service:8080 (world) pre-xlate-fwd TRACED (TCP) Jan 16 04:28:10.690: default/echo-service:8080 (world) <> default/client-7dccb64ff6-t5gc7 (ID:5152) post-xlate-rev TRANSLATED (TCP)

注意这两条事件:pre-xlate-fwd(xlate 前正向)与post-xlate-rev(xlate 后反向),表明流量直接走 BPF 服务表做了地址转换,路径中没有代理

五、添加注解启用 Proxy Load Balancing

对目标 Service 添加 L7 注解,引入 Envoy 代理:

$ kubectl annotate service echo-service service.cilium.io/lb-l7=enabled service/echo-service annotated

再次发起请求并观察 Hubble:

$ kubectl exec -it $CLIENT -- curl -v echo-service:8080/

流量模式发生了本质变化——请求先被送进 Envoy 代理(to-proxy),再由代理转发到后端(to-endpoint):

Jan 16 04:32:27.737: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) -> default/echo-service:8080 (world) to-proxy FORWARDED (TCP Flags: SYN) Jan 16 04:32:27.737: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) <- default/echo-service:8080 (world) to-endpoint FORWARDED (TCP Flags: SYN, ACK) Jan 16 04:32:27.737: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) -> default/echo-service:8080 (world) to-proxy FORWARDED (TCP Flags: ACK) Jan 16 04:32:27.737: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) -> default/echo-service:8080 (world) to-proxy FORWARDED (TCP Flags: ACK, PSH) Jan 16 04:32:27.739: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) <- default/echo-service:8080 (world) to-endpoint FORWARDED (TCP Flags: ACK, PSH) Jan 16 04:32:27.740: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) -> default/echo-service:8080 (world) to-proxy FORWARDED (TCP Flags: ACK, FIN) Jan 16 04:32:27.740: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) <- default/echo-service:8080 (world) to-endpoint FORWARDED (TCP Flags: ACK, FIN) Jan 16 04:32:27.740: default/client-7dccb64ff6-t5gc7:56462 (ID:5152) -> default/echo-service:8080 (world) to-proxy FORWARDED (TCP Flags: ACK)

对比第四节,可以清楚看到流量路径从pre-xlate-fwd / post-xlate-rev变成了to-proxy / to-endpoint:请求先由客户端发往 Envoy 代理,再由代理按七层策略选择后端 Pod 并转发。

六、支持的注解(Supported Annotations)

官方指南定义的注解如下表(完整继承自原文档):

注解名说明适用取值默认值
service.cilium.io/lb-l7为 Kubernetes Service 启用 L7 负载均衡enableddisabled默认disabled
service.cilium.io/lb-l7-algorithm服务使用的 LB 算法round_robinleast_requestrandom默认取 Helm 参数loadBalancer.l7.algorithm的值

6.1 注解解析的源码依据

两个注解的常量定义与解析逻辑集中在 operator/pkg/ciliumenvoyconfig/annotations.go:

const ( lbEnabledAnnotation = annotation.ServicePrefix + "/lb-l7" lbModeAnnotation = annotation.ServicePrefix + "/lb-l7-algorithm" ) // isLBProtocolAnnotationEnabled returns true if the load balancer protocol is enabled func isLBProtocolAnnotationEnabled(obj metav1.Object) bool { return obj.GetAnnotations()[lbEnabledAnnotation] == "enabled" } // getLBProtocolModelAnnotation returns the load balancer mode func getLBProtocolModelAnnotation(obj metav1.Object) string { return obj.GetAnnotations()[lbModeAnnotation] }

从源码结构看有两点值得注意:

  1. 开启注解是严格匹配"enabled"字符串的,其他取值(包括disabled或任意拼写错误)都不会启用该功能;
  2. 算法注解通过lbModeClusterMutator落到 Envoy Cluster 的LbPolicy字段上,取值会被strings.ToUpper后再查 Envoy 的Cluster_LbPolicy枚举(ROUND_ROBINLEAST_REQUESTRANDOM):
func lbModeClusterMutator(obj metav1.Object) clusterMutator { return func(cluster *envoy_config_cluster_v3.Cluster) *envoy_config_cluster_v3.Cluster { lbMode := getLBProtocolModelAnnotation(obj) if lbMode == "" { return cluster } cluster.LbPolicy = envoy_config_cluster_v3.Cluster_LbPolicy( envoy_config_cluster_v3.Cluster_LbPolicy_value[strings.ToUpper(lbMode)]) return cluster } }

即:若未设置service.cilium.io/lb-l7-algorithm,Cluster 保留由全局默认算法生成的LbPolicy(envoy_config.go 中 getClusterResources 用 Helm 的loadBalancer.l7.algorithm生成默认策略,解析失败时兜底为ROUND_ROBIN)。此外,生成的 Cluster 还内置了 EDS 端点发现与 Outlier Detection(ConsecutiveLocalOriginFailure: 2),意味着被注解服务天然获得 Envoy 层的不健康后端剔除能力。

七、底层数据面:Service 如何被打上 L7LB 标记

注解不仅影响控制面(生成 Envoy 配置),还会影响 BPF 数据面的服务表示。在 pkg/loadbalancer/loadbalancer.go 中,Service 有一个专门的标志位serviceFlagL7LoadBalancer = 1 << 10,通过ServiceFlags.IsL7LB()判断某个服务是否走 L7 代理;Service4Value/Service6Value还携带L7LBProxyPort(见 pkg/loadbalancer/maps/types.go),记录本机 Envoy 代理监听端口。BPF reconciler 在更新服务表时写入该端口(pkg/loadbalancer/reconciler/bpf_reconciler.go:svcVal.SetL7LBProxyPort(pr.ProxyPort))。

这也解释了 Hubble 事件中的to-proxy路径:BPF 数据面知道该服务由本机代理接管,于是将流量导向代理端口,再由 Envoy 完成 xlate 与转发(to-endpoint)。cilium-dbg bpf lb list命令在输出 L7LB 服务时会额外展示代理端口(L7LB Proxy Port: ...,见 bpf_lb_list.go),可用于实操中确认标记是否生效。

八、端到端验证:cilium-cli 连通性测试

Cilium 官方的连通性测试套件对这一功能有系统性的覆盖,可作为生产验证的参考实现:

  • 部署阶段,deployment.go 会为l7-lb服务显式打上"service.cilium.io/lb-l7": "enabled"注解;同时另建一个不带注解的同后端非 L7 服务,专门用于测试 BPF hairpinning 路径;
  • 测试用例 builder/l7_lb.go 定义了PodToL7Service场景,其中hair-pinning子用例验证"客户端 Pod 通过 L7 服务访问自身所在 Pod"的 hairpin 流量(带重试条件)。

从源码结构看,这说明官方测试同时覆盖了:注解开启后的正常 L7 转发、未注解服务的 BPF 原生路径、以及 L7 路径下的同 Pod 回环(hairpin)三种场景。如果你在集群中遇到问题,可以用cilium connectivity test中对应的 L7 LB 用例快速定位。

九、适用前提与限制

  • beta 特性:文档明确标注为 beta,建议在生产环境中评估后再启用,遇到问题按文档提示提交反馈;
  • 默认关闭:Helm 侧loadBalancer.l7.backend默认disabled,Service 侧注解默认不启用,二者都需要显式配置;
  • 注解匹配严格service.cilium.io/lb-l7只有值等于enabled时才生效;
  • 算法取值:注解仅支持round_robinleast_requestrandom三种(解析为 Envoy 对应大写枚举,其他取值从源码行为看不会改变 LbPolicy),默认回落到loadBalancer.l7.algorithm(默认round_robin);
  • 与 Ingress 相互独立:该功能不依赖也不影响 Cilium Ingress;
  • 端口级自动重定向:如果不希望逐个打注解,也可以配置loadBalancer.l7.ports,让暴露对应端口的 Service 自动接入 L7 代理(见第二节 values.yaml 注释)。

十、小结

Proxy Load Balancing 让 Cilium 在 Kubernetes Service 路径上插入一个 Cilium 托管的 Envoy 代理:控制面由 Operator 的ciliumenvoyconfig控制器监听 Service 注解生成 Envoy 配置(含LbPolicy、EDS、Outlier Detection),数据面通过serviceFlagL7LoadBalancerL7LBProxyPort让 BPF 服务表把流量引向本机代理。整条链路可通过三个层次的证据验证:kubectl annotate后 Hubble 中to-proxy/to-endpoint事件的变化、cilium-dbg bpf lb list显示的 L7LB 代理端口,以及cilium connectivity test中针对 L7 LB 与 hairpin 的自动化用例。

【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 13:27:24

电动快换为何必须用RS485+Modbus RTU

1. 为什么电动快换模块非得用 RS485 Modbus RTU&#xff1f;——不是选它&#xff0c;而是绕不开它 你拆过一台工业协作机器人的末端执行器吗&#xff1f;我去年在帮一家做汽车焊装产线的客户做快换模块升级时&#xff0c;第一次把那个银灰色金属壳子拧开&#xff0c;里面三根…

作者头像 李华
网站建设 2026/9/14 13:27:20

大模型生成测试用例实战:看懂设计稿、跑通CI才是选型王道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 13:25:25

相声与脱口秀融合:高晓攀《说点别的》的创新实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 13:25:23

工业级定制线缆设计核心逻辑与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华