Kubernetes 亲和性与命名空间(Namespace)深度指南

------ 同域隔离、跨域协同与配置实战

在 Kubernetes 中,Namespace(命名空间) 是逻辑隔离单元,而 Affinity/Anti-Affinity(亲和/反亲和) 是调度约束。

很多用户有一个误区:认为亲和性规则会自动在全集群生效。事实并非如此。 默认情况下,亲和性规则被严格限制在当前 Namespace 内。

本教程将详细解析:

  1. 默认行为:为什么不同 Namespace 的 Pod 互不影响?
  2. 跨 Namespace 生效机制:如何打破隔离?
  3. 实战配置:同 Namespace 与跨 Namespace 的具体 YAML 写法。
  4. 最佳实践与陷阱

第一部分:核心概念与默认行为

1. 默认规则:Namespace 是"隐形墙"

当你配置 podAffinitypodAntiAffinity 时,如果没有显式指定 namespaces 字段,Kubernetes 调度器只会当前 Pod 所在的 Namespace 中查找匹配标签的其他 Pod。

场景演示

假设集群有两个 Namespace:ns-prodns-dev

  • ns-prod 中运行着 app=redis (Pod A)。
  • ns-dev 中运行着 app=redis (Pod B)。
  • 你在 ns-prod 中部署一个新的 app=web Pod,配置了针对 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-ains-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

调度逻辑:

  1. 调度器查看 ns-backend 中所有 role=database 的 Pod。
  2. 获取这些 Pod 所在节点的 hostname。
  3. 禁止将 web-frontend 调度到这些节点上。
  4. ns-frontend 内部的其他 Pod 不会 影响此规则(除非你也把 ns-frontend 加入列表)。

场景 3:多 Namespace 全局互斥

目标ns-job-ans-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 也互斥,则失败)。
  • 建议 :跨 Namespace 尽量使用 preferred(软策略)TopologySpreadConstraints,避免硬死锁。

🕳️ 陷阱 3:性能开销

  • 同 Namespace:调度器只扫描一个 Namespace 的 Pod,速度快。
  • 跨 Namespace:调度器需要扫描多个 Namespace 的 Pod。如果指定的 Namespace 很多,或者集群规模巨大,会增加调度延迟。
  • 建议 :只列出必要的 Namespace,不要随意使用"所有 Namespace"(虽然 K8s 没直接提供 * 通配符,但列出所有 NS 等效于此)。

✅ 最佳实践总结

  1. 默认隔离是好事:利用 Namespace 的天然隔离,减少调度复杂度。大多数应用只需关心同 Namespace 内的分布。
  2. 明确列出 Namespace :如果需要跨域,务必在 namespaces 字段中明确列出目标 Namespace,不要依赖隐式行为。
  3. 优先软策略 :跨 Namespace 的资源争用通常很复杂,使用 preferredDuringSchedulingIgnoredDuringExecutionrequired 更安全,能避免 Pending。
  4. 标签规范 :跨 Namespace 协作时,确保双方使用的 labelSelector 标签含义一致(例如都使用 app: redisrole: db),最好通过团队约定或 OPA/Gatekeeper 策略来管理。

第六部分:调试与验证

1. 验证规则是否生效

复制代码
# 查看 Pod 事件,确认调度器是否检查了目标 Namespace
kubectl describe pod <pod-name> -n <source-ns>

在 Events 中,如果你看到类似 PreFilter plugin failedNo 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 字段打破它。