AI时代的Kubernetes教程36招-第14招-CNI、Pod网络、NetworkPolicy和跨节点通信到底怎么工作?

系统学习Kubernetes网络模型:从Pod CIDR到默认拒绝的网络策略落地

跨Node偶发超时,先怀疑网络,还是先怀疑策略?

[读者] 负责数字人平台 Kubernetes 落地与网络排障的工程师
[痛点] Pod 能起、Service 能通,但跨 Node 偶发超时、策略放了却没生效
[现在读] frontend/gateway/ai/data 要分命名空间隔离,必须先搞清 IP 从哪来、包怎么走
[读完] 能按 CIDR、路由、MTU、DNS、NetworkPolicy 五要素定位问题并落地默认拒绝
[旧方案] 所有命名空间共用一个 default,Service 互访全放开
    |
    v
[新需求] 数字人四条链路要按命名空间隔离,只放必须的通路
    |
    v
[冲突] Pod IP 从哪来、跨 Node 怎么转发、策略在哪一层拦,认知不清就只会盲改
    |
    v
[后果] 要么放太宽等于没隔离,要么收紧后 DNS、健康检查、模型拉取全线超时

我是老李,在做数字人平台的 Kubernetes 落地。平台上有四个命名空间:frontend 放 Web 与推流页面,gateway 放接入网关与鉴权,ai 放推理与模型服务,data 放特征、向量库与离线任务。上线初期它们都在 default 里,Service 之间随便通,运维靠“能通就行”活着。

变化来得很自然:合规要求数据域不能被动访问,ai 域不允许被 frontend 直连,gateway 只能暴露有限入口。这时候才意识到,我们其实从没真正理解过这套网络模型——只知道“Pod 有个 IP”,不知道这个 IP 是谁发的、跨 Node 谁转的、策略是在哪一层拦的。

是先照着别人的 YAML 抄一份 NetworkPolicy,还是先把 Pod CIDR、Service CIDR、跨 Node 路由、MTU、DNS 这五件事讲清楚?

如果选错,会发生什么?大概率是策略一上,DNS 挂了、探针挂了、模型拉取超时,最后一把回滚,从此再也不敢动网络配置。

01、故事

数字人平台的第一版跑得很顺:前端页面、网关、推理服务、数据服务在同一集群同一命名空间,Service 名字互调,谁也没想过“这条调用该不该存在”。任务是让平台在合规审计下继续上线,而不是把网络当成黑盒。

审计提了三条:数据域的存储与特征服务不得被用户侧直接访问;ai 推理服务只能被 gateway 调用;frontend 不得直连 ai。对照现状,这三条几乎全违反。

于是第一次尝试用 NetworkPolicy 收口,结果当天晚上就出事故:ai 命名空间的 Pod 全部报 DNS 解析失败,data 里的任务拉不到镜像仓库的元数据。回滚之后大家得出一个错误的结论——“NetworkPolicy 太危险”。

真正的问题不是策略危险,而是我们不知道一个 Pod 发包到另一个 Pod,中间会经过哪些判断点。把判断点搞清楚,策略就只是把默认放行改成默认拒绝,再一条条开洞而已。

不辨包行路
只知通与堵
一收全局断
回滚成定局

02、问题

旧方案失效的表现不在“完全不通”,而在“大部分通、少数不通”:跨 Node 的 ai 到 data 偶发超时,同 Node 的调用正常;大响应体的推理结果偶尔卡住,小请求没事。这类现象最容易被误判成应用慢。

业务影响很直接:数字人合成任务偶发失败重试,用户看到的是“偶尔卡一下”,排障成本却极高;合规侧则更严重——默认放行意味着审计要求的隔离事实上不存在,随时可能被判定不通过。

技术表现可以归成四类可观测现象:Pod 间跨 Node 不通或高延迟;Service 访问失败但 Pod IP 直连正常;某个命名空间出现 DNS 解析超时;策略对象已创建但流量未被拦截。

可验证的完成标准需要写死四条:任意两个允许互通的 Pod 跨 Node 稳定通信;所有未显式放行的 Namespace 间流量被拒绝且可观测;frontend/gateway/ai/data 各自有清晰的入站与出站白名单;出现故障时能用有限的命令区分是 IP 分配、路由、MTU、DNS 还是策略问题。

