2. Pod网络与Service网络基础

在OpenShift中,容器网络并不是“一张网”,而是由多个相互叠加的地址平面共同构成:Pod网络负责容器之间的直接可达性,Service网络负责为动态变化的Pod集合提供稳定入口,而kube-proxy与OVN则负责把“稳定入口”翻译成“当前真实的后端地址”。理解这三层关系,是排查网络问题的起点。

Pod IP:每个Pod一个可路由地址

OpenShift默认使用OVN-Kubernetes作为网络插件,为每个Pod分配一个集群内唯一的IP地址。这个地址来自集群配置的clusterNetwork网段,例如10.128.0.0/14,并按节点进一步细分为hostSubnet。每个节点从自己的子网中为Pod分配IP,因此Pod IP天然带有“节点归属”信息。

Pod IP的特点是:生命周期与Pod绑定,Pod重建即更换。这意味着任何直接依赖Pod IP的访问方式都不稳定。例如一个Deployment扩容到3个副本,三个Pod分别获得10.128.2.5、10.128.2.6、10.128.3.7,一旦某个Pod被驱逐重建,新Pod会拿到新IP。因此,业务之间不应直接写Pod IP,而应通过Service访问。

在节点上可以用以下命令观察Pod IP与节点子网的对应关系:

oc get pod -o wide
oc get hostsubnet
oc get pod my-app-7d9f8c6b5-abcde -o jsonpath='{.status.podIP}'

Service:三种对外暴露方式

Service为一组Pod提供固定虚拟IP(VIP),即ClusterIP。它通过标签选择器(selector)动态关联后端Pod,无论Pod如何变化,Service的ClusterIP保持不变。OpenShift支持三种主要类型:

  • ClusterIP:仅集群内部可达,分配一个serviceNetwork网段内的虚拟IP,例如172.30.0.0/16中的172.30.45.10。适合内部微服务调用。
  • NodePort:在每个节点上开放一个固定端口(默认范围30000-32767),外部可通过节点IP:NodePort访问。适合测试或没有负载均衡器的环境。
  • LoadBalancer:在NodePort基础上,向底层基础设施(如云平台或MetalLB)申请一个外部IP,形成外部IP:端口 → Service → Pod的完整链路。适合生产环境对外暴露。

三种类型并非互斥,而是层层叠加:LoadBalancer包含NodePort,NodePort包含ClusterIP。创建示例如下:

apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080

其中port是Service端口,targetPort是Pod容器实际监听端口,nodePort是节点暴露端口。三者关系是排查Service不通时的第一检查点。

Endpoint与EndpointSlice:Service背后的真实地址

Service本身只是规则,真正承载流量的是Endpoint。当Service创建后,Endpoint控制器会根据selector筛选出匹配的Pod,把它们的IP:targetPort写入Endpoint对象。若selector匹配不到任何Pod,Endpoint为空,Service访问就会失败。

在大规模集群中,单个Endpoint对象可能包含数千个地址,更新时全量推送会造成性能瓶颈。因此Kubernetes引入EndpointSlice,把Endpoint按100个地址一组切片,并附加endpointslice.kubernetes.io/managed-by等标签。EndpointSlice支持按需更新,显著降低控制面压力。OpenShift中默认启用EndpointSlice,Endpoint对象仍保留以兼容旧组件。

观察命令:

oc get endpoints web-svc
oc get endpointslice -l kubernetes.io/service-name=web-svc
oc describe endpointslice web-svc-abcde

如果EndpointSlice中endpoints列表为空,或conditions.ready为false,说明Pod未通过就绪探针,流量不会转发到该Pod。

kube-proxy与OVN流表:把VIP翻译成Pod IP

Service的ClusterIP是一个虚拟地址,并不配置在任何网卡上。真正完成“VIP → Pod IP”转换的,是数据面组件。传统Kubernetes使用kube-proxy,通过iptables或IPVS规则实现;OpenShift默认使用OVN-Kubernetes,由ovn-controller在每个节点上把Service和Endpoint信息编译成OpenFlow流表,写入OVS网桥。

当Pod访问ClusterIP时,流量进入OVS网桥,命中流表后执行DNAT,把目的地址改为某个Endpoint的Pod IP,并可能伴随SNAT以保证回程对称。整个过程对应用透明。可以用以下命令查看流表:

oc get svc web-svc -o jsonpath='{.spec.clusterIP}'
oc rsh -n openshift-ovn-kubernetes ovnkube-node-xxxxx
ovs-ofctl dump-flows br-int | grep 172.30.45.10

流表中通常能看到ct(commit)、nat(dst=10.128.2.5:8080)等动作,说明该ClusterIP已被正确解析到后端Pod。若流表缺失,常见原因是Endpoint未就绪、OVN控制器未同步或节点间隧道异常。

Service 流量解析链路:ClusterIP → EndpointSlice → Pod IP 客户端 Pod 10.128.2.10 Service web-svc ClusterIP 172.30.45.10 port 80 → targetPort 8080 EndpointSlice 10.128.2.5:8080 ready 10.128.2.6:8080 ready 10.128.3.7:8080 ready Pod A 10.128.2.5 Pod B 10.128.2.6 Pod C 10.128.3.7 数据面:OVN-Kubernetes 流表处理 OVS br-int 入口流量 流表匹配 dst=172.30.45.10 DNAT → 10.128.2.5:8080 Pod A 接收请求 若 EndpointSlice 为空或 ready=false,流表不会生成 DNAT 规则,Service 访问失败

三层地址的排障顺序

当Service访问异常时,建议按“Service → EndpointSlice → 流表 → Pod”的顺序逐层确认:

检查层命令正常表现异常含义
Serviceoc get svc web-svcClusterIP已分配,端口正确无ClusterIP说明未分配或类型错误
EndpointSliceoc get endpointslice -l kubernetes.io/service-name=web-svcendpoints列表非空且ready为空说明selector不匹配或Pod未就绪
流表ovs-ofctl dump-flows br-int存在对应ClusterIP的DNAT规则缺失说明OVN未同步或节点异常
Podoc get pod -o widePod Running且IP在EndpointSlice中IP不一致说明Endpoint未更新
ClusterIP、NodePort、LoadBalancer三者是叠加关系:LoadBalancer自动创建NodePort,NodePort自动创建ClusterIP。因此排查LoadBalancer不通时,可以先确认ClusterIP是否可达,再逐层向外检查。
不要用Pod IP做服务发现。Pod IP会随重建、扩缩容、调度而变化,直接写死Pod IP会导致间歇性连接失败。始终通过Service名称或ClusterIP访问。

掌握Pod IP、Service、EndpointSlice与OVN流表之间的映射关系后,后续排查DNS解析、网络策略、Ingress路由等问题时,就能快速定位故障发生在哪一层,而不是在“网络不通”这个模糊结论上反复试探。