------ 同域隔离、跨域协同与配置实战
在 Kubernetes 中,Namespace(命名空间) 是逻辑隔离单元,而 Affinity/Anti-Affinity(亲和/反亲和) 是调度约束。
很多用户有一个误区:认为亲和性规则会自动在全集群生效。事实并非如此。 默认情况下,亲和性规则被严格限制在当前 Namespace 内。
本教程将详细解析:
- 默认行为:为什么不同 Namespace 的 Pod 互不影响?
- 跨 Namespace 生效机制:如何打破隔离?
- 实战配置:同 Namespace 与跨 Namespace 的具体 YAML 写法。
- 最佳实践与陷阱。
第一部分:核心概念与默认行为
1. 默认规则:Namespace 是"隐形墙"
当你配置 podAffinity 或 podAntiAffinity 时,如果没有显式指定 namespaces 字段,Kubernetes 调度器只会 在当前 Pod 所在的 Namespace 中查找匹配标签的其他 Pod。
场景演示
假设集群有两个 Namespace:ns-prod 和 ns-dev。
- ns-prod 中运行着
app=redis(Pod A)。 - ns-dev 中运行着
app=redis(Pod B)。 - 你在 ns-prod 中部署一个新的
app=webPod,配置了针对app=redis的反亲和性。
结果:
- ✅ 生效 :新 Pod 会避开 ns-prod 中的 Pod A。
- ❌ 不生效 :新 Pod 不会 避开 ns-dev 中的 Pod B。
- 原因 :调度器在 ns-prod 中查找
app=redis,找到了 Pod A,但根本不去 ns-dev 查找。Pod B 对它是"不可见"的。
结论 :默认情况下,不同 Namespace 的 Pod 互不干扰。即使标签完全相同,它们也可以调度到同一台节点上。
第二部分:如何跨 Namespace 生效?
要打破 Namespace 的隔离,必须在 podAffinityTerm 中显式使用 namespaces 字段。
1. 关键字段:namespaces
podAffinityTerm:
labelSelector: ...
topologyKey: ...
namespaces:
- "target-namespace-1"
- "target-namespace-2"
- 作用:告诉调度器:"去这些指定的 Namespace 里查找匹配标签的 Pod"。
- 注意 :如果省略此字段,默认值为
[当前Pod的Namespace]。
2. 跨 Namespace 的两种模式
模式 A:单向关注(我关注你)
- 场景 :
ns-web的 Pod 想要避开ns-db的 Pod。 - 配置 :在
ns-web的 Pod 配置中,设置namespaces: ["ns-db"]。 - 效果:Web Pod 会避开 DB Pod,但 DB Pod 不受 Web Pod 影响(除非 DB 也配置了反向规则)。
模式 B:双向互斥(我们互相避开)
- 场景 :
ns-ai和ns-data的任务都极度消耗 GPU,不能共存于同一节点。 - 配置 :
ns-ai的 Pod 配置反亲和性,namespaces: ["ns-ai", "ns-data"]。ns-data的 Pod 配置反亲和性,namespaces: ["ns-ai", "ns-data"]。
- 效果:无论谁先启动,后启动的那个都会避开对方所在的节点。
第三部分:实战配置模板
场景 1:同 Namespace 内的反亲和(默认行为)
目标 :确保 my-app 的副本分散在 default 命名空间的不同节点上。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
spec:
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: my-app
topologyKey: "kubernetes.io/hostname"
# 注意:这里没有写 namespaces,默认只在 default 内生效
containers:
- name: app
image: nginx
场景 2:跨 Namespace 的反亲和(显式配置)
目标 :ns-frontend 的 Web 应用,必须避开 ns-backend 中所有 role=database 的 Pod,无论它们在哪。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
namespace: ns-frontend
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchLabels:
role: database
topologyKey: "kubernetes.io/hostname"
namespaces:
- "ns-backend" # <--- 关键:指定去 ns-backend 查找
containers:
- name: web
image: nginx
调度逻辑:
- 调度器查看
ns-backend中所有role=database的 Pod。 - 获取这些 Pod 所在节点的 hostname。
- 禁止将
web-frontend调度到这些节点上。 ns-frontend内部的其他 Pod 不会 影响此规则(除非你也把ns-frontend加入列表)。
场景 3:多 Namespace 全局互斥
目标 :ns-job-a 和 ns-job-b 的任务不能跑在同一台机器上。
配置 Job A (ns-job-a):
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
type: heavy-compute
topologyKey: "kubernetes.io/hostname"
namespaces:
- "ns-job-a"
- "ns-job-b"
配置 Job B (ns-job-b):
(配置同上,labels 也要匹配 type: heavy-compute)
第四部分:亲和性(Affinity)的跨 Namespace 行为
虽然反亲和性更常用,但**亲和性(我想靠近)**同样受 Namespace 限制。
场景:缓存加速
ns-cache中有 Redis。ns-app中有 Web 应用。- 希望 Web 应用尽量靠近 Redis。
错误配置(无效):
# 在 ns-app 的 Pod 中
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchLabels:
app: redis
topologyKey: "topology.kubernetes.io/zone"
# 没写 namespaces,默认只在 ns-app 找 redis -> 找不到 -> 规则无效
正确配置(有效):
# 在 ns-app 的 Pod 中
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: redis
topologyKey: "topology.kubernetes.io/zone"
namespaces:
- "ns-cache" # <--- 明确去 ns-cache 找
第五部分:常见陷阱与最佳实践
🕳️ 陷阱 1:权限问题(RBAC)
跨 Namespace 调度需要调度器有权限读取其他 Namespace 的 Pod 信息。
- 官方调度器:默认拥有集群级读取权限,通常没问题。
- 自定义调度器 :如果使用第三方调度器,需确保其 ServiceAccount 有
get/list pods跨 Namespace 的权限。
🕳️ 陷阱 2:循环依赖与死锁
如果在两个 Namespace 之间配置了硬反亲和性(Required),且节点资源紧张,可能导致死锁。
- 例子 :
- Node 1 跑了 Pod A (
ns-1)。 - Pod B (
ns-2) 想避开 Pod A,只能去 Node 2。 - Pod C (
ns-1) 想避开 Pod B,只能去 Node 1(但 Node 1 有 Pod A,如果 A 和 C 也互斥,则失败)。
- Node 1 跑了 Pod A (
- 建议 :跨 Namespace 尽量使用
preferred(软策略) 或TopologySpreadConstraints,避免硬死锁。
🕳️ 陷阱 3:性能开销
- 同 Namespace:调度器只扫描一个 Namespace 的 Pod,速度快。
- 跨 Namespace:调度器需要扫描多个 Namespace 的 Pod。如果指定的 Namespace 很多,或者集群规模巨大,会增加调度延迟。
- 建议 :只列出必要的 Namespace,不要随意使用"所有 Namespace"(虽然 K8s 没直接提供
*通配符,但列出所有 NS 等效于此)。
✅ 最佳实践总结
- 默认隔离是好事:利用 Namespace 的天然隔离,减少调度复杂度。大多数应用只需关心同 Namespace 内的分布。
- 明确列出 Namespace :如果需要跨域,务必在
namespaces字段中明确列出目标 Namespace,不要依赖隐式行为。 - 优先软策略 :跨 Namespace 的资源争用通常很复杂,使用
preferredDuringSchedulingIgnoredDuringExecution比required更安全,能避免 Pending。 - 标签规范 :跨 Namespace 协作时,确保双方使用的
labelSelector标签含义一致(例如都使用app: redis或role: db),最好通过团队约定或 OPA/Gatekeeper 策略来管理。
第六部分:调试与验证
1. 验证规则是否生效
# 查看 Pod 事件,确认调度器是否检查了目标 Namespace
kubectl describe pod <pod-name> -n <source-ns>
在 Events 中,如果你看到类似 PreFilter plugin failed 或 No nodes matched,检查是否因为跨 Namespace 规则导致无节点可用。
2. 模拟测试
创建一个临时 Pod,观察它是否避开了目标 Namespace 的节点。
# 在 ns-A 创建 Pod,配置避开 ns-B 的节点
kubectl run test-pod --image=nginx -n ns-A --dry-run=client -o yaml > test.yaml
# 编辑 test.yaml 添加跨 ns 反亲和性
kubectl apply -f test.yaml
# 观察 test-pod 被调度到了哪个节点
kubectl get pod test-pod -n ns-A -o wide
总结
全屏复制
| 特性 | 同 Namespace (默认) | 跨 Namespace (配置后) |
|---|---|---|
| 搜索范围 | 仅当前 NS | 指定的 NS 列表 |
| 配置字段 | 无需配置 namespaces |
必须配置 namespaces: ["ns-x"] |
| 隔离性 | 强隔离,互不影响 | 弱隔离,可互相约束 |
| 适用场景 | 单应用高可用、内部微服务 | 全局资源隔离、跨团队协作、混合部署 |
记住一句话 :Namespace 是亲和性的边界,除非你用 namespaces 字段打破它。