通而不稳时
先分四类症状
标准先写死
再谈怎么改

03、原理

Kubernetes 网络模型对 Pod 网络有明确要求:每个 Pod 拥有集群内可路由的 IP,同一 Pod 内多个容器共享网络命名空间,Pod 之间可以直接通信而不需要 NAT。这条模型只规定“应该怎样”,真正的实现交给 CNI 插件,官方在服务网络与网络策略文档里都强调实现层面的差异需要按插件核验。

反直觉判断:Pod 之间“不需要 NAT 就能通”并不等于“随便就能通”,也不等于“同 Node 通就代表跨 Node 一定通”。模型是声明式的,转发路径是分布式的——每个 Node 上都有路由与转发规则,任何一个 Node 的配置不一致,都会表现为“偶发不通”。

第二个反直觉判断:NetworkPolicy 不是防火墙规则书,它是插件对一组标签选择器的解释。策略在哪个组件、哪个 hook 上被翻译成具体规则,完全取决于 CNI 实现,这也是“策略建了却没拦”最常见的根因。

Service CIDR 是另一条线。Service 是一个虚拟 IP,靠每个 Node 上的转发组件把访问虚拟 IP 的包改写成某个 Pod IP。所以“Service 不通但 Pod IP 直连通”几乎总是指向转发层或 Endpoint 问题,而不是 CNI 的 Pod 连通性。

MTU 是第三个容易被忽略的点。Pod 网络通常存在一层封装或隧道,可用 MTU 小于节点网卡 MTU;如果应用发大包而路径上不允许分片,就会出现“小请求通、大响应卡”的典型症状。这个值具体该设多少,需按当前官方文档与所用 CNI 文档核验。

模型只定义
实现各有别
同点非全程
大包见真章

04、架构

[输入] frontend/gateway/ai/data 四个命名空间发起的请求
    |
    v
[模块] CNI 插件 + kube-proxy/eBPF 转发 + kube-dns
    |
    v
[数据/状态] Pod CIDR 分片、Service CIDR、Endpoint、NetworkPolicy 对象
    |
    v
[处理] IP 分配 -> 跨 Node 路由 -> 虚拟 IP 改写 -> DNS 解析 -> 策略匹配
    |
    v
[输出] 允许的调用到达目标 Pod,其余被拒绝并记录

边界在这里:CNI 负责 Pod 起停时的 IP 分配与连通性,转发组件负责 Service 语义,DNS 组件负责名字解析,NetworkPolicy 只负责“谁可以和谁说话”。四者任一层出问题,表象都可能是“网络不通”。

收益是职责清晰:排障时先定位是哪一层,而不是笼统地“重启网络插件”。代价是配置面变多,尤其是 MTU、CIDR 规划与策略选择器需要统一约定。适用条件是集群网络插件支持 NetworkPolicy;Calico 与 Cilium 都提供策略能力,但数据面实现不同,选择与配置需按各自官方文档核验。

至于 cluster/cni/ 与 deploy/network/ 这两个目录,本篇的用法是:前者承载 CNI 相关配置与清单,后者承载命名空间与策略清单,二者分离才能让“网络实现”和“网络规则”各自演进。

四层各司职
先分再动手
目录分两类
规则才可管

05、实战一次

环境与依赖:一个可用的 Kubernetes 集群,已安装支持 NetworkPolicy 的 CNI 插件;本地具备 kubectl 与集群访问权限。以下清单放入 deploy/network/,CNI 相关配置放入 cluster/cni/。

第一步,建立四个命名空间并加上统一标签,便于后续用选择器批量引用。

apiVersion: v1
kind: Namespace
metadata:
  name: frontend
  labels:
    zone: web
---
apiVersion: v1
kind: Namespace
metadata:
  name: gateway
  labels:
    zone: edge
---
apiVersion: v1
kind: Namespace
metadata:
  name: ai
  labels:
    zone: compute
---
apiVersion: v1
kind: Namespace
metadata:
  name: data
  labels:
    zone: store

