MicroK8s+Cilium+MetalLB环境下节点本地无法通过Service外部IP访问Docker Registry Pod
看起来你遇到的是Cilium服务代理逻辑和主机多路由表策略冲突导致的典型问题,我来帮你拆解分析并给出具体的排查解决步骤:
问题根因分析
你的节点是多宿主(跨多个VLAN)配置,同时Cilium默认会替换kube-proxy的功能——当节点本地发起对Service IP的请求时,Cilium会直接做服务端点的DNAT,把请求转发到Pod的ClusterIP(也就是你看到的10.1.3.82),而不是走正常的网络栈去触发MetalLB的L2 ARP解析。这种情况下,请求的目的IP变成了Cilium集群内部的Pod IP,而路由器没有这个网段的路由规则,自然无法处理。
而当你指定默认VLAN的源IP发起请求时,主机的路由策略让请求走了默认路由表,绕过了Cilium的本地代理逻辑,所以能正常访问。
具体解决步骤
1. 调整Cilium的服务代理行为(最直接的解决方案)
给你的Docker Registry Service添加Cilium专属注解,让它跳过节点本地的IP代理,强制请求走主机网络栈触发MetalLB的ARP响应:
apiVersion: v1 kind: Service metadata: annotations: cilium.io/skip-ipproxy: "true" # 关键注解:跳过Cilium本地代理 name: docker-registry-svc namespace: your-namespace spec: type: LoadBalancer externalIPs: - 10.9.40.146 # MetalLB分配的外部IP ports: - port: 5000 targetPort: 5000 selector: app: docker-registry
更新Service后,再从节点本地执行curl -k -X GET https://10.9.40.146:5000/v2/_catalog测试,应该就能正常访问了。
2. 检查并调整主机的反向路径过滤(RPFilter)
多宿主节点的反向路径过滤规则可能会干扰请求的路由,你可以临时关闭测试:
# 临时关闭所有接口的RPFilter sysctl -w net.ipv4.conf.all.rp_filter=0 # 单独关闭VLAN 12接口的RPFilter sysctl -w net.ipv4.conf.ens2.12.rp_filter=0
如果测试有效,需要将配置永久化:在/etc/sysctl.conf中添加以下内容,然后执行sysctl -p生效:
net.ipv4.conf.all.rp_filter=0 net.ipv4.conf.ens2.12.rp_filter=0
3. 优化MetalLB的L2模式配置
确保MetalLB只在指定的VLAN接口上发送ARP通告,避免路由混淆:
apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: vlan12-pool protocol: layer2 addresses: - 10.9.40.140-10.9.40.150 interfaces: - ens2.12 # 指定监听的VLAN接口
4. 验证节点路由路径
你可以用以下命令检查节点对Service IP的路由走向,确认是否被Cilium劫持:
ip route get 10.9.40.146 from 10.9.40.148
如果返回结果指向cilium_host接口,说明Cilium在劫持路由,这时候步骤1的注解就能解决问题;如果返回的是走ens2.12到路由器的路由,那问题可能出在RPFilter或者路由表配置上。
备注:内容来源于stack exchange,提问作者Tomas

