Cilium 本地重定向策略(LRP)实战指南:从 cilium-dbg lrp 命令到 eBPF 数据面原理
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
导读
本文以 Cilium 的cilium-dbg lrp命令族为主线,系统讲解 Local Redirect Policy(本地重定向策略,简称 LRP)的完整使用闭环:如何用命令行管理策略、如何在 Kubernetes 中通过CiliumLocalRedirectPolicyCRD 定义AddressMatcher与ServiceMatcher两种策略类型、如何在 eBPF 数据面验证重定向生效,并深入节点级 DNS 缓存等典型场景。读完本文,你将掌握从策略创建、查询到故障排查的完整实战技能,同时理解 LRP 在 pkg/loadbalancer/redirectpolicy 控制器与 BPF 数据面中的底层工作方式。
cilium-dbg lrp 命令概览
cilium-dbg lrp是 Cilium 命令行工具中负责管理本地重定向策略的子命令族,其定义位于 cilium-dbg/cmd/lrp.go:
var LRPCmd = &cobra.Command{ Use: "lrp", Short: "Manage local redirect policies", }命令文档见 Documentation/cmdref/cilium-dbg_lrp.md,其顶层命令本身不执行任何操作,仅作为命名空间承载list子命令:
cilium-dbg lrp顶层选项
| 选项 | 说明 |
|---|---|
-h, --help | 显示lrp命令的帮助信息 |
继承自父命令的全局选项
所有cilium-dbg子命令都会继承以下全局选项,在通过kubectl exec进入 Cilium agent Pod 内执行时通常无需改动,但了解其含义有助于在独立运行调试时正确连接 API:
| 选项 | 默认值 | 说明 |
|---|---|---|
--config string | $HOME/.cilium.yaml | 配置文件路径 |
-D, --debug | 关闭 | 输出调试日志 |
-H, --host string | 本地 Unix socket | 服务端 API 的 URI |
--log-driver strings | - | 日志端点(示例:syslog) |
--log-opt map | - | 日志驱动选项(示例:format=json) |
核心子命令:cilium-dbg lrp list
cilium-dbg lrp list用于列出当前节点上生效的本地重定向策略,完整命令文档见 Documentation/cmdref/cilium-dbg_lrp_list.md:
cilium-dbg lrp list [flags]专属选项
| 选项 | 说明 |
|---|---|
-h, --help | 显示list命令帮助 |
-o, --output string | 输出格式,支持json、yaml、jsonpath='{}' |
list同时注册了别名ls(源码见 cilium-dbg/cmd/lrp_list.go),因此cilium-dbg lrp ls与cilium-dbg lrp list等价。
输出字段解读
不带-o参数时,命令通过tabwriter输出人类可读的表格(printLRPList):
LRP namespace LRP name FrontendType Matching Service每个策略条目包含四个核心列:
- LRP namespace / LRP name:策略对象的元数据,与
kubectl get ciliumlocalredirectpolicies中的命名空间与名称一一对应; - FrontendType:策略匹配的前端类型,常见取值为
clusterIP + all svc ports(ServiceMatcher 且未限定端口)等; - Matching Service:命中的 Kubernetes Service(格式为
namespace/serviceName)。
每个策略下方还会逐行打印前端到后端的映射明细,格式由 getPrintableMapping 生成:
<frontend-ip>:<frontend-port>/<protocol> -> <backend-ip>:<backend-port>(<pod-namespace>/<pod-name>), ...例如在节点级 DNS 缓存场景中的典型输出:
LRP namespace LRP name FrontendType Matching Service kube-system nodelocaldns clusterIP + all svc ports kube-system/kube-dns | 10.96.0.10:53/UDP -> 10.244.1.49:53(kube-system/node-local-dns-72r7m), | 10.96.0.10:53/TCP -> 10.244.1.49:53(kube-system/node-local-dns-72r7m),这表示发往 ClusterIP10.96.0.10的 UDP/TCP53端口流量,会被重定向到本节点node-local-dns-72r7mPod 的53端口。
底层实现:调用链
从源码调用链看,listLRPs会先通过 Cilium agent 的客户端调用client.GetLRPs()获取策略列表(cilium-dbg/cmd/lrp_list.go),该 API 返回[]*models.LRPSpec,其中FrontendMappings携带每个前端地址对应的本节点后端 Pod 列表。当指定-o参数时,命令改用结构化输出(json/yaml/jsonpath),便于脚本与自动化工具消费,此时每个条目会完整暴露LRPSpec的全部字段。
Local Redirect Policy 功能原理
理解命令输出前,需要先掌握 LRP 的定位:LRP 使发往某个 IP:端口/协议 元组或 Kubernetes Service 的 Pod 流量,在节点本地被 eBPF 重定向到本节点上的后端 Pod,且后端 Pod 的命名空间需与策略命名空间一致。LRP 通过CiliumLocalRedirectPolicyCustomResourceDefinition 进行配置,完整功能文档见 Documentation/network/kubernetes/local-redirect-policy.rst。
策略 CRD 的类型定义位于 pkg/k8s/apis/cilium.io/v2/clrp_types.go,其 Spec 由三部分组成:
| 字段 | 必填 | 说明 |
|---|---|---|
redirectFrontend | 是 | 待重定向的流量匹配条件,addressMatcher与serviceMatcher二选一(不可同时为空) |
redirectBackend | 是 | 重定向目标:localEndpointSelector选择本节点上的后端 Pod,toPorts指定目标端口 |
skipRedirectFromBackend | 否(默认false) | 见后文“高级配置” |
注意 CRD 通过 OpenAPIXValidation规则将redirectFrontend、redirectBackend、skipRedirectFromBackend标记为不可变(immutable),即策略一旦创建后这些字段不可修改,这也是官方文档强调“更新需删除重建”的底层原因。
策略如何处理:控制器视角
从源码结构看,LRP 的编排由 pkg/loadbalancer/redirectpolicy 包负责:控制器监听CiliumLocalRedirectPolicy资源与 Pod/Service 变更,动态维护LRPSpec与后端映射关系,最终将结果同步为LocalRedirect类型的负载均衡服务条目;同一目录下的testdata/*.txtar测试用例(如 pkg/loadbalancer/redirectpolicy/testdata/address.txtar、node-local-dns.txtar)覆盖了端口命名、地址冲突、Pod 就绪状态等边界场景,可用于深入理解控制器行为。
启用 LRP:前置条件
LRP 默认未启用,需要通过 Helm 开启并重启相关组件:
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \ --set localRedirectPolicies.enabled=true kubectl rollout restart deploy cilium-operator -n kube-system kubectl rollout restart ds cilium -n kube-system随后验证 agent 与 operator Pod 恢复 Running,并确认 CRD 已注册:
kubectl -n kube-system get pods -l k8s-app=cilium kubectl -n kube-system get pods -l name=cilium-operator kubectl get crds | grep ciliumlocalredirectpolicies # ciliumlocalredirectpolicies.cilium.io 2020-08-24T05:31:47Z数据面选型:四种 Helm 组合
LRP 支持 socket 级负载均衡与 tc 负载均衡两种数据面实现,官方文档给出了四种经过验证的 Helm 组合:
完整 kube-proxy 替换(用 Cilium eBPF 实现替代 kube-proxy 并启用 LRP):
kubeProxyReplacement: true localRedirectPolicies: enabled: true绕过 Pod 命名空间内的 socket 级负载均衡(适用于 Pod 命名空间存在自定义重定向规则、与 socket 负载均衡冲突的场景):
kubeProxyReplacement: true socketLB: hostNamespaceOnly: true localRedirectPolicies: enabled: true仅启用 socket 级负载均衡(保留 kube-proxy 处理整体 Service,仍可使用 LRP):
kubeProxyReplacement: false socketLB: enabled: true localRedirectPolicies: enabled: true仅处理来自 Pod 的 ClusterIP 访问(其余 kube-proxy 替换全部关闭;注意此配置下宿主机命名空间的 Pod 流量不受 LRP 处理):
kubeProxyReplacement: false localRedirectPolicies: enabled: true
部署示例:后端与客户端 Pod
以官方示例 examples/kubernetes-local-redirect/backend-pod.yaml 部署后端 Pod(其标签、容器端口/协议需与后续 LRP 的localEndpointSelector、toPorts一致):
kubectl apply -f examples/kubernetes-local-redirect/backend-pod.yaml kubectl get pods | grep lrp-pod # lrp-pod 1/1 Running 0 46s再部署用于产生流量的客户端 Pod(examples/kubernetes-dns/dns-sw-app.yaml):
kubectl create -f examples/kubernetes-dns/dns-sw-app.yaml kubectl wait pod/mediabot --for=condition=Ready策略类型一:AddressMatcher(按 IP:端口 匹配)
AddressMatcher 用IP 地址 + L4 端口/协议精确匹配不属于任何 Kubernetes Service 的流量。要点:
- 前端
toPorts中配置多个端口时端口必须命名,端口名用于前端端口与后端端口的映射; redirectBackend.toPorts指定的端口必须真实存在于后端 Pod 的 spec 中;localEndpointSelector用于在本节点选择接收重定向流量的后端 Pod。
完整示例见 examples/kubernetes-local-redirect/lrp-addrmatcher.yaml:将发往169.254.169.254:8080/TCP的流量重定向到标签为app=proxy、监听80/TCP的后端 Pod:
apiVersion: "cilium.io/v2" kind: CiliumLocalRedirectPolicy metadata: name: "lrp-addr" spec: redirectFrontend: addressMatcher: ip: "169.254.169.254" toPorts: - port: "8080" protocol: TCP redirectBackend: localEndpointSelector: matchLabels: app: proxy toPorts: - port: "80" protocol: TCP应用并验证:
kubectl apply -f examples/kubernetes-local-redirect/lrp-addrmatcher.yaml kubectl get ciliumlocalredirectpolicies | grep lrp-addr从类型定义看(clrp_types.go),addressMatcher.ip支持 IPv4 与 IPv6,toPorts[].protocol仅接受TCP与UDP,toPorts[].port必须是合法的 uint16 端口字符串。
验证:service list 与 curl
在与lrp-pod同一节点的 Cilium Pod 中执行cilium-dbg service list,可以看到 eBPF kube-proxy replacement 创建了一条LocalRedirect类型的服务条目,其后端正是策略选中的lrp-pod的 IP:
kubectl describe pod lrp-pod | grep 'IP:' # IP: 10.16.70.187 kubectl exec -it -n kube-system cilium-5ngzd -- cilium-dbg service list # ID Frontend Service Type Backend # 4 172.20.0.51:80 LocalRedirect 1 => 10.16.70.187:80从客户端 Pod 发起请求验证重定向生效:
kubectl exec mediabot -- curl -I -s http://169.254.169.254:8080/index.html # HTTP/1.1 200 OK # Server: nginx/1.19.2最后用 tcpdump(需在lrp-pod所在节点执行)确认流量确实打到了后端 Pod 的 80 端口:
sudo tcpdump -i any -n port 80 # 01:36:24.608566 IP 10.16.215.55.60876 > 10.16.70.187.80: Flags [S], seq 2119454273, ... # 01:36:24.609007 IP 10.16.70.187.80 > 10.16.215.55.60876: Flags [P.], seq 1:239, ... HTTP/1.1 200 OK集群级地址白名单
可通过 Helm 的localRedirectPolicies.addressMatcherCIDRs选项在全集群范围内约束允许被 AddressMatcher 重定向的地址,例如只允许169.254.169.254:
localRedirectPolicies: enabled: true addressMatchCIDRs: - 169.254.169.254/32超出白名单的地址会被 cilium-agent 拒绝并输出警告日志。
冲突保护
AddressMatcher 仅面向不属于任何 Kubernetes Service 的 IP。若redirectFrontend.addressMatcher中的 IP:端口/协议 与某个ClusterIPService 前端重合,策略不会覆盖该地址——Cilium 拒绝覆盖其他 Service 拥有的前端,并记录日志:
LocalRedirectPolicy matches an address owned by an existing service => refusing to override此时应改用 ServiceMatcher 类型。
策略类型二:ServiceMatcher(按 Service 匹配)
ServiceMatcher 用Kubernetes Service 的名称与命名空间匹配需要重定向的流量,要求 Service 类型为clusterIP。要点:
- 若
redirectFrontend不指定toPorts,则该 Service 的全部端口流量都会被重定向;只重定向部分端口时需在 spec 中显式列出,且多端口时必须命名(端口名用于前后端端口映射); - 策略应用后,eBPF kube-proxy replacement 创建的既有 Service 条目会被替换为类型
LocalRedirect的新条目,且该条目只允许包含节点本地的后端 Pod。
先部署 Service(examples/kubernetes-local-redirect/k8s-svc.yaml)并确认其 ClusterIP 与 Service 条目:
kubectl apply -f examples/kubernetes-local-redirect/k8s-svc.yaml kubectl get service | grep 'my-service' # my-service ClusterIP 172.20.0.51 <none> 80/TCP 2d7h kubectl exec -it -n kube-system ds/cilium -- cilium-dbg service list # ID Frontend Service Type Backend # 4 172.20.0.51:80 ClusterIP再创建 ServiceMatcher 类型的策略(examples/kubernetes-local-redirect/lrp-svcmatcher.yaml):
apiVersion: "cilium.io/v2" kind: CiliumLocalRedirectPolicy metadata: name: "lrp-svc" spec: redirectFrontend: serviceMatcher: serviceName: my-service namespace: default redirectBackend: localEndpointSelector: matchLabels: app: proxy toPorts: - port: "80" protocol: TCP应用后再次查看 Service 条目,可见其类型已变为LocalRedirect,后端为本节点上被策略选中的 Pod:
kubectl apply -f examples/kubernetes-local-redirect/lrp-svcmatcher.yaml kubectl get ciliumlocalredirectpolicies | grep svc kubectl exec -it -n kube-system cilium-5ngzd -- cilium-dbg service list # ID Frontend Service Type Backend # 4 172.20.0.51:80 LocalRedirect 1 => 10.16.70.187:80客户端请求与 tcpdump 验证方式与 AddressMatcher 一致,只是目标改为 ClusterIP:
kubectl exec mediabot -- curl -I -s http://172.20.0.51/index.html # HTTP/1.1 200 OK高级配置:skipRedirectFromBackend
默认情况下(skipRedirectFromBackend: false),包括后端 Pod 自身在内的所有来源,只要流量匹配前端都会被重定向。某些场景下用户希望后端 Pod 发往策略前端的流量不要被重定向回自身,而是直接转发到原始前端地址,此时可在 spec 中设置:
spec: skipRedirectFromBackend: true该能力依赖getsockopt()的SO_NETNS_COOKIE选项判断来源网络命名空间:该选项自 Linux 内核 5.7 起对 BPF 程序可用,5.12 起对用户空间暴露,因此要求内核版本 >= 5.12。此外,自 Cilium 1.16.0 起,若要对已应用策略启用该配置,需要先删除并重新创建既有策略及其选中的后端 Pod(该字段同样被 CRD 标记为不可变)。
已知限制
- 不迁移既有连接:LRP 只重定向策略生效后新建的连接;对策略匹配范围内已存在的、指向远端 Pod 的活动连接可能不会重定向。要确保全部连接都走本地重定向,应在配置策略后重启客户端 Pod。
- 不支持更新:LRP 更新目前不被支持,任何变更都需要删除旧策略后重新创建(与 CRD 的字段不可变校验一致)。
典型场景:Node-local DNS Cache(节点级 DNS 缓存)
LRP 最经典的用例是让集群内 DNS 查询优先命中节点本地的 DNS node-cache Pod,避免应用 Pod 的 DNS 请求跨节点转发到kube-dns后端,从而降低延迟并减少网络开销。
部署 node-local-dns
两种部署方式:
- 快速部署:使用仓库提供的 examples/kubernetes-local-redirect/node-local-dns.yaml,其默认值已填充
__PILLAR_LOCAL_DNS__与__PILLAR_DNS_DOMAIN__;若集群 DNS Service 不同,需按官方 NodeLocal DNSCache 配置说明替换__PILLAR__DNS__SERVER__等模板变量:kubedns=$(kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}) \ && sed -i "s/__PILLAR__DNS__SERVER__/$kubedns/g;" node-local-dns.yaml kubectl apply -f node-local-dns.yaml - 手动配置:需保证 node-local DNS 镜像版本 >= 1.15.16(以获得禁用 dummy 网卡创建/删除的开关);为 node-cache 追加
-skipteardown=true、-setupinterface=false、-setupiptables=false参数;DaemonSet 设置hostNetwork: false使其运行在非宿主机命名空间;Corefile 中绑定0.0.0.0而非静态 IP,并相应调整 health-check 与 readinessProbe 的地址配置。
部署对应的 LRP
使用 examples/kubernetes-local-redirect/node-local-dns-lrp.yaml 将 DNS 流量导向本地缓存:
kubectl apply -f examples/kubernetes-local-redirect/node-local-dns-lrp.yaml注意事项:示例默认以kube-dns作为集群 DNS Service,若实际名称不同需修改;策略命名空间需与集群 DNS Service 所在命名空间一致;示例使用dns、dns-tcp两个端口名,需与部署 yaml 中的端口名保持一致。
验证与排障
所有node-local-dnsPod 就绪后,DNS 流量即会先走本地缓存。可通过访问<node-local-dns Pod IP>:9253/metrics观察coredns_dns_requests_total指标是否随应用 Pod 发起 DNS 请求而增长来确认。
排障三步走:
确认 node-local-dns Pod 运行就绪:
kubectl --namespace kube-system get pods --selector=k8s-app=node-local-dns # node-local-dns-72r7m 1/1 Running 0 2d2h用本文主角
cilium-dbg lrp list检查策略是否在所有 agent 上正确生效:kubectl exec -it cilium-mhnhz -n kube-system -- cilium-dbg lrp list # LRP namespace LRP name FrontendType Matching Service # kube-system nodelocaldns clusterIP + all svc ports kube-system/kube-dns # | 10.96.0.10:53/UDP -> 10.244.1.49:53(kube-system/node-local-dns-72r7m), # | 10.96.0.10:53/TCP -> 10.244.1.49:53(kube-system/node-local-dns-72r7m),检查
LocalRedirectService 条目是否创建:若条目缺失,可能是策略应用与 node-local DNS DaemonSet Pod 资源之间存在竞态,可尝试重启 node-local DNS DaemonSet Pod;问题持续时建议在官方仓库提交 issue 并附带 sysdump 数据:kubectl exec -it cilium-mhnhz -n kube-system -- cilium-dbg service list | grep LocalRedirect # 11 10.96.0.10:53 LocalRedirect 1 => 10.244.1.49:53 (active)
总结
Local Redirect Policy 借助 Cilium eBPF 数据面,将“匹配指定地址/Service 的流量在节点内就地重定向”变成了声明式配置:cilium-dbg lrp list负责从 agent 侧快速查看策略与前后端映射,CiliumLocalRedirectPolicyCRD 负责声明AddressMatcher与ServiceMatcher两种匹配语义,pkg/loadbalancer/redirectpolicy控制器与LocalRedirect服务条目则构成其数据面落地路径。无论是节点级 DNS 缓存、还是将特定 IP:端口 流量导向本机代理,掌握上述命令、配置与验证手段即可在生产集群中可靠落地,并通过cilium-dbg lrp list与cilium-dbg service list的组合完成日常巡检与排障。
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考