Namespace、Label、Selector 与资源所有权

Kubernetes 不靠目录组织资源

传统服务器上,我们习惯用目录表达归属:这个程序在 /opt/app-a,那个程序在 /opt/app-b。Kubernetes 的资源不是文件,它主要靠 Namespace、Label、Selector 和 OwnerReference 建立边界与关系。

这四个概念各管一件事:

概念回答的问题
Namespace资源位于哪个隔离空间
Label资源带有什么可查询身份
Selector我想选择哪些资源
OwnerReference这个资源由谁负责生命周期

它们经常一起出现,但不能相互替代。

Namespace:资源名称的房间,也是策略边界

本项目为每个 SubPool 派生一个专属 Namespace:

1
2
SubPool demo-pool   → Namespace subpool-demo-pool
SubPool team-a      → Namespace subpool-team-a

这样每个号池内部都可以使用固定名称:

1
2
subpool-demo-pool/cpa
subpool-team-a/cpa

两个 Service 都叫 cpa,因为它们位于不同 Namespace,不会冲突。

Namespace 还是很多策略的作用域:

  • ResourceQuota 限制整个 Namespace 的资源;
  • NetworkPolicy 通常在 Namespace 内选择 Pod;
  • Role 和 RoleBinding 可把权限约束在 Namespace;
  • Secret、ConfigMap、Service 和 PVC 默认只能被同 Namespace 的 Pod 直接引用。

不过 Namespace 不是虚拟机。多个 Namespace 的 Pod 仍可能运行在同一个 Node 上,共享同一个内核。它提供的是 API、命名和策略层隔离,真正的安全还依赖 RBAC、NetworkPolicy、Pod Security、容器隔离和底层运行时。

Cluster-scoped 与 namespaced

Kubernetes 对象分为两类。

Cluster-scoped 对象全局存在,例如:

  • Namespace;
  • Node;
  • ClusterRole、ClusterRoleBinding;
  • CRD;
  • 本项目的 SubPool
  • GatewayClass。

Namespaced 对象属于某个 Namespace,例如:

  • Pod、Deployment、StatefulSet;
  • Service、ConfigMap、Secret;
  • PVC、NetworkPolicy、ResourceQuota;
  • HTTPRoute;
  • Gateway。

可以用 API discovery 查询某个资源是否 namespaced:

1
2
3
kubectl api-resources
kubectl api-resources --namespaced=true
kubectl api-resources --namespaced=false

本项目故意把 SubPool 设计为 cluster-scoped,因为一个 SubPool 会创建并拥有一个 Namespace。若它自己先被放进某个业务 Namespace,生命周期与权限边界会更绕。

Label:贴在对象上的机器可读标签

Label 是简短键值对。项目使用 Kubernetes 推荐风格的应用标签:

1
2
3
4
5
labels:
  app.kubernetes.io/name: subpool
  app.kubernetes.io/component: runtime
  app.kubernetes.io/part-of: subpool
  subrelay.example.com/pool: demo-pool

这些标签既方便人查询,也让 Service、NetworkPolicy 和 PDB 能精确选择目标。

常用查询方式:

1
2
3
4
5
kubectl -n subpool-demo-pool get pods --show-labels
kubectl -n subpool-demo-pool get pods \
  -l app.kubernetes.io/component=runtime
kubectl get namespaces \
  -l subrelay.example.com/managed=true

Label 适合低基数、用于筛选的稳定信息。大段说明、时间戳、配置内容更适合 annotation,不应塞进 label。

Selector:用标签建立松耦合关系

Selector 是查询条件。例如 CPA Service 并不记录某个 Pod 的名字,而是声明:

1
2
3
4
selector:
  app.kubernetes.io/name: subpool
  app.kubernetes.io/component: runtime
  subrelay.example.com/pool: demo-pool

符合这些标签的 Pod 就成为它的后端。Pod 被重建、名字和 IP 改变后,只要新 Pod 仍带相同标签,Service 仍能找到它。

这就是 Kubernetes 常见的松耦合方式:对象不记住易变实例,而是描述“我需要哪一类实例”。

同样的 selector 还被用在:

  • StatefulSet 判断哪些 Pod 属于自己;
  • PDB 判断哪些 Pod 受中断预算保护;
  • NetworkPolicy 判断策略作用于哪些 Pod;
  • Mihomo NetworkPolicy 判断哪些号池 Pod 可以访问代理端口。

Selector 配错会发生什么

Selector 错误通常不会产生明显语法错误,却会造成很真实的故障:

  • Service 选不到 Pod,于是 EndpointSlice 为空;
  • Service 误选了其他 Pod,流量进入错误后端;
  • PDB 选不到 Pod,实际上没有保护任何工作负载;
  • NetworkPolicy 选错 Pod,可能该拦的没拦、该放的没放;
  • StatefulSet 的 selector 与 template labels 不匹配时,API Server 会拒绝对象。