第二步,给每个命名空间打上默认拒绝的入站策略。默认拒绝只写 podSelector 为空,表示选中本命名空间全部 Pod,且不写 ingress 规则,即拒绝一切入站。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: ai
spec:
  podSelector: {}
  policyTypes:
    - Ingress

第三步,按需放行。ai 只接受 gateway 命名空间的调用,同时允许 kube-system 的 DNS 出站;data 只接受 ai 的调用;frontend 只接受来自外部入口落到 gateway 的调用链路中的必要路径。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-gateway-to-ai
  namespace: ai
spec:
  podSelector:
    matchLabels:
      app: inference
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              zone: edge
      ports:
        - protocol: TCP
          port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: ai
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

第四步,应用并验证。先确认策略对象已创建,再用一个临时 Pod 在 ai 命名空间内尝试访问 gateway 与 frontend。

kubectl apply -f deploy/network/
kubectl get networkpolicy -A
kubectl run probe --rm -it --image=busybox:1.36 --restart=Never \
  -n ai -- sh -c 'nslookup kubernetes.default.svc.cluster.local; wget -qO- --timeout=3 http://gateway:8080/healthz'

示例输出(未在当前环境实测,以下为预期结果):策略列表中出现 default-deny-ingress 与 allow-gateway-to-ai;DNS 解析成功;对 gateway 的访问取决于是否已放行,未放行时应表现为超时或拒绝。

V1 到这里跑通,但它留下三个明确问题:默认拒绝是否覆盖了出站;DNS 依赖 kube-system 命名空间标签,标签命名方式是否稳定;策略生效后如何快速判断是“被拒绝”还是“没路由”。

先建四空间
再立默认拒
逐条开小洞
验证再扩展

06、排查

现象一:策略上线后 ai 命名空间内所有 Pod 报 DNS 解析失败,但直接访问 Pod IP 仍可通。

怀疑方向是策略口径:default-deny 若同时作用于出站,而未放行到 kube-system 的 53 端口,DNS 必然失败。

检查方式是查看该命名空间中所有 NetworkPolicy 的 policyTypes,确认是否存在 Egress 类型但没有对应 egress 规则。

证据是策略对象里出现 Egress 而 egress 段落为空或缺失。根因是默认拒绝只写了收口意图,没有配套放行 DNS 与必要的出站。

修复是补一条出站策略,仅允许到 kube-system 的 UDP/TCP 53,其余出站继续拒绝。

错误尝试: 第一反应是把 default-deny 整条删掉恢复通信。这样做确实能恢复,但把审计要求的隔离一起删了,属于用回滚掩盖认知缺口,下次上线还会复现同样问题。

现象二:同 Node 的 ai 到 data 正常,跨 Node 偶发超时,大响应体尤其明显。

怀疑方向是先排除策略,再排查跨 Node 路由与 MTU。因为同 Node 通信不过物理链路,跨 Node 才走隧道或路由。

检查方式是分别从两个 Node 上的 Pod 发起同样请求,并对比小包与大包的表现。

证据是相同策略、相同服务,仅跨 Node 与大包场景失败,指向封装后的可用 MTU 与路径分片行为。

根因是 Pod 路径 MTU 与节点网卡 MTU 不匹配,或应用未做路径 MTU 发现。修复需要在 CNI 层统一 MTU 配置,具体取值按所用 CNI 的官方文档核验,并让应用对大响应具备重试与分片容忍。

先分清层次
再对照证据
删策略最易
却最不治本

07、优化

基于第 06 章的两条证据,V2 做三件事。

第一件,把默认拒绝拆成入站与出站两条明确清单,并在出站中只放行 DNS 与必要的上游依赖,避免“默认拒绝”被误解成“全向拒绝”。原因是 V1 的策略意图没有区分方向,导致 DNS 被误伤。新行为是任何命名空间出站默认全部拒绝,只有显式列出的目标可通;验证方式是再次用临时 Pod 解析集群内域名并访问一个未放行服务,前者成功、后者失败。

第二件,把策略选择器统一到命名空间标签上,而不是依赖命名空间名字。原因是 kube-system 这类系统命名空间在不同集群中的标签命名方式需要按当前官方文档核验,用名字硬编码容易在升级或迁移后失效。新行为是策略只引用 zone 与 kubernetes.io/metadata.name 这类稳定标签;验证方式是给测试命名空间改标签后观察策略是否随之收放。

