news 2026/9/14 18:01:08

Cilium Node IPAM LB:用节点 IP 直接充当 LoadBalancer 入口的免云厂商外部负载均衡方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cilium Node IPAM LB:用节点 IP 直接充当 LoadBalancer 入口的免云厂商外部负载均衡方案

Cilium Node IPAM LB:用节点 IP 直接充当 LoadBalancer 入口的免云厂商外部负载均衡方案

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

Node IPAM LB(Node-IPAM LoadBalancer)是 Cilium 实现的一种 KubernetesLoadBalancerService IPAM 方案:它由 cilium-operator 内的控制器监听 Service、EndpointSlice 与 Node 三类资源,将满足条件的节点 IP 直接写入Service.status.loadBalancer.ingress,从而"把节点 IP 广播为负载均衡入口"。该特性适用于无法控制底层网络、既不能用 Cilium L2 通告也不能用 BGP 通告 VIP 的托管环境。读完本文,你将理解其节点候选与 IP 选择规则、掌握通过 Helm 启用nodeIPAM.enableddefaultLBServiceIPAM的完整配置方法,并能看懂externalTrafficPolicyClusterLocal时的行为差异及其边界限制。

什么是 Node IPAM LB

Node IPAM LoadBalancer 的设计灵感来自 k3s 的 "ServiceLB" 特性。它的核心思想非常直接:不申请任何云上负载均衡资源,不创建 VIP,而是直接把集群节点的 IP 地址作为 LoadBalancer Service 的 ingress 地址对外暴露。工作方式是获取被选中节点的地址(status.addresses),然后将其"广播"(advertise)到 Service 的status.loadBalancer.ingress字段中。

这一设计在以下场景中尤其有用:

  • 运行环境是托管 Kubernetes 服务,你不控制底层网络,无法在外部交换机上配置 L2 MAC 通告;
  • 环境没有可用的 BGP 对等关系,Cilium 的 BGP 控制器无从通告 VIP;
  • 你希望避免依赖云厂商的 LB API(Cilium 的lbipam方案需要调用云 API 创建 NLB/ELB),转而仅用集群内已有信息完成"外部入口"的语义。

从源码结构看,Node IPAM 的全部运行时逻辑位于 cilium-operator 中,核心实现集中在 operator/pkg/nodeipam 目录:

  • nodesvclb.go:nodeSvcLBReconciler控制器,负责 Service 的调和(reconcile)与 IP 广播;
  • node_predicates.go:节点是否纳入负载均衡候选的判定谓词;
  • cell.go:Cilium hive 框架的模块注册与启用开关;
  • pkg/nodeipamconfig/cell.go:配置项--enable-node-ipamloadBalancerClass常量定义。

控制器通过 operator/cmd/root.go 中的nodeipam.Cell注册进 operator 主流程,因此Node IPAM 是 operator 侧能力,不是 agent 侧能力——启用开关下发给 operator 进程。

工作机制:从 Service 到节点 IP 的完整链路

控制器监听哪些对象

