在多租户集群里,资源隔离的边界不在某一堵墙上,而在控制面策略与数据面机制的交接处。 换句话说,你拦得住CPU和内存的争抢,不一定拦得住网络抖动,更拦不住的是“伦理上不该互相看见”的配置信息。
先给结论:真正的隔离边界,是配额、命名空间、网络策略与存储权限四层叠加之后留下的“组合拳”空隙。 空隙越小,隔离越干净;空隙越大,故障半径越大。
隔离的第一道边界:控制面的“软墙”
命名空间不是安全边界,只是视觉分区
业内专家指出,很多刚接触Kubernetes的团队会把Namespace当成隔离的全部,这是一个危险的错觉,Namespace解决的是“你能看到谁”,而不是“你能碰到谁”,在同一个Namespace里,你可以调度资源、读取Secret、修改ConfigMap,如果RBAC(基于角色的访问控制)配置得粗放,一个被攻破的Pod就能横向移动。
真正的控制面边界,必须由RBAC + ResourceQuota + LimitRange三件套构成,RBAC定义“谁有权限动什么”,ResourceQuota定义“这一个租户最多吃多少总量”,LimitRange定义“单一个容器不能超过多少”,三者叠加,才算在API Server层面画出了一条看得见的线。
实操中需要注意一个常见坑:ResourceQuota默认只覆盖CPU和内存,但跨租户的存储类(StorageClass)不归它管,如果你的租户A能创建PVC(持久化卷声明)并且用一个共享的NFS类存储,那么租户B的数据可能就睡在同一块磁盘上。
数据面的边界:Linux内核才是真正的裁判
CPU隔离的“假象”与内存隔离的“真相”
你设置了requests和limits,Pod就能被稳稳隔离了吗?在CPU维度,requests是调度依据,limits才是硬边界,当CPU资源争抢加剧时,内核的CFS调度器会给超限的容器降权,你会看到Node的load average很高,但个别Pod的CPU使用率被打成一条直线,这就是cgroup的throttle机制在生效。
内存维度更严格,一旦进程触及cgroup的memory limit,内核会直接触发OOM Killer,这里有一个容易被忽略的边界问题:当Pod内存超限,可能被杀的并不一定是那个罪魁祸首容器