第三件,把 MTU 与 CIDR 规划落到 cluster/cni/ 里做统一约定。原因是大包失败已被证据指向封装路径,而 Pod CIDR 与 Service CIDR 一旦重叠,会引发更隐蔽的地址冲突。新行为是两份 CIDR 明确分离并记录,MTU 取值按 CNI 官方文档确定;验证方式是跨 Node 发起大响应请求并观察是否仍出现超时。

需要说明的是,以上均为方法层面的改动方向,未在当前环境实测,不给出任何性能或成功率数字。

出站须单列
选择器宜稳
CIDR 不重叠
MTU 有出处

08、演进

[同一输入]
    |
    +--[V1] {默认放行 + 少量拒绝 / 代价:隔离形同虚设}
    |
    +--[V2] {默认拒绝 + 按需放行 / 代价:配置量与排障成本上升}
    |
[Trade-off]
{得到可审计的隔离边界,失去配置自由度;适用边界是命名空间与标签治理已稳定的集群}

正确性上,V2 让“允许的必须显式声明”成为默认,未声明的调用被拒绝,比 V1 的默认放行更贴近合规要求。

稳定性上,V2 引入了新的失败面:策略与 DNS、探针、镜像拉取、上游依赖都需要逐条放行,任何遗漏都会表现为超时。V1 相对“不会因为策略挂掉”,但会因为没隔离而在审计上挂掉。

复杂度上,V2 从“无策略”变成“每个命名空间至少两条策略”,需要纳入 Git 管理并由 deploy/network/ 统一发布,否则会出现人工改动与仓库不一致。

成本上,V2 的主要成本在排障时的判断路径变长:必须先确认是策略拒绝还是底层不通,再决定改动方向。

适用范围上,V2 适合多命名空间、有合规或数据隔离诉求的平台;单一命名空间的小集群收益有限。遗留问题是策略生效的观测性仍不足,被拒绝的流量需要依赖 CNI 侧日志或指标才能看到。

宽则无所守
严则处处洞
取舍在治理
不在写 YAML

09、洞见

9.1 先把模型与实现分开,再谈选型

Kubernetes 网络模型只规定 Pod 有集群可达 IP、Pod 间无需 NAT 通信,具体怎么做成,由 CNI 插件实现。反直觉判断:Calico 与 Cilium 的差异,很多时候不是“能不能通”的差异,而是“策略在哪里被判、被拒绝的流量能不能被看见”的差异。选型前先明确自己要的是连通、策略还是可观测性,否则对比表格再长也落不了地。

模型定契约
实现定差异
先问要什么
再问选哪个

9.2 默认拒绝不是一条策略,而是一套方向约定

实践中最容易踩的坑,是把默认拒绝理解成“一条空 ingress 就完事”。反直觉判断:默认拒绝真正致命的部分往往不是入站,而是出站——DNS、探针回调、镜像与依赖拉取都在出站方向。方向不分开写,收口就等于把集群内部的自愈能力一起关掉。

入站看得见
出站常被忘
方向分两条
收口才完整

9.3 CIDR 是集群的地址宪法

Pod CIDR 与 Service CIDR 一旦规划重叠,问题和策略无关,却会以“策略没生效”“服务偶发不可达”的形式出现。把两份网段写进 cluster/cni/ 的约定里,并让所有人都知道谁管哪一段,比事后排障便宜得多。

网段先划清
谁管谁写明
冲突不常见
一现即难查

9.4 排障必须按层走,而不是按直觉走

从 IP 分配、跨 Node 路由、Service 转发、DNS 解析到策略匹配,是一条有序的判断链。反直觉判断:重启网络插件看起来最像“解决问题”,实际上会把现场证据一起清掉;保留现场、逐层排除,通常比暴力重启更快定位根因。

现场即证据
重启即失忆
逐层往下走
根因自浮现

10、系统落地

原来有的,是一个 default 命名空间、默认放行、Service 名字互调大家都在用的集群;没有一个地方写清哪些调用是被允许的。

本篇新增的,是四个带标签的命名空间、每个命名空间的默认拒绝入站与出站策略、按需放行的最小通路清单,以及 cluster/cni/ 与 deploy/network/ 的目录约定。

