说到Kubernetes(以下简称K8s)的多租户环境,很多刚入坑的同学都会觉得头疼。想象一下,你是一家中型科技公司的运维负责人,公司里有三个部门:AI研发部、数据部和前端测试组。他们共享同一个K8s集群,但需求各不相同。AI组跑的大模型训练任务动不动就要几十个GPU,数据组需要海量的存储IO,而前端组只是跑些轻量的Web服务。如果没有合理的资源隔离和权限控制,后果不堪设想——AI组的任务一启动,把整个集群的资源吃干抹净,数据组的数据库查询直接卡死,前端组连个测试环境都起不来,更可怕的是,有人手抖删了别组的敏感数据,那真是神仙也难救。
今天咱们不聊那些枯燥的理论,我就以一个过来人的身份,跟你聊聊在实际生产环境中,是怎么通过Namespace配额(ResourceQuota)和RBAC权限控制,把这三个部门管得服服帖帖的。我会结合具体的代码配置和真实案例,让你不仅知道“怎么做”,还明白“为什么要这么做”。毕竟,K8s玩的就是精细化的资源管理和安全边界,咱们得把每一分算力都花在刀刃上,同时保护好公司的核心数据资产。
为什么要搞多租户?资源抢占的“血泪史”
在深入技术细节之前,咱们先得达成共识:为什么多租户这么重要?我见过太多小团队,一开始觉得“咱们就几个人,随便搞搞”,结果随着业务扩张,集群越来越乱,最后不得不推倒重来。
资源抢占是多租户环境中最常见的问题之一。简单来说,就是某个租户(比如一个部门或一个项目)因为没有约束,疯狂占用集群资源,导致其他租户无资源可用。这种情况在K8s中通常表现为:
- CPU和内存过载:某个Namespace下的Pod请求了大量资源,但实际使用很少,或者干脆就是失控的Job任务,把Node节点的CPU打满,其他Pod被迫限流甚至OOM(内存溢出)。
- 存储资源耗尽:某个数据组疯狂创建PVC(持久卷声明),占满了PV(持久卷)的存储空间,其他组的业务数据无法写入。
- 网络带宽竞争:某些高吞吐服务霸占了网络带宽,影响其他服务的网络延迟。
而数据泄露则是另一个维度的灾难。在多租户环境中,不同部门的数据敏感度不同。AI组可能处理用户隐私数据,数据组拥有核心业务模型,前端组只是做UI测试。如果权限配置不当,前端组的开发人员可能意外(或恶意)访问到AI组的数据库,或者看到敏感的配置Secret。
所以,Namespace配额和RBAC是K8s多租户安全的两大支柱。前者管“资源”,后者管“权限”。咱们一个一个来拆解。
Namespace:多租户的第一道防线
Namespace是K8s中用于隔离资源的逻辑分区。你可以把它想象成虚拟机中的“文件夹”,或者是物理集群中的“子集群”。每个Namespace下的资源(Pod、Service、PVC等)都是独立的,互不影响。
创建隔离的Namespace
首先,咱们得为每个部门创建独立的Namespace。假设我们要为AI研发部、数据部和前端测试组分别创建命名空间。
# ai-team-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ai-team
labels:
team: ai-research
environment: production
# data-team-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: data-team
labels:
team: data-engineering
environment: production
# frontend-team-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: frontend-team
labels:
team: frontend-testing
environment: staging
应用这些配置:
kubectl apply -f ai-team-namespace.yaml
kubectl apply -f data-team-namespace.yaml
kubectl apply -f frontend-team-namespace.yaml
检查一下创建的Namespace:
kubectl get namespaces | grep -E "ai-team|data-team|frontend-team"
输出应该类似这样:
NAME STATUS AGE
ai-team Active 5m
data-team Active 4m
frontend-team Active 3m
你看,每个Namespace都是独立的,资源不会互相干扰。但这还不够,咱们得给每个Namespace设置资源配额,防止某个组无限扩张。
ResourceQuota:给资源上“紧箍咒”
ResourceQuota是K8s中用于限制Namespace内资源总使用量的对象。它可以限制CPU、内存、存储、以及资源对象的数量(比如Pod数量、Service数量、ConfigMap数量等)。
为AI团队设置配额
AI团队跑的是重型计算任务,需要较多的GPU和内存,但也不能让他们把整个集群的资源都抢走。咱们给他们设定一个合理的配额。
# ai-team-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: ai-quota
namespace: ai-team
spec:
hard:
# CPU配额:总共24核,单Pod最大4核
requests.cpu: "24"
limits.cpu: "40"
# 内存配额:总共96Gi,单Pod最大16Gi
requests.memory: "96Gi"
limits.memory: "128Gi"
# GPU配额:假设集群有8张A100 GPU
nvidia.com/gpu: "4"
# Pod数量限制:最多20个Pod
pods: "20"
# PVC数量限制:最多10个
persistentvolumeclaims: "10"
# 服务数量限制:最多5个
services: "5"
应用这个配额:
kubectl apply -f ai-team-quota.yaml
检查一下配额是否生效:
kubectl describe quota ai-quota -n ai-team
输出会显示当前使用量和硬限制:
Name: ai-quota
Namespace: ai-team
Resource Used Hard
-------- ---- ----
cpu 8 24
memory 32Gi 96Gi
pods 5 20
...
为数据团队设置配额
数据团队需要大量的存储IO和内存来处理数据集,但CPU需求相对较低。
# data-team-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: data-quota
namespace: data-team
spec:
hard:
requests.cpu: "16"
limits.cpu: "24"
requests.memory: "64Gi"
limits.memory: "128Gi"
# 存储配额:限制PVC总存储容量为10Ti
requests.storage: "10Ti"
storageclass.storage.k8s.io/requests.storage: "10Ti"
pods: "30"
persistentvolumeclaims: "20"
应用并检查:
kubectl apply -f data-team-quota.yaml
kubectl describe quota data-quota -n data-team
为前端团队设置配额
前端团队只需要跑一些轻量的Web服务,资源需求很低。
# frontend-team-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: frontend-quota
namespace: frontend-team
spec:
hard:
requests.cpu: "4"
limits.cpu: "8"
requests.memory: "8Gi"
limits.memory: "16Gi"
pods: "10"
services: "5"
configmaps: "10"
secrets: "10"
应用并检查:
kubectl apply -f frontend-team-quota.yaml
kubectl describe quota frontend-quota -n frontend-team
限制对象数量的重要性
除了CPU、内存和存储,ResourceQuota还可以限制各种K8s对象的数量。这一点非常重要,因为如果一个团队创建了大量的Service或ConfigMap,即使资源使用量不高,也会增加控制平面的负担,甚至导致API Server性能下降。
举个例子,如果前端团队不小心创建了一个循环,不断生成新的Deployment和Service,ResourceQuota可以限制他们最多只能有10个Pod、5个Service,从而防止他们拖垮整个集群。
# 限制对象数量
hard:
pods: "10"
services: "5"
configmaps: "10"
secrets: "10"
replicationcontrollers: "5"
persistentvolumeclaims: "5"
这样,即使某个团队想要“刷”对象,也会很快达到上限,被系统拒绝。
LimitRange:细粒度的资源约束
ResourceQuota是Namespace级别的总量限制,而LimitRange则是Pod级别的单实例限制。两者结合使用,才能实现真正有效的资源控制。
设置LimitRange
假设AI团队的某个成员提交了一个Pod,请求100核CPU和1Ti内存,这显然是不合理的。咱们可以通过LimitRange来限制单个Pod的资源请求和限制范围。
# ai-team-limitrange.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: ai-limit-range
namespace: ai-team
spec:
limits:
# 默认限制类型:容器
- type: Container
# 默认请求值
defaultRequest:
cpu: "2"
memory: "4Gi"
# 最大限制值
max:
cpu: "8"
memory: "16Gi"
# 最小请求值
min:
cpu: "100m"
memory: "128Mi"
# Pod级别的限制
- type: Pod
max:
cpu: "16"
memory: "32Gi"
应用这个LimitRange:
kubectl apply -f ai-team-limitrange.yaml
现在,如果AI团队的成员创建Pod时没有指定资源请求和限制,K8s会自动应用默认值(2核CPU,4Gi内存)。如果他们试图创建超过最大限制的Pod,API Server会直接拒绝。
LimitRange和ResourceQuota的配合使用
ResourceQuota和LimitRange是互补的。ResourceQuota防止Namespace级别的资源耗尽,LimitRange防止单个Pod占用过多资源。两者结合,才能实现从宏观到微观的全面控制。
举个例子,AI团队的ResourceQuota限制总CPU为24核,而LimitRange限制单个Pod最大8核。这样,团队最多只能运行3个重型Pod(3 * 8 = 24),既保证了单个Pod的资源上限,又防止了整个Namespace的资源失控。
RBAC:权限控制的精细手术
如果说ResourceQuota是“量体裁衣”,那么RBAC(Role-Based Access Control,基于角色的访问控制)就是“指纹识别”。RBAC允许你为不同的用户、服务账号或组分配不同的权限,确保每个人只能做他们该做的事。
RBAC的核心概念
在深入代码之前,咱们先了解一下RBAC的四个核心概念:
- Role(角色):定义一组权限规则,比如“可以读写某个Namespace下的Pod”。
- RoleBinding(角色绑定):将Role绑定到用户、组或服务账号,赋予他们相应的权限。
- ClusterRole(集群角色):类似于Role,但作用范围是集群级别,比如“可以查看所有Namespace的Secret”。
- ClusterRoleBinding(集群角色绑定):将ClusterRole绑定到用户、组或服务账号,赋予他们集群级别的权限。
为AI团队创建角色和绑定
假设AI团队需要读写ai-team Namespace下的所有资源,但不能删除Namespace本身,也不能访问其他Namespace。
# ai-team-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: ai-team
name: ai-team-role
rules:
# 允许读写Pod
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许读写Deployment
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许读写Service
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许读写ConfigMap
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# 允许读写Secret(敏感操作,需谨慎)
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
应用这个Role:
kubectl apply -f ai-team-role.yaml
接下来,创建一个RoleBinding,将这个Role绑定到AI团队的成员。假设AI团队有一个K8s用户组ai-team-group。
# ai-team-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ai-team-rolebinding
namespace: ai-team
subjects:
- kind: Group
name: ai-team-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: ai-team-role
apiGroup: rbac.authorization.k8s.io
应用这个RoleBinding:
kubectl apply -f ai-team-rolebinding.yaml
现在,ai-team-group中的成员在ai-team Namespace下拥有指定的权限,但在其他Namespace下没有任何权限。
为数据团队创建只读角色
数据团队可能只需要查看资源,而不需要修改。咱们给他们创建一个只读角色。
# data-team-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: data-team
name: data-team-reader
rules:
# 只允许读取Pod
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
# 只允许读取Deployment
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
# 只允许读取Service
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
应用并绑定:
kubectl apply -f data-team-role.yaml
# data-team-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: data-team-rolebinding
namespace: data-team
subjects:
- kind: Group
name: data-team-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: data-team-reader
apiGroup: rbac.authorization.k8s.io
kubectl apply -f data-team-rolebinding.yaml
为运维团队创建集群级别的管理角色
运维团队需要管理整个集群,包括所有Namespace。这时需要使用ClusterRole和ClusterRoleBinding。
# ops-clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ops-cluster-admin
rules:
# 允许管理所有Namespace下的Pod
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许管理所有Namespace下的Deployment
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许管理所有Namespace下的Secret
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许管理所有Namespace下的ResourceQuota和LimitRange
- apiGroups: [""]
resources: ["resourcequotas", "limitranges"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 允许管理Node
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch", "update", "patch"]
应用这个ClusterRole:
kubectl apply -f ops-clusterrole.yaml
接下来,创建一个ClusterRoleBinding,将这个ClusterRole绑定到运维团队的用户组ops-group。
# ops-clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ops-cluster-admin-binding
subjects:
- kind: Group
name: ops-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: ops-cluster-admin
apiGroup: rbac.authorization.k8s.io
kubectl apply -f ops-clusterrolebinding.yaml
现在,ops-group中的成员对整个集群拥有管理权限,而AI团队和数据团队的成员只能在自己的Namespace内操作。
最小权限原则
在设置RBAC时,务必遵循“最小权限原则”(Principle of Least Privilege)。也就是说,只授予用户完成工作所必需的最低权限。不要随意授予cluster-admin角色,除非绝对必要。
举个例子,如果AI团队的成员只需要查看Pod日志,那么只需要授予get和list权限,而不需要create或delete权限。
”`yaml
ai-team-log-reader-role.yaml
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: ai-team name: ai-team-log-reader rules:
- apiGroups: [“”] resources: [“pods”, “pods/log