Cilium Node IPAM LB:用节点 IP 直接充当 LoadBalancer 入口的免云厂商外部负载均衡方案
Cilium Node IPAM LB用节点 IP 直接充当 LoadBalancer 入口的免云厂商外部负载均衡方案【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/ciliumNode IPAM LBNode-IPAM LoadBalancer是 Cilium 实现的一种 KubernetesLoadBalancerService IPAM 方案它由 cilium-operator 内的控制器监听 Service、EndpointSlice 与 Node 三类资源将满足条件的节点 IP 直接写入Service.status.loadBalancer.ingress从而把节点 IP 广播为负载均衡入口。该特性适用于无法控制底层网络、既不能用 Cilium L2 通告也不能用 BGP 通告 VIP 的托管环境。读完本文你将理解其节点候选与 IP 选择规则、掌握通过 Helm 启用nodeIPAM.enabled与defaultLBServiceIPAM的完整配置方法并能看懂externalTrafficPolicy为Cluster与Local时的行为差异及其边界限制。什么是 Node IPAM LBNode IPAM LoadBalancer 的设计灵感来自 k3s 的 ServiceLB 特性。它的核心思想非常直接不申请任何云上负载均衡资源不创建 VIP而是直接把集群节点的 IP 地址作为 LoadBalancer Service 的 ingress 地址对外暴露。工作方式是获取被选中节点的地址status.addresses然后将其广播advertise到 Service 的status.loadBalancer.ingress字段中。这一设计在以下场景中尤其有用运行环境是托管 Kubernetes 服务你不控制底层网络无法在外部交换机上配置 L2 MAC 通告环境没有可用的 BGP 对等关系Cilium 的 BGP 控制器无从通告 VIP你希望避免依赖云厂商的 LB APICilium 的lbipam方案需要调用云 API 创建 NLB/ELB转而仅用集群内已有信息完成外部入口的语义。从源码结构看Node IPAM 的全部运行时逻辑位于 cilium-operator 中核心实现集中在 operator/pkg/nodeipam 目录nodesvclb.gonodeSvcLBReconciler控制器负责 Service 的调和reconcile与 IP 广播node_predicates.go节点是否纳入负载均衡候选的判定谓词cell.goCilium hive 框架的模块注册与启用开关pkg/nodeipamconfig/cell.go配置项--enable-node-ipam与loadBalancerClass常量定义。控制器通过 operator/cmd/root.go 中的nodeipam.Cell注册进 operator 主流程因此Node IPAM 是 operator 侧能力不是 agent 侧能力——启用开关下发给 operator 进程。工作机制从 Service 到节点 IP 的完整链路控制器监听哪些对象nodeSvcLBReconciler.SetupWithManager注册了三类 watch见 nodesvclb.go#L48-L63Service只处理受支持的 Service见下文判定逻辑作为 reconcile 的主对象EndpointSlice当某个 EndpointSlice 更新时通过其kubernetes.io/service-name标签反查所属 Service 并触发该 Service 重新调和enqueueRequestForEndpointSlicenodesvclb.go#L65-L86。这是为了在externalTrafficPolicy: Local模式下及时反映 Pod 分布变化Node当一个通过基础谓词检查的节点发生变化时列出全部受支持的 Service 并逐一入队enqueueRequestForNodenodesvclb.go#L88-L122保证节点增删改时 ingress 列表随之刷新。哪些 Service 会被处理isServiceSupportednodesvclb.go#L151-L163的判定条件是Service 未被标记删除spec.type必须为LoadBalancerspec.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。候选节点的筛选规则节点筛选分三层全部实现于getRelevantNodes与shouldIncludeNode第一层基础排除谓词KEP-3458 对齐。node_predicates.go 中的shouldIncludeNode显式声明其语义与 kubernetes/cloud-provider 项目中的stableNodeSetPredicates保持一致包含三条节点无deletionTimestamp正在删除的节点排除节点没有标签node.kubernetes.io/exclude-from-external-load-balancersv1.LabelNodeExcludeBalancers节点没有污点ToBeDeletedByClusterAutoscalerCluster Autoscaler 在删除节点前会打上该污点。被排除的节点会被记录到日志Skipping Nodes that are being deleted or are excluded from load balancers便于排查为什么某个节点 IP 没有出现在 ingress 中。第二层externalTrafficPolicy语义。对应文档中最重要的行为分支nodesvclb.go#L165-L198externalTrafficPolicy: 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常量NodeSvcLBMatchLabelsAnnotationpkg/nodeipamconfig/cell.go#L39其值按 KubernetesLabel Selector语法解析后作为 List Nodes 的 label selector把候选范围进一步收窄——适合只有部分节点能接收外部入站流量的拓扑例如仅特定 zone/az 的节点挂了公网 EIP。解析失败会报错并终止本次调和空结果会输出告警日志No Nodes found with configured label selector。IP 选择与广播最终 IP 集合由getNodeLoadBalancerIngressesnodesvclb.go#L265-L308计算规则如下只读取节点status.addresses中类型为ExternalIP与InternalIP的地址其他类型忽略按 Service 的spec.ipFamilies过滤地址族——只有当 ipFamilies 包含IPv4时 IPv4 地址才被计入IPv6同理因此单栈集群只广播单栈地址双栈集群两种都广播优先级只要存在任何节点 ExternalIP就全部使用 ExternalIP否则回退到 InternalIP注意是整体二选一不是逐节点回退去重后按字典序排序保证输出稳定、diff 友好每个地址生成一个LoadBalancerIngress{IP: ...}整体写回svc.Status.LoadBalancer.Ingressnodesvclb.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-L2405nodeIPAM: # -- 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.enabledtrue在已有部署上启用helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set nodeIPAM.enabledtrue 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: LoadBalancer且loadBalancerClass: io.cilium/nodeapiVersion: 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设为nodeipamHelm 值defaultLBServiceIPAM取值范围lbipam/nodeipam/none默认lbipam。用注解限定承载节点要在多个节点中只让部分节点接收外部流量给 Service 加注解io.cilium.nodeipam/match-node-labels值为标准 Label SelectorapiVersion: v1 kind: Service metadata: name: node-ipam-svc annotations: # 只选择带 roleingress 标签的节点广播 IP io.cilium.nodeipam/match-node-labels: roleingress spec: type: LoadBalancer loadBalancerClass: io.cilium/node ...节点排除机制速查节点状态是否参与广播依据带标签node.kubernetes.io/exclude-from-external-load-balancers否node_predicates.gonodeIncludedPredicate带污点ToBeDeletedByClusterAutoscaler否node_predicates.gonodeUnTaintedPredicatedeletionTimestamp非零否nodeNotDeletedPredicate被match-node-labelsselector 排除否nodesvclb.go#L210-L221eTPLocal 且无 ready 后端 Pod 落在此节点否nodesvclb.go#L184-L195已知限制与排查eTPLocal 依赖真实 EndpointSlice。文档明确警告externalTrafficPolicy: Local但没有对应或只有 dummyEndpointSlice 的 Service 无法工作。因此 Cilium 自研的 GatewayAPI/Ingress 实现其 LoadBalancer Service 使用 dummy Endpoint见 install/kubernetes/cilium/templates/cilium-ingress-service.yaml不能设Local只能使用Cluster。ExternalIP 是全有或全无。IP 选择逻辑是集合级回退只要任一节点有 ExternalIPingress 就只包含 ExternalIP 集合InternalIP 不会被混合进来。在异构网络部分节点有公网 IP、部分只有内网 IP中这一点需要特别注意。广播的是节点地址不是 VIP。客户端会看到多个入口 IP端口转发依赖节点上内核/endpoint 对 Service 的处理路径若外部防火墙只放行单一 VIP该方案不适用。排查入口operator 日志控制器名为service-nodeipamnodesvclb.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: LoadBalancerloadBalancerClass: io.cilium/node或通过defaultLBServiceIPAM: nodeipam使其成为默认方案启用Helm 值nodeIPAM.enabledtrue映射为 operator 的enable-node-ipam开关改动后需重启cilium-operator行为eTPCluster 选全部合格节点eTPLocal 选运行后端 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),仅供参考