Kubernetes 不靠目录组织资源
传统服务器上,我们习惯用目录表达归属:这个程序在 /opt/app-a,那个程序在 /opt/app-b。Kubernetes 的资源不是文件,它主要靠 Namespace、Label、Selector 和 OwnerReference 建立边界与关系。
这四个概念各管一件事:
| 概念 | 回答的问题 |
|---|---|
| Namespace | 资源位于哪个隔离空间 |
| Label | 资源带有什么可查询身份 |
| Selector | 我想选择哪些资源 |
| OwnerReference | 这个资源由谁负责生命周期 |
它们经常一起出现,但不能相互替代。
Namespace:资源名称的房间,也是策略边界
本项目为每个 SubPool 派生一个专属 Namespace:
| |
这样每个号池内部都可以使用固定名称:
| |
两个 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:
| |
本项目故意把 SubPool 设计为 cluster-scoped,因为一个 SubPool 会创建并拥有一个 Namespace。若它自己先被放进某个业务 Namespace,生命周期与权限边界会更绕。
Label:贴在对象上的机器可读标签
Label 是简短键值对。项目使用 Kubernetes 推荐风格的应用标签:
| |
这些标签既方便人查询,也让 Service、NetworkPolicy 和 PDB 能精确选择目标。
常用查询方式:
| |
Label 适合低基数、用于筛选的稳定信息。大段说明、时间戳、配置内容更适合 annotation,不应塞进 label。
Selector:用标签建立松耦合关系
Selector 是查询条件。例如 CPA Service 并不记录某个 Pod 的名字,而是声明:
| |
符合这些标签的 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:
| |
OwnerReference:谁负责这个对象的生与死
Operator 创建资源时会写入 owner reference。概念上类似:
| |
这里最重要的是 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。
具体生命周期是:
| |
删除 SubPool 时,Operator 先明确删除自己拥有的 Namespace。Namespace 的删除又触发内部 namespaced 资源清理。
所有权与“名字一样”是两回事
项目中有两个典型冲突:
NamespaceConflict
派生 Namespace 已存在,但不属于当前 SubPool。Operator 停止创建,不覆盖 labels,也不在删除 SubPool 时删除它。
CredentialConflict
subpool-credentials Secret 已存在,但不由当前 SubPool 控制。Operator 不读取并当作自己的凭据,也不覆盖其内容。
正确判断逻辑是:
| |
缺一项都不应盲目接管。
Namespace label 还能连接跨 Namespace 策略
本项目给派生 Namespace 添加:
| |
Shared Mihomo 的 NetworkPolicy 通过 namespaceSelector 只允许这些受管 Namespace 中的 CPA Pod访问 18000 端口。
Shared Envoy Gateway 的 listener 也用相同 Namespace label 限制哪些 Namespace 可以绑定 HTTPRoute:
| |
这是一种很漂亮的“共享基础设施准入证”:Operator 创建并标记受管 Namespace,共享网关只接受带这个标记的租户。
Pod Security 也通过 Namespace label 启用
Operator Configuration 还会把这些标签投影到每个号池 Namespace:
| |
它们启用 Pod Security Admission 的 restricted 级别。若 Pod 配置违反规则,enforce 会阻止创建,audit 和 warn 则留下审计或警告信号。
因此 Namespace 不只是“分类文件夹”,还是安全策略和准入策略的重要挂载点。
实用排查顺序
遇到“资源明明存在,但系统不工作”时,可以按下面检查:
| |
重点回答:
- Namespace 是否正确派生并带有 managed label?
- 子资源的 owner UID 是否指向当前 SubPool?
- Service、PDB、NetworkPolicy 的 selector 是否与 Pod label 完全匹配?
- 有没有同名但不属于当前 SubPool 的遗留资源?
理解这些关系后,Kubernetes 就不再是一堆相互独立的 YAML,而是一张由作用域、标签选择和生命周期所有权连接起来的对象图。