Kubernetes 基础体系 · 第 7/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes Namespace:作用域、资源配额、权限、多租户和删除
Namespace(命名空间)是 Kubernetes API 中用于组织对象的逻辑边界。它最直接的作用是给一组命名空间作用域资源增加名称前缀,并让 RBAC、ResourceQuota、LimitRange 等机制能够以 Namespace 为治理单位。
Namespace 不是一台虚拟集群,也不是天然的安全边界。它能提供名称隔离、权限隔离和资源治理的基础,但无法单独完成网络、节点、内核、存储或控制面层面的强隔离。
本文示例使用当前稳定的 Kubernetes API:
v1:Namespace、Pod、Service、ResourceQuota、LimitRangerbac.authorization.k8s.io/v1:Role、RoleBinding、ClusterRole、ClusterRoleBindingnetworking.k8s.io/v1:NetworkPolicy
云厂商托管集群可能额外提供租户、项目、节点池或网络策略实现,但这些不属于 Namespace API 本身。
Namespace 到底解决了什么问题
在 Kubernetes 中,对象的完整身份通常由以下信息共同决定:
例如:
("", "pods", "team-a", "web")
("", "pods", "team-b", "web")
这两个 Pod 的名称都叫 web,但因为 Namespace 不同,所以可以同时存在。
对命名空间作用域资源,API 路径也体现了 Namespace:
/api/v1/namespaces/team-a/pods/web
/api/v1/namespaces/team-b/pods/web
而节点是集群作用域资源:
/api/v1/nodes/node-1
它没有 Namespace 部分。
命名空间作用域与集群作用域
常见资源可以分为两类。
| 类型 | 示例 | 是否属于某个 Namespace |
|---|---|---|
| 命名空间作用域 | Pod、Deployment、Service、ConfigMap、Secret、PVC、Role、RoleBinding、ResourceQuota、LimitRange | 是 |
| 集群作用域 | Node、Namespace、PersistentVolume、StorageClass、ClusterRole、ClusterRoleBinding、CustomResourceDefinition、PriorityClass | 否 |
可以用 API 发现信息确认资源是否 namespaced:
kubectl api-resources --verbs=list --namespaced
kubectl api-resources --verbs=list --namespaced=false
例如:
kubectl api-resources | grep -E 'pods|nodes|persistentvolumes|roles'
这比凭记忆判断更可靠,因为 CRD 是否 namespaced 由 CRD 定义决定。
Namespace 不会自动隔离资源引用
Namespace 主要约束对象的身份和权限范围,并不意味着所有引用都只能指向同一 Namespace。
例如:
- Pod 可以通过 DNS 访问另一个 Namespace 中的 Service;
- RoleBinding 可以把一个 Namespace 中的 ServiceAccount 授予另一个 Namespace 中的 Role;
- PVC 是 namespaced,但它绑定的 PersistentVolume 是 cluster-scoped;
StorageClass是 cluster-scoped;- Node 是 cluster-scoped,Pod 可以被调度到任意符合条件的节点;
- Image、RuntimeClass、PriorityClass 等资源通常不是 Namespace 私有的。
Service 的 DNS 名称体现了 Namespace:
web.team-a.svc.cluster.local
同一个 Namespace 内通常可以使用短名称:
web
但这只是集群 DNS 的解析约定,不是安全控制。NetworkPolicy 和 RBAC 才分别承担网络访问控制与 API 操作授权。
创建 Namespace 与选择默认作用域
最简单的 Namespace 定义如下:
apiVersion: v1
kind: Namespace
metadata:
name: team-a
labels:
tenant: team-a
environment: production
创建并验证:
kubectl apply -f namespace.yaml
kubectl get namespace team-a
kubectl describe namespace team-a
预期可看到:
NAME STATUS AGE
team-a Active ...
Namespace 名称需要符合 Kubernetes 对资源名称的约束,通常使用小写字母、数字和连字符,并且不能随意修改。需要改名时,通常是创建新 Namespace、迁移对象,再删除旧 Namespace,而不是修改 metadata.name。
命令行中可以通过 -n 指定 Namespace:
kubectl get pods -n team-a
kubectl apply -f deployment.yaml -n team-a
也可以为当前 kubeconfig 上下文设置默认 Namespace:
kubectl config set-context --current --namespace=team-a
kubectl config view --minify --output='jsonpath={..namespace}{"\n"}'
没有显式指定 Namespace 的 namespaced 对象,kubectl 通常使用当前上下文的 Namespace;如果上下文没有设置,则常见默认值是 default。这不是 API 要求对象必须属于 default,而是客户端的默认行为。
下面的命令可能产生误解:
kubectl get pods
它通常只列出当前 Namespace 的 Pod,而不是整个集群。查看所有 Namespace 需要显式指定:
kubectl get pods -A
但命令能否成功还取决于当前用户是否有跨 Namespace 列表权限。
Namespace 与资源配额
ResourceQuota 的作用
ResourceQuota(资源配额)是在一个 Namespace 内对资源总量设置上限的准入控制机制。它可以限制:
- 计算资源总量,例如 CPU 和内存;
- 某些对象的数量,例如 Pod、Service、Secret;
- 某些类别的 Pod,例如带有特定优先级或 QoS 分类的 Pod。
ResourceQuota 不是实时监控器。它主要在对象创建或更新进入 API Server 准入流程时,计算该操作是否会使 Namespace 超过配额。
以 CPU 请求为例,如果 Namespace 当前累计请求量为 ,本次操作新增请求为 ,配额上限为 ,则准入条件是:
如果条件不成立,API Server 拒绝请求,Pod 不会被成功创建。
配额的计算通常基于对象声明的 requests、limits 或对象数量,而不是节点上瞬时的实际 CPU 使用率。一个 Pod 即使实际只使用很少 CPU,只要它声明了较大的 requests.cpu,就会消耗相应的配额。
一个完整的配额示例
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
pods: "10"
services: "5"
count/secrets: "20"
requests.cpu: "2"
limits.cpu: "4"
requests.memory: 4Gi
limits.memory: 8Gi
应用并查看:
kubectl apply -f quota.yaml
kubectl describe resourcequota team-a-quota -n team-a
输出中的核心信息类似:
Resource Used Hard
-------- ---- ----
limits.cpu 1500m 4
limits.memory 1536Mi 8Gi
pods 3 10
requests.cpu 300m 2
requests.memory 384Mi 4Gi
services 1 5
count/secrets 2 20
Used 是当前 Namespace 中已经被配额统计的使用量,Hard 是上限。这里的 services: "5" 是资源短名数量配额;count/secrets: "20" 使用了通用对象计数语法。
可以进一步按 API 资源限制数量:
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: team-a
spec:
hard:
count/deployments.apps: "20"
count/jobs.batch: "50"
count/configmaps: "100"
count/secrets: "100"
具体资源是否支持计数,以及资源名称如何写,应以当前集群的 API 发现和 ResourceQuota 文档为准。对 CRD 对象进行数量治理时,也要确认集群版本和该资源是否支持对象计数配额。
requests、limits 与配额的关系
容器资源字段含义不同:
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
requests主要用于调度器判断节点是否有足够的可分配资源;limits主要用于运行时限制容器的资源使用;- ResourceQuota 可以分别统计
requests.*和limits.*; - quota 不会把实际 CPU 利用率当作
requests.cpu; - CPU limit 通常通过 cgroup 限制,内存 limit 超出时可能触发 OOM,具体行为还受运行时和内核影响。
例如,Namespace 配置:
requests.cpu: "2"
有三个 Pod,每个 Pod 中一个容器声明:
requests:
cpu: "500m"
则累计 CPU 请求为:
再创建一个 600m 请求的 Pod 时:
API Server 会拒绝该创建请求,常见错误类似:
exceeded quota: team-a-quota, requested: requests.cpu=600m,
used: requests.cpu=1500m, limited: requests.cpu=2
这里的失败发生在对象创建阶段,不是 Pod 先创建、再由调度器发现节点不足。
Pod 副本数与配额计算
Deployment 的副本数会间接产生 Pod,但 ResourceQuota 默认统计的是实际被创建的 Pod,而不是 Deployment 的 spec.replicas 字段本身。
例如:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: team-a
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:stable
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
创建后,配额中的 Pod 数量通常增加 3,CPU 请求增加:
如果还设置了:
count/deployments.apps: "20"
则 Deployment 对象本身也会消耗一个 Deployment 数量配额。
滚动更新时,旧 ReplicaSet 和新 ReplicaSet 可能短时间同时存在,Pod 数量可能暂时高于稳定副本数。因此,配额只设置为“刚好等于期望副本数”会使滚动更新失败。需要为更新过程预留临时容量,或者采用适合配额的滚动更新参数。
ResourceQuota 的边界
ResourceQuota 不等同于完整的资源隔离:
- 它限制的是 Namespace 的声明量,不保证租户实际获得独占 CPU;
- 一个 Namespace 中的 Pod 仍可能和其他 Namespace 的 Pod 运行在同一节点;
- CPU limit 不是 CPU 独占;
- 内存 limit 不能防止节点整体内存压力;
- 配额不自动限制节点数量、磁盘 I/O、网络带宽或 API 请求速率;
- ResourceQuota 本身不提供权限控制;
- 如果配额依赖 requests 或 limits,而工作负载没有填写这些字段,就需要配合 LimitRange 或准入策略补齐。
LimitRange:让配额能够被正确使用
ResourceQuota 负责 Namespace 级别的总量,LimitRange 负责 Namespace 内单个对象或容器的默认值与边界。两者解决的是不同问题:
| 机制 | 主要问题 |
|---|---|
| ResourceQuota | 这个 Namespace 总共可以使用多少 |
| LimitRange | 单个容器或 Pod 至少、最多、默认使用多少 |
示例:
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
cpu: 500m
memory: 512Mi
min:
cpu: 10m
memory: 16Mi
max:
cpu: "2"
memory: 2Gi
其效果是:
- 容器没有填写 requests 时,可能被补上
100mCPU 和128Mi内存; - 容器没有填写 limits 时,可能被补上
500mCPU 和512Mi内存; - 低于
min或高于max的容器请求会被拒绝; - LimitRange 不会把整个 Namespace 的总量限制为某个数值。
因此,前面的 Deployment 如果省略资源字段,LimitRange 可以使它获得默认资源值。三个副本会按默认值消耗约:
requests.cpu = 3 × 100m = 300m
requests.memory = 3 × 128Mi = 384Mi
limits.cpu = 3 × 500m = 1500m
limits.memory = 3 × 512Mi = 1536Mi
实际准入结果还应通过对象查询确认:
kubectl get deployment web -n team-a -o yaml
kubectl get pod -n team-a -o yaml
需要注意,LimitRange 的默认值是准入时写入对象或影响准入判断的默认行为,不是调度器在运行时临时猜测的值。修改 LimitRange 通常不会自动重写已经存在的 Pod;要让旧 Pod 使用新默认值,通常需要重新创建或通过控制器滚动更新。
Namespace 与 RBAC 权限
RBAC(Role-Based Access Control,基于角色的访问控制)回答的是:
某个主体能否对某个 API 资源执行某个动作?
它不是 Namespace 的附属属性,而是单独的授权机制。
Role 与 RoleBinding
Role 是 Namespace 作用域资源,描述该 Namespace 内允许的 API 操作:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: workload-reader
namespace: team-a
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
RoleBinding 将角色授予主体:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alice-workload-reader
namespace: team-a
subjects:
- kind: User
name: alice@example.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: workload-reader
apiGroup: rbac.authorization.k8s.io
验证权限:
kubectl auth can-i get pods \
--as=alice@example.com \
-n team-a
kubectl auth can-i get pods \
--as=alice@example.com \
-n team-b
第一个命令预期为 yes,第二个通常为 no,因为 RoleBinding 只存在于 team-a。
RBAC 默认是允许规则累加模型:
- 没有显式拒绝规则;
- 多个 RoleBinding 的权限会合并;
- 删除一个 Binding 后,主体可能仍通过另一个 Binding 保有权限。
ServiceAccount 的 Namespace 归属
工作负载通常以 ServiceAccount 身份访问 Kubernetes API:
apiVersion: v1
kind: ServiceAccount
metadata:
name: deployer
namespace: team-a
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-binding
namespace: team-a
subjects:
- kind: ServiceAccount
name: deployer
namespace: team-a
roleRef:
kind: Role
name: workload-reader
apiGroup: rbac.authorization.k8s.io
ServiceAccount 的完整主体名是:
system:serviceaccount:team-a:deployer
一个 Namespace 中的 RoleBinding 可以引用另一个 Namespace 中的 ServiceAccount,但这会形成跨租户授权,必须显式审查。
ClusterRole 不一定意味着集群级权限
ClusterRole 是集群作用域的角色对象,但它可以通过 RoleBinding 在某一个 Namespace 中授予命名空间资源权限:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-a-view
namespace: team-a
subjects:
- kind: Group
name: team-a-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
此时 view 中对 Pod、Service 等 namespaced 资源的权限只在 team-a 生效。
相反,ClusterRoleBinding 会把角色授予整个集群范围。例如:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: team-a-admin
subjects:
- kind: Group
name: team-a-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io
这类绑定可能使主体获得所有 Namespace 的管理权限,不能因为角色名称中没有 cluster 就认为它是租户内权限。
Role 不能授权访问集群作用域资源。例如,Role 中写入 nodes 并不能让主体访问节点;访问 Node 必须使用适当的 ClusterRole 和 ClusterRoleBinding。Namespace 本身也是集群作用域资源,删除 Namespace 通常需要对 Namespace 资源拥有集群级权限。
Namespace 与多租户
多租户(multi-tenancy)是多个相互独立的团队、项目或客户共享一个 Kubernetes 集群,同时限制彼此影响和访问的架构问题。
Namespace 是多租户的常见组织单位,但一个可用的租户边界至少包含以下维度:
| 维度 | 主要机制 | 解决的问题 |
|---|---|---|
| API 权限 | RBAC | 谁能读写哪些对象 |
| 资源预算 | ResourceQuota | 租户最多声明多少资源或对象 |
| 单对象约束 | LimitRange、准入策略 | 单个容器的默认值和上下限 |
| 网络访问 | NetworkPolicy | Pod 之间允许哪些网络连接 |
| 调度边界 | taint、toleration、nodeAffinity、节点池 | 工作负载能运行在哪些节点 |
| 凭据与配置 | Secret、外部密钥系统、RBAC | 谁能读取敏感数据 |
| API 治理 | Admission webhook、Pod Security Admission | 哪些对象可以被创建 |
| 存储边界 | StorageClass、CSI、云厂商策略 | 数据卷的生命周期和访问范围 |
| 故障边界 | 独立集群、节点池、控制面 | 租户之间能否互相造成基础设施故障 |
网络默认行为不是租户隔离
如果集群没有 NetworkPolicy,许多常见网络插件的默认行为是允许 Pod 之间通信。此时下面两个 Pod 即使属于不同 Namespace,也可能互相访问:
team-a/web -> team-b/database
要改变这个行为,需要网络插件支持并正确实现 networking.k8s.io/v1 的 NetworkPolicy。例如,在 team-a 中启用入口默认拒绝:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Ingress
这表示选择 team-a 中所有 Pod,并对入口流量启用隔离。它不自动限制出口流量,也不自动允许同 Namespace 的通信。
允许 team-a 中的 Pod 访问 team-b 中带有标签的服务端 Pod,可以在 team-b 配置:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-team-a
namespace: team-b
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: team-a
这里的 namespaceSelector 选择 Namespace 标签,而不是 Namespace 名称本身。team-b Namespace 必须确实带有:
metadata:
labels:
tenant: team-b
NetworkPolicy 的实际能力依赖网络插件。API 对象创建成功,不代表底层网络实现一定支持所有行为;应通过实际连接测试验证。NetworkPolicy 通常也不等同于节点级防火墙,不能替代云网络安全组或主机安全策略。
Namespace 不是强安全边界
以下情况会突破“只划分 Namespace 就完成隔离”的假设:
- 用户拥有创建
privilegedPod 的权限; - Pod 使用
hostNetwork、hostPID或hostPath; - 容器拥有过高 Linux capabilities;
- 用户可以创建或修改跨 Namespace 的 RoleBinding;
- 用户可以读取其他 Namespace 的 Secret;
- 节点内核、容器运行时或 CSI 驱动存在漏洞;
- 租户共享节点导致噪声、侧信道或节点级资源争用;
- 用户可以创建影响全局的 CRD、Webhook、ClusterRole 或 StorageClass;
- Admission 策略没有限制危险的 Pod Security 设置。
Pod Security Admission 可以按 Namespace 标签启用 Pod 安全级别。例如:
kubectl label namespace team-a \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
restricted 会拒绝不符合 Pod Security Standards 的 Pod,但它仍不提供完整的租户隔离;应用还需要合理的 RBAC、NetworkPolicy、Quota 和节点治理。
如果租户之间存在强安全、强合规或高风险需求,独立集群通常比共享 Namespace 更容易验证隔离边界。Namespace 多租户适合逻辑隔离和组织治理,但不能被描述为“等价于虚拟机”或“等价于独立集群”。
Namespace 删除的状态机
Namespace 删除不是简单删除一行元数据。它大致经历以下状态:
stateDiagram-v2
[*] --> Active
Active --> Terminating: DELETE Namespace
Terminating --> Terminating: 清理对象或 finalizer 未完成
Terminating --> [*]: finalizers 完成且对象清理完成
删除请求被 API Server 接受后,Namespace 通常不再保持 Active,而进入:
Terminating
此时 Namespace 的删除时间戳已经写入对象。Namespace Controller 会尝试:
- 发现当前集群中可用的 API 资源;
- 枚举该 Namespace 中的 namespaced 对象;
- 删除这些对象;
- 等待对象上的 finalizer 完成;
- 在清理完成后移除 Namespace 自身的最终 finalizer。
典型删除命令:
kubectl delete namespace team-a
这是高风险操作。它会触发 team-a 中 Deployment、Pod、Service、ConfigMap、Secret、PVC、RoleBinding、ResourceQuota 等 namespaced 对象的清理。PV 是 cluster-scoped,不会因为 Namespace 删除而自动删除;但 PVC 删除可能触发底层存储供应商根据 StorageClass 回收策略处理对应的 PV 和云盘。
finalizer 为什么会阻塞删除
Finalizer 是对象元数据中的一个字符串列表,用于表达:
在真正删除对象前,某个控制器必须先完成外部清理。
删除对象时,API Server 通常先写入 deletionTimestamp,对象进入删除中状态,而不是立即从存储中消失。只有相关 finalizer 被控制器移除后,对象才会完成删除。
Namespace 卡在 Terminating 时,常见原因包括:
- Namespace 中存在带 finalizer 的对象;
- 对应的控制器已经停止运行;
- 某个 APIService 不可用,Namespace Controller 无法发现资源;
- CRD 被删除,但残留对象或控制器清理逻辑不完整;
- Webhook、聚合 API 或控制器持续返回错误。
诊断步骤:
kubectl get namespace team-a -o yaml
kubectl describe namespace team-a
kubectl get apiservice | grep -v True
重点查看 Namespace 的状态条件:
kubectl get namespace team-a \
-o jsonpath='{range .status.conditions[*]}{.type}={.status} reason={.reason} message={.message}{"\n"}{end}'
也可以查看 Namespace 中的资源:
kubectl api-resources --verbs=list --namespaced -o name | \
while read resource; do
kubectl get "$resource" -n team-a --ignore-not-found
done
该方法会受权限、API 聚合服务和资源数量影响;如果某类资源的 API 不可用,命令本身也可能报错,错误信息就是诊断线索之一。
强制移除 Namespace finalizer 的风险
在确认阻塞原因并完成必要清理后,才考虑移除 Namespace 的 finalizer。常见的低风险路径是先修复对应控制器或删除阻塞对象,而不是直接修改 finalizer。
强制 finalize 的操作示例:
kubectl get namespace team-a -o json > team-a.json
编辑 team-a.json,只在确认风险后移除 Namespace 对象中的 spec.finalizers,然后通过 finalize API 提交。不同客户端版本对 kubectl replace --raw 的用法可能存在差异,执行前应检查当前客户端帮助信息:
kubectl replace --raw \
"/api/v1/namespaces/team-a/finalize" \
-f team-a.json
该操作的危险在于:
- 它可能使 Namespace 对象消失,但某些对象没有完成控制器清理;
- 外部云资源、DNS、负载均衡器或存储资源可能残留;
- 某些资源可能成为无法通过正常 Namespace 路径访问的孤儿对象;
- 重新创建同名 Namespace 后,残留资源或旧控制器可能产生混淆。
因此,强制移除 finalizer 不是“修复删除”的通用按钮,而是放弃一部分由控制器提供的清理保证。执行后应检查:
kubectl get namespace team-a
kubectl get pv
kubectl get service --all-namespaces
kubectl get events --all-namespaces --sort-by=.lastTimestamp
还需要到云厂商控制台或 CSI、Ingress、LoadBalancer 控制器的日志中确认外部资源是否已经删除。
常见误解与失败路径
误解一:Namespace 名称不同,Pod 就不能互相访问
错误。Namespace 主要影响对象作用域和权限,不会自动创建网络隔离。需要 NetworkPolicy,并且网络插件必须支持和执行这些策略。
误解二:给 Namespace 设置 CPU 配额,就获得了独占 CPU
错误。配额限制的是声明量和准入量。不同 Namespace 的 Pod 仍可能共享节点 CPU。要实现更强的节点级隔离,需要节点池、taint、toleration、亲和性和底层基础设施配合。
误解三:ResourceQuota 会替用户补齐资源字段
错误。ResourceQuota 负责总量限制,LimitRange 才负责默认 requests 和 limits。生产治理中常把二者一起配置,但它们不是同一个机制。
误解四:删除 Deployment 后,Namespace 配额立即归零
通常会下降,但不一定立即归零。控制器删除 Pod、ReplicaSet、Job 或其他对象是异步过程;带 finalizer 的对象也可能延迟消失。应通过以下命令观察实际状态:
kubectl describe resourcequota -n team-a
kubectl get all -n team-a
kubectl get events -n team-a --sort-by=.lastTimestamp
误解五:Namespace 删除成功就代表云资源全部删除
不一定。Kubernetes 对象和外部资源的删除依赖控制器、finalizer、CSI 驱动以及云厂商实现。尤其是 LoadBalancer、云盘、DNS 记录和外部数据库,必须检查实际云资源状态。
误解六:能读取 Namespace 就能读取其中所有对象
错误。get namespaces 和 get pods -n team-a 是不同资源、不同授权检查。主体即使能看到 Namespace 名称,也可能没有读取其中 Secret 或 Pod 的权限。
一个可验证的租户基础配置
下面的配置把 Namespace、LimitRange、ResourceQuota 和最小 RBAC 组合起来:
apiVersion: v1
kind: Namespace
metadata:
name: team-a
labels:
tenant: team-a
---
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: team-a
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
cpu: 500m
memory: 512Mi
min:
cpu: 10m
memory: 16Mi
max:
cpu: "2"
memory: 2Gi
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: budget
namespace: team-a
spec:
hard:
pods: "10"
requests.cpu: "2"
limits.cpu: "4"
requests.memory: 4Gi
limits.memory: 8Gi
services: "5"
count/secrets: "20"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-reader
namespace: team-a
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-a-readers
namespace: team-a
subjects:
- kind: Group
name: team-a-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: app-reader
apiGroup: rbac.authorization.k8s.io
应用后验证:
kubectl apply -f team-a-governance.yaml
kubectl get namespace team-a
kubectl describe resourcequota -n team-a
kubectl describe limitrange -n team-a
kubectl auth can-i list pods \
--as-group=team-a-developers \
--as=alice@example.com \
-n team-a
然后创建一个不声明资源字段的 Pod 或 Deployment,检查 API Server 是否写入了 LimitRange 默认值:
kubectl get pod -n team-a -o yaml
再创建超过配额的工作负载,观察 API Server 返回的 exceeded quota 错误。这个验证顺序很重要:
- 先确认 Namespace 存在且为
Active; - 确认 LimitRange 和 ResourceQuota 已被 API Server 接受;
- 确认 RBAC 主体确实使用预期身份;
- 检查生成对象中的实际 requests 和 limits;
- 最后验证超额请求是否被拒绝;
- 如果网络隔离也在要求范围内,再使用实际连接测试验证 NetworkPolicy,而不能只检查策略对象存在。
结语:Namespace 的准确定位
Namespace 是 Kubernetes 中连接多个机制的核心作用域:
- 它让 namespaced 对象可以在不同团队之间重用名称;
- ResourceQuota 用它计算租户预算和对象数量;
- LimitRange 用它提供单容器默认值与边界;
- Role 和 RoleBinding 用它限制 API 权限范围;
- NetworkPolicy 用它表达一部分租户网络边界;
- Namespace Controller 在删除时以它为单位清理 namespaced 对象。
但 Namespace 本身不提供完整多租户隔离,也不保证独占计算资源、网络、节点、存储或内核。可靠的租户治理必须明确区分:
删除 Namespace 也不是瞬时操作,而是由 API Server、Namespace Controller、各类资源控制器和 finalizer 共同完成的异步生命周期。理解这些边界,才能在配额拒绝、权限不足、跨租户访问和 Namespace 卡在 Terminating 时,沿着正确的组件和状态路径进行诊断。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes Label、Selector、Annotation 与 OwnerReference:对象关系设计
- 下一篇:Kubernetes 调谐循环:Desired State、Watch、Queue、幂等和最终一致
- 延伸:ResourceQuota 与 LimitRange:租户预算、默认值、对象数量和治理
- 延伸:Kubernetes 多租户:Namespace、RBAC、NetworkPolicy、Quota 和隔离极限
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论