nodeSvcLBReconciler.SetupWithManager注册了三类 watch(见 nodesvclb.go#L48-L63):

  1. Service:只处理"受支持"的 Service(见下文判定逻辑),作为 reconcile 的主对象;
  2. EndpointSlice:当某个 EndpointSlice 更新时,通过其kubernetes.io/service-name标签反查所属 Service 并触发该 Service 重新调和(enqueueRequestForEndpointSlice,nodesvclb.go#L65-L86)。这是为了在externalTrafficPolicy: Local模式下及时反映 Pod 分布变化;
  3. Node:当一个通过基础谓词检查的节点发生变化时,列出全部受支持的 Service 并逐一入队(enqueueRequestForNode,nodesvclb.go#L88-L122),保证节点增删改时 ingress 列表随之刷新。

哪些 Service 会被处理

isServiceSupported(nodesvclb.go#L151-L163)的判定条件是:

  • Service 未被标记删除;
  • spec.type必须为LoadBalancer
  • spec.loadBalancerClass等于io.cilium/node(常量NodeSvcLBClass,定义于 pkg/nodeipamconfig/cell.go#L38);
  • 若 Service未设置loadBalancerClass,则只有当defaultLBServiceIPAM配置为nodeipam时才会被接管(该标志在 pkg/lbipamconfig/cell.go#L57 中定义为DefaultLBClassNodeIPAM = "nodeipam")。

这一设计让 Node IPAM 能与其他 LB 方案(如云厂商lbipam)按loadBalancerClass共存,互不干扰;测试用例Test_nodeIPAM_Reconcile中的 "unsupported service reset" 子用例(nodesvclb_test.go#L361-L382)专门验证了:对于不受支持的 Service,控制器不会去改动它已有的status.loadBalancer.ingress

候选节点的筛选规则

节点筛选分三层,全部实现于getRelevantNodesshouldIncludeNode

第一层:基础排除谓词(KEP-3458 对齐)。node_predicates.go 中的shouldIncludeNode显式声明其语义与 kubernetes/cloud-provider 项目中的stableNodeSetPredicates保持一致,包含三条:

  • 节点无deletionTimestamp(正在删除的节点排除);
  • 节点没有标签node.kubernetes.io/exclude-from-external-load-balancersv1.LabelNodeExcludeBalancers);
  • 节点没有污点ToBeDeletedByClusterAutoscaler(Cluster Autoscaler 在删除节点前会打上该污点)。

被排除的节点会被记录到日志("Skipping Nodes that are being deleted or are excluded from load balancers"),便于排查为什么某个节点 IP 没有出现在 ingress 中。

第二层:externalTrafficPolicy语义。对应文档中最重要的行为分支(nodesvclb.go#L165-L198):

  • externalTrafficPolicy: Cluster:不对节点做任何基于 Pod 的过滤,所有通过第一层的节点都是候选
  • externalTrafficPolicy: Local:列出该 Service 在对应 namespace 下的全部 EndpointSlice,只收集conditions.ready不为 false 且带有nodeName的 endpoint,得到"实际运行该 Service 后端 Pod 的节点集合",只保留这些节点。

这正是文档警告项的源码依据:当externalTrafficPolicy: Local却没有(或只有空的/假数据的)EndpointSlice 关联到该 Service 时,候选节点集合为空,Node IPAM 无法广播任何地址。文档特别提醒:Cilium 自研的 GatewayAPI / Ingress 实现使用的是 dummy Endpoint,其 LoadBalancer Service 的externalTrafficPolicy不能设为Local(相关模板见 install/kubernetes/cilium/templates/cilium-ingress-service.yaml);其他多数 GatewayAPI 实现预计不受影响,若行为异常应检查其 EndpointSlice 或改回Cluster

第三层:按 Service 注解限定节点范围。若 Service 带有注解io.cilium.nodeipam/match-node-labels(常量NodeSvcLBMatchLabelsAnnotation,pkg/nodeipamconfig/cell.go#L39),其值按 KubernetesLabel Selector语法解析后作为 List Nodes 的 label selector,把候选范围进一步收窄——适合"只有部分节点能接收外部入站流量"的拓扑(例如仅特定 zone/az 的节点挂了公网 EIP)。解析失败会报错并终止本次调和,空结果会输出告警日志"No Nodes found with configured label selector"

IP 选择与广播

最终 IP 集合由getNodeLoadBalancerIngresses(nodesvclb.go#L265-L308)计算,规则如下:

  1. 只读取节点status.addresses中类型为ExternalIPInternalIP的地址,其他类型忽略;
  2. 按 Service 的spec.ipFamilies过滤地址族——只有当 ipFamilies 包含IPv4时 IPv4 地址才被计入,IPv6同理,因此单栈集群只广播单栈地址,双栈集群两种都广播;
  3. 优先级:只要存在任何节点 ExternalIP,就全部使用 ExternalIP;否则回退到 InternalIP(注意是"整体二选一",不是逐节点回退);
  4. 去重后按字典序排序,保证输出稳定、diff 友好;
  5. 每个地址生成一个LoadBalancerIngress{IP: ...},整体写回svc.Status.LoadBalancer.Ingress(nodesvclb.go#L143-L148)。

单元测试覆盖了上述各分支:单栈下 IPv4/IPv6 × internal/external 四种单地址组合、双栈广播(nodesvclb_test.go#L384-L420)、defaultLBServiceIPAM模式(Test_nodeIPAM_defaultIPAM_Reconcile)以及 Cilium 自定义资源联动场景(Test_nodeIPAM_CiliumResources_Reconcile),可作为行为核对的权威依据。

启用与使用

通过 Helm 启用

Cilium 的 Node IPAM默认禁用。Helm 值定义在 install/kubernetes/cilium/values.yaml#L2402-L2405:

nodeIPAM: # -- Configure Node IPAM # ref: https://docs.cilium.io/en/stable/network/node-ipam/ enabled: false

安装时启用(全新部署):

helm install cilium cilium/cilium --namespace kube-system --set nodeIPAM.enabled=true

在已有部署上启用:

helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set nodeIPAM.enabled=true kubectl -n kube-system rollout restart deployment/cilium-operator

启用后,Helm 模板 install/kubernetes/cilium/templates/cilium-configmap.yaml#L1279-L1283 会在配置中写入:

enable-node-ipam: "true" default-lb-service-ipam: "lbipam"

这两个键分别对应 operator 的--enable-node-ipam布尔开关(pkg/nodeipamconfig/cell.go#L33-L35)与defaultLBServiceIPAM。从源码结构看,operator/pkg/nodeipam/cell.go 中的registerNodeSvcLBReconciler会在NodeIPAMConfig.IsEnabled()为 false 或 Kubernetes clientset 未启用时直接跳过控制器注册——这也解释了为什么改动该值后需要重启 cilium-operator 才能生效。

声明一个 Node IPAM 管理的 Service

最小可用的 Service 需要满足两个条件:type: LoadBalancerloadBalancerClass: io.cilium/node

apiVersion: v1 kind: Service metadata: name: node-ipam-svc namespace: default spec: type: LoadBalancer loadBalancerClass: io.cilium/node selector: app: myapp ports: - port: 80 targetPort: 8080

调谐完成后,kubectl get svc node-ipam-svc的 EXTERNAL-IP 列会列出被选中节点的 IP(按前述 ExternalIP 优先规则)。

若希望未声明loadBalancerClass的 LoadBalancer Service 也默认走 Node IPAM,把defaultLBServiceIPAM设为nodeipam(Helm 值defaultLBServiceIPAM,取值范围lbipam/nodeipam/none,默认lbipam)。

用注解限定承载节点

要在多个节点中只让部分节点接收外部流量,给 Service 加注解io.cilium.nodeipam/match-node-labels,值为标准 Label Selector:

apiVersion: v1 kind: Service metadata: name: node-ipam-svc annotations: # 只选择带 role=ingress 标签的节点广播 IP io.cilium.nodeipam/match-node-labels: role=ingress spec: type: LoadBalancer loadBalancerClass: io.cilium/node ...

节点排除机制速查

节点状态是否参与广播依据
带标签node.kubernetes.io/exclude-from-external-load-balancersnode_predicates.gonodeIncludedPredicate
带污点ToBeDeletedByClusterAutoscalernode_predicates.gonodeUnTaintedPredicate
deletionTimestamp非零nodeNotDeletedPredicate
match-node-labelsselector 排除nodesvclb.go#L210-L221
eTP=Local 且无 ready 后端 Pod 落在此节点nodesvclb.go#L184-L195

已知限制与排查

  1. eTP=Local 依赖真实 EndpointSlice。文档明确警告:externalTrafficPolicy: Local但没有对应(或只有 dummy)EndpointSlice 的 Service 无法工作。因此 Cilium 自研的 GatewayAPI/Ingress 实现(其 LoadBalancer Service 使用 dummy Endpoint,见 install/kubernetes/cilium/templates/cilium-ingress-service.yaml)不能设Local,只能使用Cluster
  2. ExternalIP 是"全有或全无"。IP 选择逻辑是集合级回退:只要任一节点有 ExternalIP,ingress 就只包含 ExternalIP 集合,InternalIP 不会被混合进来。在异构网络(部分节点有公网 IP、部分只有内网 IP)中这一点需要特别注意。
  3. 广播的是节点地址,不是 VIP。客户端会看到多个入口 IP,端口转发依赖节点上内核/endpoint 对 Service 的处理路径;若外部防火墙只放行单一 VIP,该方案不适用。
  4. 排查入口:operator 日志(控制器名为service-nodeipam,nodesvclb.go#L61)会输出 "Reconciling Service"、"Skipping Nodes..."、"No Nodes found after filtering..." 等关键事件;operator 侧还有 feature 指标(pkg/metrics/features/operator中的 NodeIPAM enabled 指标)可用于确认特性处于启用状态。

小结

Node IPAM LB 用"广播节点 IP"这一最朴素的手段补齐了 Cilium 在无 L2/BGP 能力环境下的 LoadBalancer 语义缺口:

  • 入口type: LoadBalancer+loadBalancerClass: io.cilium/node,或通过defaultLBServiceIPAM: nodeipam使其成为默认方案;
  • 启用:Helm 值nodeIPAM.enabled=true,映射为 operator 的enable-node-ipam开关,改动后需重启cilium-operator
  • 行为:eTP=Cluster 选全部合格节点,eTP=Local 选运行后端 Pod 的节点;io.cilium.nodeipam/match-node-labels注解进一步收窄范围;IP 广播遵循ipFamilies过滤、ExternalIP 优先、去重排序规则;
  • 边界:节点排除谓词与 KEP-3458 的 cloud-provider 行为对齐,Cluster Autoscaler 缩容节点不会残留在 ingress 中。

核心实现、开关与测试分别位于 operator/pkg/nodeipam、pkg/nodeipamconfig 与 operator/pkg/nodeipam/nodesvclb_test.go,可作为进一步阅读源码与核对行为的起点。

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

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

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

免费一键导出微信聊天记录:WeChatMsg 完整指南

免费一键导出微信聊天记录:WeChatMsg 完整指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg …

作者头像 李华
网站建设 2026/9/14 17:57:07

MATLAB实现RRT-ANN混合算法优化无人机三维路径规划

1. 项目背景与核心价值无人机三维路径规划一直是自主飞行系统的关键技术瓶颈。传统RRT算法虽然在高维空间搜索中表现优异,但随机采样特性导致路径质量不稳定、收敛速度慢等问题。我在实际无人机项目中多次遇到这样的困境:当环境复杂度提升时,…

作者头像 李华
网站建设 2026/9/14 17:56:53

安装 PhotoGIMP 后 GIMP 界面没有任何变化,怎么排查?

安装 PhotoGIMP 后 GIMP 界面没有任何变化,怎么排查? 【免费下载链接】PhotoGIMP A Patch for GIMP 3 for Photoshop Users 项目地址: https://gitcode.com/GitHub_Trending/ph/PhotoGIMP PhotoGIMP 装完之后打开 GIMP,界面却和安装前…

作者头像 李华
网站建设 2026/9/14 17:54:39

Vitest deps 配置完全指南:依赖解析、预打包优化与 CJS 互操作

Vitest deps 配置完全指南:依赖解析、预打包优化与 CJS 互操作 【免费下载链接】vitest Next generation testing framework powered by Vite. 项目地址: https://gitcode.com/GitHub_Trending/vi/vitest Vitest 的 test.deps 配置是控制测试运行器如何处理外…

作者头像 李华
网站建设 2026/9/14 17:53:56

如何为自研库用 stubtest 验证 .pyi 存根与实现的一致性?

如何为自研库用 stubtest 验证 .pyi 存根与实现的一致性? 【免费下载链接】mypy Optional static typing for Python 项目地址: https://gitcode.com/GitHub_Trending/my/mypy 如果你给自研 Python 库维护了一份 .pyi 存根文件,最常见的风险是&am…

作者头像 李华