现在能做的,是把 frontend 只暴露必要入口、gateway 只调用 ai 与必要的上游、ai 只接受 gateway 调用并仅出站访问 data 与 DNS、data 只接受 ai 与离线任务访问。审计时可以直接用策略清单回答“谁能访问谁”。

还缺的,是策略拒绝流量的可观测性:目前只能靠应用侧超时感知,缺少集中的拒绝日志或指标视图。

下一步演进方向,是把策略清单纳入发布流水线,任何新增调用都必须以策略变更的形式提交;同时补齐跨 Node 大包场景的回归用例,把 MTU 与 CIDR 约定变成可验证的检查项,而不是文档里的一句话。

有边界可审
有目录可管
缺观测待补
缺回归待加

11、小结

Q1 → 抄 YAML 还是先搞清模型?先搞清 Pod CIDR、Service CIDR、跨 Node 路由、MTU、DNS 与策略匹配这五层,再动配置
Q2 → 策略一上就挂怎么办?把默认拒绝按入站与出站分开写,逐条放行 DNS 与必要依赖,而不是整条删除
Q3 → 怎么判断是策略还是底层?按层排除:先看策略对象与方向,再看跨 Node 与大包表现,最后回到路由与 MTU
状态 → frontend/gateway/ai/data 四命名空间可交付默认拒绝 + 按需放行的策略清单与验证方法

层清则路明
向分则网稳
约定进仓库
隔离才可持续

12、作业

12.1 理解题:为什么“Pod 之间不需要 NAT 就能通信”不能推出“默认就应该放行所有流量”?

参考答案:前者描述的是网络模型对连通性的要求,属于“能力”;后者是安全与合规层面的“授权”。模型保证能通,不代表业务上应该通。默认拒绝是把授权从默认放行改为默认不授权,二者不在同一层,不构成矛盾。

能通非应通
模型非授权
分清这两层
策略才有据

12.2 实战题:给 ai 命名空间同时加默认拒绝与 DNS 出站放行,写清关键字段。

参考答案:一条策略用 podSelector 为空选中全部 Pod,policyTypes 只写 Ingress,不放行任何入站;另一条策略 policyTypes 写 Egress,egress 中 to 指向 kube-system 命名空间选择器,ports 写 UDP 与 TCP 的 53。两条分开写,方向清晰,避免误伤。

入站一条拒
出站一条放
方向分开写
误伤自然少

12.3 排障题:某服务在同 Node 访问正常、跨 Node 偶发超时且大响应更明显,如何定位?

参考答案:先确认策略两侧一致以排除策略因素,再对比同 Node 与跨 Node 的差异,最后用大包与小包对照验证。若仅跨 Node 与大包失败,优先怀疑封装路径的可用 MTU 与分片行为,按所用 CNI 官方文档核验 MTU 取值。

同点先排除
跨点看差异
大小包对照
指向 MTU

12.4 架构判断题:是否应该让四个命名空间共用一套策略清单以减少维护量?

参考答案:不建议。四个命名空间的职责与信任级别不同,共用清单会导致放行范围取并集,隔离强度被稀释。合理做法是共用命名与标签约定、共用目录与发布流程,但策略按命名空间分别声明,必要时用统一模板生成。

约定可共享
权限须分治
并集即放水
模板解重复

13、思考

回到最初那个问题:跨 Node 偶发超时,先怀疑网络还是先怀疑策略?真正的答案是不先怀疑任何一层,而是先承认自己对包在网络里的行走路径不清楚。模型规定了契约,插件决定了实现,策略决定了授权,三者不能混为一谈。

数字人平台的这次收口,最终收获的不是一份 YAML,而是一条判断链:地址从哪来、包往哪走、名字怎么解析、谁能和谁说话。这条链一旦建立,Calico 还是 Cilium 就不再是信仰问题,而是需求问题;策略该放多宽也不再是拍脑袋,而是证据问题。

网络的复杂性不会因为少写几条策略而消失,只会以偶发故障的形式回来找你。

路径先看清
授权再收口
证据定边界
认知即基建

posted @ 2026-10-12 10:19  李福春  阅读(2)  评论(0)    收藏  举报