排查 Service 时,应并排检查 selector 和 Pod labels:

1
2
3
4
kubectl -n subpool-demo-pool get service cpa -o yaml
kubectl -n subpool-demo-pool get pods --show-labels
kubectl -n subpool-demo-pool get endpointslice \
  -l kubernetes.io/service-name=cpa

OwnerReference:谁负责这个对象的生与死

Operator 创建资源时会写入 owner reference。概念上类似:

1
2
3
4
5
6
7
metadata:
  ownerReferences:
    - apiVersion: subrelay.example.com/v1alpha1
      kind: SubPool
      name: demo-pool
      uid: ...
      controller: true

这里最重要的是 UID,而不只是 name。即使旧的 demo-pool 被删除后又创建了同名对象,新对象的 UID 也不同,不能自然接管旧对象。

OwnerReference 有两个主要用途:

  • 让控制器判断对象是不是自己管理的;
  • 让 Kubernetes garbage collector 在 owner 删除后清理 dependents。

本项目对所有权非常保守。若 subpool-demo-pool Namespace 已存在,但 owner reference 不属于当前 SubPool,Operator 报告 NamespaceConflict,不会接管它。

这不是“自动化不够聪明”,而是安全边界。仅凭同名就接管资源,可能误删别人的 Namespace 和数据。

为什么 Namespace 也能由 SubPool 拥有

Namespaced owner 不能跨 Namespace 随意拥有其他 Namespace 的对象,但 cluster-scoped owner 可以拥有 namespaced 对象。本项目的 SubPool 是 cluster-scoped,因此可以成为派生 Namespace 的 controller owner。

具体生命周期是:

1
2
3
4
5
6
7
SubPool
  └─ Namespace subpool-demo-pool
       ├─ Secret
       ├─ StatefulSet
       ├─ Service
       ├─ PVC
       └─ NetworkPolicy 等

删除 SubPool 时,Operator 先明确删除自己拥有的 Namespace。Namespace 的删除又触发内部 namespaced 资源清理。

所有权与“名字一样”是两回事

项目中有两个典型冲突:

NamespaceConflict

派生 Namespace 已存在,但不属于当前 SubPool。Operator 停止创建,不覆盖 labels,也不在删除 SubPool 时删除它。

CredentialConflict

subpool-credentials Secret 已存在,但不由当前 SubPool 控制。Operator 不读取并当作自己的凭据,也不覆盖其内容。

正确判断逻辑是:

1
名称符合预期 + owner UID 符合预期 + 对象结构符合约定

缺一项都不应盲目接管。

Namespace label 还能连接跨 Namespace 策略

本项目给派生 Namespace 添加:

1
subrelay.example.com/managed: "true"

Shared Mihomo 的 NetworkPolicy 通过 namespaceSelector 只允许这些受管 Namespace 中的 CPA Pod访问 18000 端口。

Shared Envoy Gateway 的 listener 也用相同 Namespace label 限制哪些 Namespace 可以绑定 HTTPRoute:

1
2
3
4
5
6
allowedRoutes:
  namespaces:
    from: Selector
    selector:
      matchLabels:
        subrelay.example.com/managed: "true"

这是一种很漂亮的“共享基础设施准入证”:Operator 创建并标记受管 Namespace,共享网关只接受带这个标记的租户。

Pod Security 也通过 Namespace label 启用

Operator Configuration 还会把这些标签投影到每个号池 Namespace:

1
2
3
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted

它们启用 Pod Security Admission 的 restricted 级别。若 Pod 配置违反规则,enforce 会阻止创建,auditwarn 则留下审计或警告信号。

因此 Namespace 不只是“分类文件夹”,还是安全策略和准入策略的重要挂载点。

实用排查顺序

遇到“资源明明存在,但系统不工作”时,可以按下面检查:

1
2
3
4
5
6
kubectl get sp demo-pool -o yaml
kubectl get namespace subpool-demo-pool -o yaml
kubectl -n subpool-demo-pool get all --show-labels
kubectl -n subpool-demo-pool get service cpa -o yaml
kubectl -n subpool-demo-pool get endpointslice \
  -l kubernetes.io/service-name=cpa -o yaml

重点回答:

  1. Namespace 是否正确派生并带有 managed label?
  2. 子资源的 owner UID 是否指向当前 SubPool?
  3. Service、PDB、NetworkPolicy 的 selector 是否与 Pod label 完全匹配?
  4. 有没有同名但不属于当前 SubPool 的遗留资源?

理解这些关系后,Kubernetes 就不再是一堆相互独立的 YAML,而是一张由作用域、标签选择和生命周期所有权连接起来的对象图。

使用 Hugo 构建
主题 StackJimmy 设计