
Cilium Service Mesh用 CiliumEnvoyConfig 实现 L7 负载均衡与 URL 重写完整实践【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium本文基于 Cilium 官方文档《L7 Load Balancing and URL re-writing》展开介绍 Cilium Service Mesh 中CiliumEnvoyConfig/CiliumClusterwideEnvoyConfigCRD 的完整实战流程部署测试工作负载、通过 Hubble 观测流量、添加 L7 网络策略让内置 Envoy 代理介入流量路径最终用CiliumClusterwideEnvoyConfig定义 Envoy Listener、Route 和 Cluster 资源在两个后端服务之间做 50/50 加权负载均衡并将路径/foo重写为/。读完本文你可以掌握在 Cilium 集群中用声明式 xDS 资源实现 L7 流量管理负载均衡、URL 重写、重试策略、异常检测的全部操作步骤并理解 Envoy 资源在 Cilium Agent 中的加载方式与校验限制。Cilium Service Mesh 定义了CiliumEnvoyConfigCRD允许用户为嵌入 Cilium Agent 的 Envoy 组件设置配置。本文的示例场景就是配置一个 Envoy Listener将流量在两个后端服务echo-service-1和echo-service-2之间负载均衡。部署测试应用先部署测试工作负载对应的清单文件位于 examples/kubernetes/servicemesh/envoy/test-application.yamlkubectl apply -f examples/kubernetes/servicemesh/envoy/test-application.yaml该清单包含以下资源两个客户端 Deploymentclient和client2使用quay.io/cilium/alpine-curl镜像以sleep方式常驻供kubectl exec发起请求两个 Echo 服务echo-service-1和echo-service-2基于quay.io/cilium/json-mock镜像监听 8080 端口返回 JSON 回显echo-service-1额外带otherecho标签并设置了与 client 的 podAffinity两个 echo Pod 内还各带一个 sidecar CoreDNS 容器用于 DNS 测试两个NodePort类型的 Serviceecho-service-1、echo-service-2均暴露 8080/TCP。查看这些 Pod 的信息kubectl get pods --show-labels -o wide输出示例kindclient,nameclient2,otherclient这类标签组合决定了后续策略的选择器NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS client-7568bc7f86-dlfqr 1/1 Running 0 100s 10.0.1.8 minikube-m02 none none kindclient,nameclient,pod-template-hash7568bc7f86 client2-8b4c4fd75-xn25d 1/1 Running 0 100s 10.0.1.24 minikube-m02 none none kindclient,nameclient2,otherclient,pod-template-hash8b4c4fd75 echo-service-1-97748874-4sztx 2/2 Running 0 100s 10.0.1.86 minikube-m02 none none kindecho,nameecho-service-1,otherecho,pod-template-hash97748874 echo-service-2-76c584c4bf-p4z4w 2/2 Running 0 100s 10.0.1.16 minikube-m02 none none kindecho,nameecho-service-2,pod-template-hash76c584c4bf可以注意到只有client2带有otherclient标签——这个标签稍后会用在CiliumNetworkPolicy的endpointSelector中使 L7 策略只作用于client2这个端点。把client2的 Pod 名保存到环境变量中后文命令会反复用到export CLIENT2$(kubectl get pods -l nameclient2 -o jsonpath{.items[0].metadata.name})用 Hubble 开始观测流量先按照 Hubble 安装文档的说明在集群中启用 Hubble见 Hubble 安装文档。然后在另一个终端启用 Hubble 端口转发并持续观测来自client2Pod 的流量kubectl -n kube-system port-forward deployment/hubble-relay 4245:4245 hubble observe --from-pod $CLIENT2 -f此时从client2直接向两个后端服务发请求应该都能各自获得响应尚未经过代理kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/ kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/观察 Hubble 输出你会发现这些 Pod 之间的所有流都标记为to/from-stack、to/from-overlay或to/from-endpoint——此时还没有任何流量被标记为流向/来自代理to/from-proxy。这一观察结果成立的前提是集群中尚不存在其他会影响该流量的 L7 策略。再验证一个基线事实向这两个服务的不存在 URL/foo发请求会得到 404 响应kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/foo kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/foo添加 L7 策略让 Envoy 代理进入流量路径添加 L7 策略会使 Envoy 代理被引入该流量的转发路径。需要应用两个策略文件kubectl apply -f examples/kubernetes/servicemesh/envoy/client-egress-l7-http.yaml kubectl apply -f examples/kubernetes/servicemesh/envoy/client-egress-only-dns.yaml第一个文件 client-egress-l7-http.yaml 是对 L7 策略的完整定义值得逐段解读apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: client-egress-l7-http spec: description: Allow GET one.one.one.one:80/ and GET echo:8080/ from client2 endpointSelector: matchLabels: other: client # 只选中 client2唯一带 otherclient 标签的 Pod egress: # 允许 GET / 请求流向 echo Pod - toEndpoints: - matchLabels: k8s:kind: echo toPorts: - ports: - port: 8080 protocol: TCP rules: http: - method: GET path: / # 只允许 GET 方法 根路径其他路径全部丢弃 # 仅允许对 one.one.one.one 的 GET / 请求80 端口 - toFQDNs: - matchName: one.one.one.one toPorts: - ports: - port: 80 protocol: TCP rules: http: - method: GET path: /第二个文件 client-egress-only-dns.yaml 允许所有kind: client端点向 kube-system 命名空间中的 kube-dns/coredns 端点发起任意 DNS 查询matchPattern: *53 端口 ANY 协议它启用的是 DNS 层解析能力是toFQDNs类策略能工作的前提。添加 L7 策略后再次发请求kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/ kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/foo此时 Hubble 输出出现了关键变化出现了to-proxy流向——说明流量被重定向到了本地 Envoy 代理L7 可见性被激活输出中出现了第 7 层的 HTTP 协议信息例如HTTP/1.1 GET http://echo-service-1:8080/。关于 X-Forwarded-For 与 Envoy 头部清理Sanitizing需要注意Envoy 出于安全考虑会对部分 HTTP 头部做清理sanitize例如丢弃入站请求中由客户端伪造的X-Forwarded-For。如果后端服务需要感知真实客户端 IP可以让 Envoy 信任前序跳数、阻止其重写这些头部。做法是通过 Helm values 设置envoy.xffNumTrustedHopsL7PolicyIngressingress L7 策略 Envoy 监听器信任的跳数envoy.xffNumTrustedHopsL7PolicyEgressegress L7 策略 Envoy 监听器信任的跳数。从 install/kubernetes/values.yaml.tmpl 可见这两个值默认都是0不信任任何前序跳数。语义上的区别是egress 策略中前序跳数就是源 Pod 本身ingress 策略中前序跳数可能是源 Pod、egress policy transparent proxy、Cilium Ingress Controller、Cilium Gateway API或任何其他 Ingress 代理/基础设施。是否信任前序跳数存在安全含义客户端可伪造头部应结合部署环境自行评估。验证 L7 策略强制执行由于策略只允许对/路径的 GET 请求对其他任何 URL 的请求都会被丢弃。例如kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/fooHubble 输出会显示该 HTTP 请求被丢弃Jul 7 08:40:15.076: default/client2-8b4c4fd75-6pgvl:58586 - default/echo-service-1-97748874-n7758:8080 http-request DROPPED (HTTP/1.1 GET http://echo-service-1:8080/foo)同时 curl 端会收到403 Forbidden响应。至此验证了L7 策略由代理路径上的 Envoy 实例强制执行未匹配的请求在 L7 层被拦截。添加 Envoy 负载均衡与 URL 重写现在应用核心配置文件 envoy-traffic-management-test.yaml它定义了一个CiliumClusterwideEnvoyConfig集群级 CRD与命名空间级的CiliumEnvoyConfig对应CRD 定义见 examples/crds/v2/ciliumclusterwideenvoyconfigs.yamlkubectl apply -f examples/kubernetes/servicemesh/envoy/envoy-traffic-management-test.yaml该配置的完整内容如下这也是理解 Cilium 如何声明 Envoy xDS 资源的最佳范例apiVersion: cilium.io/v2 kind: CiliumClusterwideEnvoyConfig metadata: name: envoy-lb-listener spec: services: - name: echo-service-1 namespace: default - name: echo-service-2 namespace: default resources: # 1) ListenerHTTP 连接管理器指向名为 lb_route 的路由配置 - type: type.googleapis.com/envoy.config.listener.v3.Listener name: envoy-lb-listener filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: envoy-lb-listener rds: route_config_name: lb_route use_remote_address: true skip_xff_append: true http_filters: - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router # 2) RouteConfiguration按前缀 / 匹配加权转发并做正则重写 - type: type.googleapis.com/envoy.config.route.v3.RouteConfiguration name: lb_route virtual_hosts: - name: lb_route domains: [ * ] routes: - match: prefix: / route: weighted_clusters: clusters: - name: default/echo-service-1 weight: 50 - name: default/echo-service-2 weight: 50 retry_policy: retry_on: 5xx num_retries: 3 per_try_timeout: 1s regex_rewrite: pattern: google_re2: { } regex: ^/foo.*$ substitution: / # 3) 两个 EDS Cluster后端由 Cilium 自动同步 - type: type.googleapis.com/envoy.config.cluster.v3.Cluster name: default/echo-service-1 connect_timeout: 5s lb_policy: ROUND_ROBIN type: EDS outlier_detection: split_external_local_origin_errors: true consecutive_local_origin_failure: 2 - type: type.googleapis.com/envoy.config.cluster.v3.Cluster name: default/echo-service-2 connect_timeout: 3s lb_policy: ROUND_ROBIN type: EDS outlier_detection: split_external_local_origin_errors: true consecutive_local_origin_failure: 2结合 CRD schemaexamples/crds/v2/ciliumenvoyconfigs.yaml可以明确spec中三个关键字段的分工字段作用services指定流量应被重定向到 Envoy Listener 做 L7 负载均衡的 Kubernetes Service。这些服务的后端会自动通过 EDS 同步到 Envoylistener字段可指定重定向到哪个 Listener缺省用resources中的第一个 Listenerports可限定仅重定向哪些前端端口缺省全部backendServices指定后端自动通过 EDS 同步到 Envoy 的 Service但不将这些服务的流量转发到 Listener——即允许自定义 Listener 做加权负载均衡同时保留 Cilium 原生的 Service 负载均衡二者并存resourcesEnvoy xDS 资源列表仅接受五种类型envoy.config.listener.v3.Listener、envoy.config.route.v3.RouteConfiguration、envoy.config.cluster.v3.Cluster、envoy.config.endpoint.v3.ClusterLoadAssignment、envoy.extensions.transport_sockets.tls.v3.Secret另外两个与集群级 CRD 相关的细节来自 schema 描述namespace字段在CiliumEnvoyConfig中默认取 CEC 自身命名空间而在CiliumClusterwideEnvoyConfig中默认取defaultnodeSelector标准 K8s 标签选择器可限定配置生效的节点为 nil 时作用于所有节点。注意官方警告这些 Envoy 资源完全不被 Kubernetes 校验schema 使用x-kubernetes-preserve-unknown-fields: true。Envoy 资源中的任何错误只会在 Cilium Agent 观察这些 CRD 时暴露——kubectl apply会报告成功而节点本地 Envoy 实例的解析/安装可能已经失败。目前唯一的验证手段是查看 Cilium Agent 日志中的错误与警告Agent 还会对集群中相互冲突的 Envoy 资源打印警告日志。此外Cilium Ingress Controller 会在底层自行配置所需的 Envoy 资源如果你显式创建 Envoy 资源务必检查 Agent 日志确认没有冲突。原文见 warning.rst。配置效果50/50 负载均衡 路径重写该配置监听发往两个echo-服务之一的流量并在两个后端echo-服务之间做 50/50 加权负载均衡将路径/foo重写为/正则^/foo.*$→ 替换为/附带retry_policy遇到 5xx 时最多重试 3 次单试超时 1 秒两个 Cluster 均配置了outlier_detection连续 2 次本地发起失败即将端点剔除并采用ROUND_ROBIN负载均衡与 EDS 端点发现。由于路径重写之前被 403/404 挡住的/foo请求现在应该成功kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/foo但网络策略仍然会拦截任何没有被重写成/的路径。例如这个请求会导致数据包被丢弃并返回 403kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/barHubble 输出Jul 7 08:43:47.165: default/client2-8b4c4fd75-6pgvl:33376 - default/echo-service-2-76c584c4bf-874dm:8080 http-request DROPPED (HTTP/1.1 GET http://echo-service-1:8080/bar)注意这里还有一个微妙但重要的行为请求虽然发往echo-service-1但策略判定依据的 HTTP 目标仍然是原始 URIhttp://echo-service-1:8080/bar——重写发生在 Envoy 转发层策略评估看到的是代理解析出的 L7 请求二者协作才实现重写放行、未重写拦截的效果。观察负载均衡行为对同一个后端服务连续发起多次请求你会在 Hubble 输出中观察到大约一半的请求实际由另一个后端处理例如Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 - kube-system/coredns-64897985d-8jhhn:53 L3-L4 REDIRECTED (UDP) Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 - kube-system/coredns-64897985d-8jhhn:53 to-proxy FORWARDED (UDP) Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 - kube-system/coredns-64897985d-8jhhn:53 dns-request FORWARDED (DNS Query echo-service-1.default.svc.cluster.local. AAAA) Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 - kube-system/coredns-64897985d-8jhhn:53 dns-request FORWARDED (DNS Query echo-service-1.default.svc.cluster.local. A) Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-1:8080 none REDIRECTED (TCP Flags: SYN) Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: SYN) Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK) Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK, PSH) Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-2-76c584c4bf-874dm:8080 L3-L4 REDIRECTED (TCP Flags: SYN) Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: SYN) Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: ACK) Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: ACK, PSH) Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-2-76c584c4bf-874dm:8080 http-request FORWARDED (HTTP/1.1 GET http://echo-service-1:8080/) Jul 7 08:45:25.811: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK, FIN) Jul 7 08:45:25.811: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK) Jul 7 08:45:30.811: default/client2-8b4c4fd75-6pgvl:57942 - default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: ACK, FIN)这段输出清晰地还原了整条数据路径客户端 SYN 被REDIRECTED到本地代理to-proxyEnvoy 通过weighted_clusters把连接交给echo-service-2的端点to-endpointL7 请求http-request FORWARDED中显示的 URI 仍为重写前的http://echo-service-1:8080/。这就是客户端只看到一个 Service实际流量被 Envoy 按权重分摊到两个 Service的完整证据链。小结这套机制的关键点两层职责分离L7 网络策略CiliumNetworkPolicy的rules.http负责允许/丢弃CiliumEnvoyConfig负责如何转发加权、重写、重试、剔除异常端点——二者叠加生效路径重写不能绕过策略评估。声明式 xDSCilium 把 Envoy 的 Listener/Route/Cluster 作为 CRD 字段直接暴露spec.resources后端端点通过 EDS 自动同步无需手写ClusterLoadAssignment也无需独立部署 Envoy 控制面。无 schema 校验的现实约束resources字段不经过 K8s 校验配置正确性最终靠 Cilium Agent 日志确认这是运维该特性时最重要的检查动作。可观测性闭环Hubble 的to-proxy/http-request事件让代理是否介入、L7 内容是什么、策略在哪一层丢弃了请求全部可视是调试 L7 流量管理问题的第一入口。相关文件索引文档源文件envoy-traffic-management.rst、warning.rst示例清单test-application.yaml、client-egress-l7-http.yaml、client-egress-only-dns.yaml、envoy-traffic-management-test.yamlCRD 定义ciliumenvoyconfigs.yaml、ciliumclusterwideenvoyconfigs.yaml相关主题同一 servicemesh 目录下的延伸阅读Envoy 负载均衡、L7 流量管理、流量染色/灰度【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考