如果Pod里有多个进程,内核挑的是score最高的那个进程,单容器单进程不是洁癖,而是内存隔离边界清晰化的前提。
网络隔离:默认全通,需要自己“拧紧”
很多人在规划多租户集群时,把注意力放在CPU和内存上,网网络策略放在最后考虑,但请记住:Kubernetes默认的Pod网络是“全通”的任何Pod都可以访问其他Pod,这意味着只要你跑的是多租户集群,PodA和PodB之间哪怕不在一个命名空间,也可能直接通过ClusterIP打通。
要收紧这道边界,你需要NetworkPolicy,它工作在三/四层,你可以规定:租户A的某个应用只能访问租户A自己的DB服务,其他入站流量一律拒绝,但这里有个很现实的边界盲区:NetworkPolicy只管Pod到Pod的流量,管不了Pod到Service、Pod到NodePort的流量,如果你的服务类型是NodePort,外部流量绕过Pod网络直接打到节点端口,策略就会失效。
存储隔离:最容易被攻破的边界
相比CPU争抢和网络抖动,存储隔离的失效往往是静默的,多租户场景里,即使你给每个租户分配了独立的PVC,只要底层PV是用同一种存储类动态创建的,且存储后端没有启用专池(专用性能池)隔离,租户A的IOPS大量消耗时,租户B的数据库延迟也会跟着飙升。
在文件系统层面,设置FSGroup和SupplementalGroup能防读取,但防不了磁盘IO抢占,如果想做到比较干净的存储隔离,要么依赖存储后端的租户专属卷(比如Ceph的pool、NFS的子目录挂载),要么使用CSI的Topology(拓扑感知调度)把租户A的Pod钉在特定存储节点上。
多维度隔离配置的实际操作路径
以下是一套经实践验证的配置顺序,按步骤操作可少走弯路:
-
划定租户与命名空间
- 每个租户一个Namespace,命名规则建议用
ns-{租户名},避免后续审计时出现命名歧义。 - 开启
metadata.namespace的标签,比如tenant=alpha、env=prod。
- 每个租户一个Namespace,命名规则建议用
-
定义ResourceQuota与LimitRange
- ResourceQuota必须包含:
requests.cpu、requests.memory
、
limits.cpu、limits.memory、persistentvolumeclaims。 - LimitRange设默认值,避免租户里出现一个不写limit的“裸奔”Pod。
- ResourceQuota必须包含:
-
编写NetworkPolicy的默认拒绝规则
- 先给每个租户套一个
policyTypes: [Ingress, Egress]且spec: {}的规则,表示默认全拒。 - 再按业务需要放行具体流向,例如放行来自网关Pod的80端口。
- 先给每个租户套一个
-
验证隔离边界
- 用
kubectl -n ns-a exec进入一个Pod,尝试访问ns-b里的Service ClusterIP,观察是否超时。 - 用
kubectl top pods -n ns-a查看资源水位,然后手动在租户B压测,交替观察租户A的P95延迟是否被拖垮。
- 用
下表可以直观展示三种基础隔离维度的边界归属:
| 隔离维度 | 生效层 | 边界失效场景 | 推荐策略 |
|---|---|---|---|
| CPU/内存 | cgroup/调度器 | 节点内存耗尽触发全局OOM | 设置nodeSelector隔离物理机 |
| 网络 | iptables/ipvs | 未配置NetworkPolicy时默认全通 | 默认拒绝+白名单规则 |
| 存储 | CSI/文件系统权限 | 共享存储池导致IO争抢 | 专用存储池或Topology约束 |
隔离的“代价”和“边界盲区”
过度隔离会吃掉集群的可管理性
隔离不是越严越好,多租户集群里每一个NetworkPolicy、每一个ResourceQuota都会加重调度器的计算量,也会让DevOps排障变得更困难,生产环境中比较常见的情况是:租户A说网络有问题,你排查半天,发现是策略放行列表里漏了一条DNS请求到CoreDNS的Egress规则,导致域名解析失败,这种问题在单租户集群几乎不会出现。
资源配额的“垂直边坑”
ResourceQuota是作用于Namespace的,但Node级资源争抢不在Kubernetes配额的控制范围内,如果两个租户的Pod被调度到了同一台Node上,即使它们各自的Namespace配额都没超,当Node的磁盘IO、CPU缓存、内存带宽被吃满时,两边都会受损,行业比较通用的做法是给不同的高敏感租户分配独立的Node节点池,这样隔离边界就从API Server下沉到了物理机器。

建议定期“边界巡检”
可以考虑每个月做一次“攻防演练”租户A的人尝试访问租户B的监控Dashboard,或者用集群里的特权和ServiceAccount试图列出其他命名空间的Secret。集群隔离的边界不是写完配置就固化的,它存在于每一次镜像更新、每一次RBAC策略修改之后。 尤其要留意的是那些明明没用到的NodePort服务和允许访问的ClusterRole,它们往往是边界上最容易被捅破的洞。
常见问题与排障要点
Kubernetes里面的多租户隔离和虚拟机多租户隔离有什么区别
虚拟机的隔离边界在Hypervisor层,管理面独立、内核独立,安全边界相对清晰,Kubernetes的隔离则依赖API Server里的策略配置与内核cgroup的配合,没有Hypervisor这一层“物理铁幕”,如果你比较看重强隔离属性,虚拟机的容器运行时(如Kata Containers)会更贴近传统虚拟机方式,但开销会高不少。
怎么判断我的集群隔离配置是不是合格
给你一个操作思路:尝试用租户A的ServiceAccount去执行kubectl get pods -n ns-b,如果看到的是“Forbidden”,说明RBAC这层是合格的,然后从租户A的Pod里执行curl http://{ns-b-service-ip}:端口,如果连接超时或拒绝,说明网络策略是生效的,ResourceQuota的验证更直接:用租户A的账户创建超出配额的Deployment,看它是否会因超出配额被API Server拒绝。
多租户集群里面资源隔离的边界怎么理解
边界是一个动态范围,它由两部分组成:一部分是控制面策略(谁能操作什么资源),另一部分是数据面机制(流量怎么走、内存怎么分配、磁盘IO怎么限流),这两条边界线不一定重合有的集群控制面管得很严,但数据面的网络处于“裸奔”状态,当你评估一个多租户集群的隔离边界时,建议沿着“认证 → 授权 → 配额 → 调度 → 网络 → 存储”这条链路逐层检查,任何一层的缺失,都等于整个隔离方案出现了破口。