4. 标签选择器与命名空间选择器
在 NetworkPolicy 中,真正决定“谁能访问谁”的并不是 IP 地址,而是选择器。NetworkPolicy 通过 podSelector、namespaceSelector 以及它们内部的 matchLabels 与 matchExpressions 来圈定一组 Pod,再配合 ingress/egress 规则决定放行还是拒绝。理解选择器的匹配语义,是写出正确策略的前提;一旦语义理解偏差,策略可能过宽(形同虚设)或过窄(误伤正常流量)。
一、matchLabels 与 matchExpressions 的匹配语义
选择器由两部分组成:matchLabels 是键值相等的精确匹配,多个键之间是“与”关系;matchExpressions 支持 In、NotIn、Exists、DoesNotExist 四种运算符,用于表达集合与存在性判断。两者同时出现时,同样按“与”合并,即所有条件都必须满足。
需要特别注意两个边界:其一,matchLabels 与 matchExpressions 之间是逻辑与,不是或;其二,NotIn 与 DoesNotExist 对“标签不存在”的处理不同——NotIn 要求键存在且值不在集合内,而 DoesNotExist 只要求键不存在。下面用一段策略片段说明组合方式:
podSelector:
matchLabels:
app: web
matchExpressions:
- key: tier
operator: In
values: ["frontend", "edge"]
- key: env
operator: NotIn
values: ["test"]
该选择器选中的 Pod 必须同时满足:app=web、tier 的值为 frontend 或 edge、且 env 存在且不等于 test。若某个 Pod 没有 env 标签,则因 NotIn 要求键存在而被排除。
二、namespaceSelector 与 podSelector 的组合规则
跨命名空间通信控制的关键,在于 ingress.from 或 egress.to 中 namespaceSelector 与 podSelector 的书写位置。它们有两种截然不同的语义:
- 并列在同一列表项内(同一
from元素下同时写namespaceSelector和podSelector):表示“在匹配的命名空间中,再匹配其中的 Pod”,两者是“与”关系。 - 分别写在不同的列表项中:表示“或”关系,即满足任一来源即可放行。
此外,namespaceSelector 为空 {} 时表示“所有命名空间”,而完全省略 namespaceSelector 只写 podSelector 时,默认只作用于策略所在命名空间内的 Pod。这一区别是跨命名空间策略中最容易出错的地方。
namespaceSelector 与 podSelector 写成两个并列的 from 项,本意是“A 命名空间的 X 应用”,结果却变成“A 命名空间全部 Pod 或本命名空间 X 应用”,放行了远超预期的流量。三、跨命名空间通信的典型场景
假设集群中有 frontend、backend、monitoring 三个命名空间。backend 中的 api 服务只允许 frontend 命名空间内带 app=web 标签的 Pod 访问 8080 端口,同时允许 monitoring 命名空间内所有 Pod 抓取指标端口 9090。策略可写为:
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: frontend
podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 9090
这里第一条规则中两个选择器处于同一列表项,是“与”;第二条规则只写 namespaceSelector,表示该命名空间下所有 Pod。Kubernetes 1.21 起会自动为每个命名空间打上 kubernetes.io/metadata.name 标签,因此可以直接用它做命名空间匹配,无需手动打标。
四、匹配关系可视化
下图把“策略选择器 → 命名空间 → Pod 标签 → 端口”的匹配链路画出来,帮助对照理解“与/或”在结构上的位置差异。
五、实操验证与判断依据
策略下发后,不能只看 YAML 是否创建成功,而要用实际流量验证匹配结果。推荐按以下顺序排查:
- 确认目标 Pod 的标签:
kubectl get pod -n backend -l app=api --show-labels,核对podSelector是否真的命中。 - 确认命名空间标签:
kubectl get ns frontend --show-labels,检查kubernetes.io/metadata.name是否存在。 - 从来源 Pod 发起连接:
kubectl exec -n frontend web-0 -- curl -s -o /dev/null -w "%{http_code}" http://api.backend:8080/health,预期返回 200。 - 从非授权 Pod 发起连接,预期超时或被拒绝,以此确认策略确实收紧了范围。
若授权来源访问失败,优先检查三件事:目标 Pod 是否真的带有 app=api 标签;namespaceSelector 是否误写成了 matchLabels 下的错误键;以及端口号是否与容器实际监听端口一致。若未授权来源仍能访问,则多半是把“与”写成了两个并列的 from 项,导致规则被放宽为“或”。
掌握选择器语义后,跨命名空间通信控制就从“凭感觉写 YAML”变成了可推理、可验证的工程操作:先明确来源命名空间与来源 Pod 标签,再决定它们应处于同一列表项还是不同列表项,最后用真实请求验证放行与拒绝是否符合预期。