某企业Kubernetes集群因多租户配置失误导致数据泄露多团队共用一台集群如何避免资源争抢与权限越界命名空间隔离与RBAC权限配置全攻略
先说个真事。去年有个做电商的企业,大概两百多号人,分成了产品、研发、运维、数据四个大团队。他们为了省成本,把所有人的服务都跑在同一套K8s集群里,美其名曰”资源整合”。结果呢?有一天数据团队的小伙子想查一下用户订单数据,随手写了个kubectl get pods –all-namespaces,结果发现生产环境的数据库账号明文就写在配置中心里,顺藤摸瓜还看到了客户手机号。这事儿最后被监管罚了八十万,负责人直接被开除。
你看,多租户集群不是不能用,而是你用错了方式,代价真的很大。下面我把这套东西拆开来,一点一点讲清楚。
一、先搞清楚:什么叫”多租户”,什么叫”单租户”
这个词听着高大上,其实道理很简单。
想象你住在一栋公寓楼里。
单租户模式,就像你一个人包下一整栋楼。你想怎么装修就怎么装修,想几点睡觉就几点睡觉,不用担心邻居吵你,也不用担心你会不会误碰邻居家的电路。缺点是贵,一栋楼就你一个人住,浪费。
多租户模式,就像这栋楼住了几十户人家。每家每户有自己的门牌号、自己的钥匙,公共区域大家一起用。好处是省钱,坏处是——如果你家的锁没装好,或者物业没有管好公共区域,可能隔壁老王就会溜进你家翻东西。
Kubernetes里的多租户,就是第二种情况。多个团队、多个业务共享同一个集群,靠技术手段把大家隔离开,谁也不能随便越界。
很多企业一开始图省事,所有团队全塞一个集群,连namespace都没分好,这就是事故的根源。
二、资源争抢的问题:为什么多团队混用会打架
假设你们公司有两个团队:A团队在做核心支付系统,B团队在做内部报表工具。
如果两个团队都在同一个集群里,没有做任何资源限制:
- B团队的报表工具突发流量,一把吃掉集群里80%的CPU和内存
- A团队的支付系统因为拿不到资源,直接卡死,用户付不了钱
- 运维人员一看,服务器明明还有空闲,但A团队的服务就是起不来
这就是典型的资源争抢。
2.1 没有资源限制的后果
下面是一个没有设置资源请求和限制的Deployment示例,大家看看问题在哪儿:
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
spec:
containers:
- name: payment
image: company/payment:v2.1
# 注意:这里没有设置 resources!
# 这个容器理论上可以无限占用节点资源
ports:
- containerPort: 8080
这个配置看起来很正常,对吧?但问题在于,这个支付服务的容器没有任何资源上限。如果代码里有bug导致内存泄漏,这个容器会一路吃内存,直到把整个节点挤爆,同节点上的其他pod都会受影响。
2.2 正确的做法:设置资源请求和限制
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
spec:
containers:
- name: payment
image: company/payment:v2.1
resources:
requests:
cpu: "500m" # 请求500毫核,保证至少有这个资源
memory: "512Mi" # 请求512Mi内存
limits:
cpu: "2000m" # 最多用2个核
memory: "2Gi" # 最多用2G内存
ports:
- containerPort: 8080
resources.requests是调度器分配资源时的依据,requests是保底,limits是天花板。
- 调度器会找一个节点,这个节点剩余资源能满足requests,才把pod放上去
- 运行期间,pod不能超过limits,超过了会被kill掉重启
- 这样每个团队的服务都不会抢对方的资源
2.3 LimitRange:给namespace加一道保险
除了每个pod单独设limits,还可以在整个namespace层面设置默认值。这样即使有人忘了配resources,也不会出问题。
apiVersion: v1
kind: LimitRange
metadata:
name: payment-limit-range
namespace: production
spec:
limits:
# 单个容器默认资源
- type: Container
default:
cpu: "1000m"
memory: "1Gi"
defaultRequest:
cpu: "500m"
memory: "512Mi"
max:
cpu: "4000m"
memory: "4Gi"
min:
cpu: "100m"
memory: "128Mi"
# 单个pod资源上限
- type: Pod
max:
cpu: "8000m"
memory: "8Gi"
配置了LimitRange之后,任何人在production命名空间里创建pod,如果没写resources,就会自动套用上边的默认值。同时,任何容器都不能超过max设定的上限。
2.4 ResourceQuota:给整个团队设配额
如果说LimitRange是管单个pod的,那ResourceQuota就是管整个团队的。
apiVersion: v1
kind: ResourceQuota
metadata:
name: payment-team-quota
namespace: production
spec:
hard:
# 计算资源总量
requests.cpu: "32" # 整个namespace最多请求32个核
requests.memory: "64Gi" # 整个namespace最多请求64G内存
limits.cpu: "64" # 整个namespace最多限制64个核
limits.memory: "128Gi" # 整个namespace最多限制128G内存
# 资源对象数量限制
pods: "100" # 最多100个pod
services: "20" # 最多20个service
configmaps: "50" # 最多50个configmap
secrets: "50" # 最多50个secret
persistentvolumeclaims: "20" # 最多20个PVC
有了这个,即使B团队的某个人发了疯一样创建pod,也永远超不过这个配额。生产环境的资源是安全的。
三、命名空间隔离:给每个团队划一块”地”
命名空间是Kubernetes里最基础的隔离手段。它不是防火墙,不能阻止网络访问,但它提供了第一道边界——不同namespace下的资源默认是互相看不到的。
3.1 为什么要用命名空间
回到那个电商公司的例子。他们有四个团队,如果不用命名空间,所有人的资源都堆在一起,运维人员想查一个服务,得在整个集群里翻;安全人员想看谁访问了什么,得排查所有资源。
有了命名空间,事情就清晰了:
default # 默认命名空间,尽量别用
production # 生产环境,核心业务
staging # 预发环境,测试用
data-team # 数据团队专属
dev-team-a # A研发团队专属
dev-team-b # B研发团队专属
monitoring # 监控团队专属
ingress-nginx # 网关团队专属
每个团队只在自己的namespace里活动,互不干扰。
3.2 命名空间的创建规范
# 创建命名空间的规范写法,带上标签,方便管理
kubectl create namespace production \
--labels team=core,business=payment,env=production
kubectl create namespace data-team \
--labels team=data,business=analytics,env=production
kubectl create namespace dev-team-a \
--labels team=dev-a,business=frontend,env=staging
标签(labels)非常重要。后面做权限控制、资源配额、监控告警,全靠标签来区分。
3.3 禁止跨命名空间访问
很多事故不是因为没设权限,而是因为默认情况下,Kubernetes其实是允许一定跨namespace操作的。比如:
- 一个pod里可以挂载另一个namespace的Secret
- 一个service可以通过kube-dns访问其他namespace的service(格式是
<service>.<namespace>.svc.cluster.local) - 某些kubectl命令不加–namespace参数时,可能会打到错误的命名空间
要阻断这些隐式通道,需要配合NetworkPolicy和RBAC一起用,这部分后面细说。
四、RBAC权限配置:谁能干什么,权限最小化原则
RBAC(Role-Based Access Control,基于角色的访问控制)是多租户集群的核心。它控制着”谁能对哪些资源做什么操作”。
4.1 RBAC的四个基本概念
理解RBAC之前,先搞清楚四个名词:
- User(用户):真实的人或者外部系统,比如开发工程师小明
- Group(组):一类用户的集合,比如”前端开发组”
- ServiceAccount(服务账户):给pod里运行的程序用的,不是给人用的
- Role(角色):定义了一组权限,比如”可以看production命名空间里的所有资源”
- ClusterRole(集群角色):和Role类似,但作用范围是整个集群
- RoleBinding(角色绑定):把Role绑定到某个User/Group/ServiceAccount上,生效范围是某个命名空间
- ClusterRoleBinding(集群角色绑定):把ClusterRole绑定到某个User/Group/ServiceAccount上,生效范围是整个集群
简单说:Role定义”能做什么”,RoleBinding决定”谁可以做”。
4.2 一个真实的事故:权限过大导致的泄露
上面说的那个电商公司,事故原因之一就是权限过大。他们的运维人员用的是cluster-admin角色,这个角色的权限是”整个集群的上帝模式”——可以创建、删除、查看任何命名空间的任何资源,包括Secret。
结果某个运维人员的kubeconfig泄露了,黑客拿到之后直接sudo删掉了生产数据库。
4.3 最小权限原则:从cluster-admin到具体角色
下面我来设计几个实际可用的角色。
场景一:开发团队只能看自己namespace的资源
# 定义一个只读角色,作用在特定namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-reader
namespace: dev-team-a
rules:
- apiGroups: ["", "apps", "batch", "extensions"]
resources: ["pods", "deployments", "jobs", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
注意,这里的secrets也只给了read权限,没有delete和update。而且只限在dev-team-a这个namespace里生效。
# 把这个角色绑定到开发团队的所有人
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-team-a-readers
namespace: dev-team-a
subjects:
- kind: Group
name: dev-team-a
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-reader
apiGroup: rbac.authorization.k8s.io
这样,dev-team-a组的成员,只能看自己团队的资源,想去看production?没门。
场景二:运维团队可以管理生产环境,但不能删库
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ops-manager
namespace: production
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "deployments", "services", "configmaps", "secrets", "jobs", "cronjobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# 特别地,对PodLog和PodExec放开
- apiGroups: [""]
resources: ["pods/log", "pods/exec", "pods/attach"]
verbs: ["get", "create"]
# 明确禁止删除StatefulSet和PVC,防止误删数据库
- apiGroups: ["apps"]
resources: ["statefulsets", "persistentvolumeclaims"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# 注意:这里故意没有写delete
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ops-production-manage
namespace: production
subjects:
- kind: Group
name: ops-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: ops-manager
apiGroup: rbac.authorization.k8s.io
这个 Role 的设计思路是:运维可以增删改查大部分资源,但是StatefulSet和PVC没有delete权限,防止手滑删库。
场景三:数据团队只能访问自己的命名空间,而且不能访问Secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: data-analyst
namespace: data-team
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "deployments", "jobs", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# 明确排除secrets
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
# 数据团队可以通过CRD访问自己的分析任务
- apiGroups: ["batch.volcano.sh"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: data-team-analysts
namespace: data-team
subjects:
- kind: Group
name: data-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: data-analyst
apiGroup: rbac.authorization.k8s.io
4.4 ServiceAccount:给程序用的身份
上面的例子都是给人用的。但集群里还有很多是程序在跑,比如一个自动扩缩容的控制器,或者一个日志收集agent。它们也需要身份,但不能用人。
# 给支付服务创建一个专属的ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: payment-sa
namespace: production
labels:
app: payment-service
# 给这个ServiceAccount分配权限,只能看日志和重启pod
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: payment-auto-ops
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: payment-sa-binding
namespace: production
subjects:
- kind: ServiceAccount
name: payment-sa
namespace: production
roleRef:
kind: Role
name: payment-auto-ops
apiGroup: rbac.authorization.k8s.io
然后在Deployment里引用这个ServiceAccount:
spec:
serviceAccountName: payment-sa
containers:
- name: payment
image: company/payment:v2.1
这样,这个pod里的程序就只能做Role里定义的那些事,别的什么也干不了。
4.5 检查当前权限:你现在的角色到底是什么
很多人不知道自己有什么权限。可以用这个命令自查:
# 查看当前认证的身份
kubectl auth whoami
# 查看当前用户有哪些权限(模拟检查)
kubectl auth can-i create pods --namespace production
kubectl auth can-i get secrets --namespace production
kubectl auth can-i list pods --namespace staging
# 查看当前用户的完整角色绑定
kubectl describe rolebinding --all-namespaces | grep -A5 "dev-team"
kubectl describe clusterrolebinding --all-namespaces | grep -A5 "dev-team"
五、NetworkPolicy:隔离子命名空间之间的网络
前面说了命名空间隔离,但Kubernetes默认情况下,不同namespace的pod是可以互相通信的。也就是说,即使B团队的人没有权限kubectl到production命名空间,B团队的一个pod仍然可以通过ClusterIP访问production里的数据库服务。
这就是”权限隔离了,网络没隔离”的典型漏洞。
5.1 默认拒绝所有流量,然后白名单放行
# 在production命名空间创建默认拒绝策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
这个策略的意思是:production命名空间里,所有pod默认拒绝所有入站和出站流量。
5.2 然后白名单放行必要的流量
# 允许ingress-nginx的pod访问production里的服务
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-to-production
namespace: production
spec:
podSelector:
matchLabels:
app: payment-service
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
- podSelector:
matchLabels:
app: nginx-ingress
ports:
- protocol: TCP
port: 8080
---
# 允许production里的pod访问外部的数据库
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-production-to-external-db
namespace: production
spec:
podSelector:
matchLabels:
app: payment-service
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 3306
这样,即使有人从其他namespace的pod发起网络请求,也进不来production。
5.3 禁止跨namespace的DNS解析
前面提到过,Kubernetes的CoreDNS默认允许跨namespace解析。如果不想让其他namespace的人通过DNS发现你的服务:
# 在data-team命名空间设置策略,禁止访问production
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-dns-to-production
namespace: data-team
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: production
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
实际上,更稳妥的做法是直接禁止所有非本namespace的DNS查询,只在需要的时候白名单放行。
六、Secret和ConfigMap的安全管理
数据泄露的元凶,很多时候不是代码漏洞,而是明文存储的账号密码。
6.1 Secret不要明文写在yaml里
很多团队的习惯是这样:
# 错误示范!绝对不要这样写
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
username: YWRtaW4= # 这是base64,不是加密!
password: MTIzNDU2
base64解码一下就知道密码了,这不是安全,这是自欺欺人。
正确的做法是开启etcd的加密功能,或者使用外部密钥管理系统:
# 使用Kubernetes自带的加密provider(AES)
# 在apiserver的配置里启用
encryption-config:
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <你的加密密钥,至少32字节>
- identity: {}
更好的方案是用Sealed Secrets或者External Secrets Operator,把密钥存在Vault或者AWS Secrets Manager里,Kubernetes集群只持有解密后的值,而且不解密就看不到明文。
6.2 限制Secret的访问权限
结合前面说的RBAC,确保只有需要访问Secret的团队才能看到:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader-production
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ops-secret-reader
namespace: production
subjects:
- kind: Group
name: ops-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: secret-reader-production
apiGroup: rbac.authorization.k8s.io
其他团队,比如dev-team-a,根本没有production命名空间的RoleBinding,自然看不到Secret。
6.3 审计:谁看过你的Secret
即使做了权限控制,也建议开启审计日志,记录谁在什么时候访问了什么:
# 查看审计日志中关于Secret的访问
kubectl audit log --namespace production --resource secrets --since 24h
企业级的话,可以把审计日志接入ELK或者SIEM系统,设置告警规则:一旦有非ops团队的人访问production的Secret,立刻报警。
七、实际落地:从一个混乱的集群到规范的多租户集群
下面是一套完整的落地方案,按照这个顺序来做,基本不会出大问题。
7.1 第一步:划分命名空间
# 生产环境
kubectl create namespace production \
--labels env=production,team=core
# 预发环境
kubectl create namespace staging \
--labels env=staging,team=core
# 各团队专属namespace
kubectl create namespace dev-team-a \
--labels env=staging,team=dev-a
kubectl create namespace dev-team-b \
--labels env=staging,team=dev-b
kubectl create namespace data-team \
--labels env=production,team=data
kubectl create namespace monitoring \
--labels env=production,team=ops
kubectl create namespace ingress-nginx \
--labels env=production,team=infra
7.2 第二步:为每个namespace设置LimitRange和ResourceQuota
# 以production为例
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
name: production-lr
namespace: production
spec:
limits:
- type: Container
default: {cpu: "1000m", memory: "2Gi"}
defaultRequest: {cpu: "500m", memory: "1Gi"}
max: {cpu: "8000m", memory: "8Gi"}
min: {cpu: "100m", memory: "128Mi"}
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-rq
namespace: production
spec:
hard:
requests.cpu: "64"
requests.memory: "128Gi"
limits.cpu: "128"
limits.memory: "256Gi"
pods: "200"
services: "50"
secrets: "100"
configmaps: "100"
EOF
其他namespace同理,根据团队规模调整配额。
7.3 第三步:创建各团队的角色和绑定
每个团队至少需要两个角色:
- 开发者角色:可以部署应用、查看日志、重启pod,但不能看Secret、不能删StatefulSet
- 运维角色:可以管理所有资源,但不能删etcd、不能修改集群级别的配置
# 开发者的角色模板
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: dev-team-a
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "deployments", "services", "configmaps", "jobs", "cronjobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# 明确不给secrets权限
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-team-a-developers
namespace: dev-team-a
subjects:
- kind: Group
name: dev-team-a
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
7.4 第四步:配置NetworkPolicy
# 为每个namespace创建默认deny-all策略
for ns in production staging dev-team-a dev-team-b data-team; do
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: $ns
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
done
然后为每个namespace配置白名单规则,只放行必要的流量。
7.5 第五步:启用审计和监控
# 确保kube-apiserver开启了审计日志
# 审计策略文件示例
cat > audit-policy.yaml <<EOF
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# 记录所有Secret的访问
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
# 记录所有delete操作
- level: Request
verbs: ["delete"]
# 记录所有bind和escalate操作(权限提升)
- level: Metadata
verbs: ["bind", "escalate"]
EOF
八、常见坑和避坑指南
坑一:忘记给新创建的namespace配RBAC
新创一个namespace,如果忘了配Role和RoleBinding,这个namespace里的一切对所有人都可见。每次创建namespace之后,第一件事就是配权限。
坑二:用了cluster-admin但是没有区分团队
cluster-admin是集群的最高权限,一个cluster-admin kubeconfig等于拿到了整个集群的钥匙。应该尽量避免给人分配cluster-admin,而是用RoleBinding做精细控制。运维如果需要紧急权限,可以用RBAC临时提升,用完就撤。
坑三:NetworkPolicy没部署
有些团队只做了RBAC,没做NetworkPolicy,结果权限隔离了,但网络还能跨namespace访问。两个都要做,缺一不可。
坑四:默认ServiceAccount被滥用
Kubernetes每个namespace都有一个默认的ServiceAccount,任何没指定serviceAccountName的pod都会用它。这个默认SA默认有比较多的权限,很多事故的源头就是这个。
# 检查哪些pod用了默认ServiceAccount
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}' | grep default
# 建议在每个namespace里禁用自动挂载default SA
# 在kubelet配置中设置
automountServiceAccountToken: false
坑五:ConfigMap里混入了敏感信息
ConfigMap不是加密的,明文存储。不要把数据库密码、API Key放在ConfigMap里,一律用Secret。
# 错误:密码放在ConfigMap里
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
DATABASE_PASSWORD: "mypassword123" # 明文!
APP_PORT: "8080"
# 正确:密码放在Secret里
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
DATABASE_PASSWORD: bXlwYXNzd29yZDEyMw==
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
APP_PORT: "8080"
# 密码从这里引用Secret,而不是直接写
九、总结:多租户集群的核心原则
多租户集群不是不能用,而是不能用错了。核心原则就三条:
- 资源隔离:用命名空间+LimitRange+ResourceQuota,让每个团队有自己的资源边界,不会抢也不会超
- 权限隔离:用RBAC做精细的权限控制,每个团队只能做自己的事,最小权限原则,没有例外
- 网络隔离:用NetworkPolicy阻断跨namespace的网络访问,防止权限隔离了但网络没隔离的漏洞
这三个层次,缺一不可。只做了RBAC没做NetworkPolicy,别人可以从网络层面绕过权限;只做了命名空间没做ResourceQuota,一个团队可以拖垮整个集群。
那个电商公司的事故,根源就是三者都没做好。如果他们当时有命名空间隔离、有RBAC权限控制、有NetworkPolicy,数据泄露的根本不可能发生。
记住一句话:在Kubernetes里做多租户,隔离不是靠自觉,是靠配置。配置不做到位,事故就在路上。