接私有化部署这种活儿多了之后,你会发现一个特别现实的规律:单机部署能跑通,只是万里长征第一步。真正让运维头疼的,往往是“跑起来之后怎么管”。尤其是企业内部把 Sealos 私有化部署好之后,紧接着就会面临一个绕不开的问题——多个部门、多支研发团队都要用这套平台,怎么保证他们互不干扰?我这里的“互不干扰”可不是说各自建个文件夹那么简单,底层涉及资源配额、权限边界、网络隔离、存储隔离一整套东西。这篇文章就把我在实际项目中做 Sealos 多租户隔离的完整思路和操作步骤拆开讲清楚,从原理到落地,从踩坑到排查,一次说透。
先说适用人群:负责 Sealos 私有化部署的运维工程师、平台架构师,以及需要在团队内推广 Kubernetes 平台但被“多团队共用”问题卡住的技术负责人。就算你之前只是把 Sealos 当单机版 PaaS 在用,读完也能立刻把隔离体系撑起来。
1. Sealos 多租户隔离的整体设计思路
1.1 为什么多租户隔离是企业私有化部署的刚需
很多人都把“多租户”理解成“多建几个账号”,这个认知偏差在实践中会付出很惨痛的代价。Sealos 本身是构建在 Kubernetes 之上的云操作系统,它的租户本质上对应着一组 Kubernetes 原生的资源集合。如果只是给不同部门创建了登录账号,却没有在调度、配额、网络、存储这些层面做隔离,那后果就是:某个团队跑了个内存密集型任务,直接把节点内存打满,其他团队的服务跟着一起卡死;或者一个部门误删了 Namespace,另一个部门的数据跟着遭殃;再极端一点,A 团队通过集群内部网络直接访问到了 B 团队的数据库服务。
我在一次企业交付中遇到过真实案例:某公司把 Sealos 部署到三台物理机上,研发部、测试部、数据组共用一个平台。最开始运维图省事,所有业务全部堆在 default 命名空间里,资源不设限。结果数据组凌晨跑定时任务,内存申请直接占满所有可分配额度,第二天早上研发部的 Web 服务全部 OOM 重启。从那以后,这家公司的运维才真正意识到:多租户隔离不是“要不要做”的问题,而是“怎么做才规范”的问题。
从技术本质上看,Sealos 的多租户隔离需要覆盖四个层面:基础设施隔离、资源配额隔离、权限访问隔离、网络与存储隔离。这四个层面缺一不可,只做其中一两个,后面迟早会在线上出事故。
1.2 基于 Namespace 的租户模型选型
Sealos 底层是 Kubernetes,所以它的租户模型最自然的落点就是 Namespace。选 Namespace 而不是选独立集群,核心原因有两个:一是成本,独立集群意味着每个租户都要一套控制平面,硬件和管理成本直接翻倍;二是 Sealos 自身的应用管理、监控、日志模块都是围绕集群维度设计的,拆成多个集群后这些能力就很难统一收口。
实际操作中,我推荐一套“一个租户一个 Namespace + 一组专属 ServiceAccount + 一套资源配额”的组合拳。租户的边界用 Namespace 物理框定,租户内部的操作权限通过 RBAC 绑定到具体的用户或用户组,资源消耗上限用 ResourceQuota 和 LimitRange 卡死。这套模型的好处在于:它完全基于 Kubernetes 原生机制,不依赖 Sealos 的私有 API,后面升级版本、迁移集群都不会被绑死。
如果你需要更硬隔离,比如某个租户对安全要求极高、不允许和其他租户共享节点,那可以在 Namespace 上打污点和容忍度标签,把特定节点划给特定租户。但这属于进阶玩法,多数企业用不到,而且会牺牲调度弹性,节点利用率会明显下降。
1.3 四层隔离模型:从控制面到数据面
四层隔离模型是我在做多租户方案时一直沿用的框架,这里拆开说明:
| 隔离层 | 核心机制 | 解决什么问题 | 失效后果 |
|---|---|---|---|
| 基础设施层 | Namespace、节点池、污点容忍度 | 租户之间的资源边界不清晰 | 一个租户影响全局稳定性 |
| 资源配额层 | ResourceQuota、LimitRange、HPA | 租户无限抢占 CPU、内存、存储 | 资源争抢、服务崩溃 |
| 权限访问层 | RBAC、ServiceAccount、Sealos 用户体系 | 租户越权访问他人资源 | 数据泄露、误操作 |
| 网络与存储层 | NetworkPolicy、StorageClass、PV 隔离 | 租户间网络互通、数据越界 | 数据库被外部租户直连、数据串扰 |
这四个层面不是割裂的,而是层层递进。基础设施层决定“你能用哪块地盘”,资源配额层决定“你能用多少”,权限层决定“你能碰什么”,网络存储层决定“你能连通到哪里”。做隔离方案时,如果只盯着其中一层,大概率会出现短板效应。
2. 核心细节解析与实操要点
2.1 Namespace 级别的资源配额怎么配才科学
很多人在配 ResourceQuota 时最容易犯的错,是只限制 CPU 和内存,忽略了 PVC 数量和存储容量。结果就是,租户虽然 CPU 内存受限,但可以疯狂创建 PV,把底层存储池直接打爆。我经手的一个项目里,某租户在测试环境一次性创建了上百个 PVC,每个 PVC 默认 10Gi,存储节点磁盘直接告警。所以配额一定要把存储维度纳入进来。
下面是我在 Sealos 私有化环境里常用的一套 ResourceQuota 配置模板,可以直接参考:
apiVersion: v1 kind: ResourceQuota metadata: name: quota-team-a namespace: ns-team-a spec: hard: requests.cpu: "20" requests.memory: 40Gi limits.cpu: "40" limits.memory: 80Gi persistentvolumeclaims: "30" requests.storage: 500Gi count/services: "50" count/secrets: "100" count/configmaps: "100"这套配置的考量逻辑是:requests 是调度依据,limits 是运行上限,两者之间留出两倍余量,避免租户把请求值设得过高导致节点资源被“预定”但实际用不满。PVC 数量和存储容量双重限制是必须的,防止存储资源被无限消耗。再叠加服务、密钥、配置映射的数量限制,主要是防患某些奇葩业务在命名空间里创建海量小对象,给 API Server 造成压力。
你还需要配套一个 LimitRange,因为只设 ResourceQuota 不设 LimitRange,会有个漏洞:租户可以创建不声明资源限制的 Pod,这类 Pod 在调度时不受 quota 约束,等运行起来又可能无限占用资源。LimitRange 的作用就是给命名空间内每个 Pod 设置默认的 requests 和 limits,并把单个容器资源兜底值框死。
apiVersion: v1 kind: LimitRange metadata: name: limit-range-team-a namespace: ns-team-a spec: limits: - default: cpu: 500m memory: 512Mi defaultRequest: cpu: 100m memory: 128Mi type: Container这个配置里,default 是 Pod 未显式声明资源时的兜底值,defaultRequest 是调度器用于计算节点资源的请求值。如果租户内某个工作负载需要更大资源,就必须显式声明,这就把“隐式超大资源申请”这条路堵死了。
2.2 RBAC 权限模型:让每个租户只能看到自己的天空
Sealos 自带用户体系,但在底层还是要映射到 Kubernetes RBAC。我建议的权限模型是:每个租户创建一个独立 Namespace,同时创建一个对应的专属 ServiceAccount,再通过 RoleBinding 把该 ServiceAccount 绑定到 Namespace 内的 admin 角色。这样一来,租户管理员在 Sealos 界面上操作时,实际上是带着这个身份在 Kubernetes API 层面被识别。
创建租户管理员权限的完整流程分三步。第一步,创建服务账号:
kubectl create serviceaccount sa-team-a -n ns-team-a第二步,在命名空间内创建角色绑定,这里直接复用 Kubernetes 内置的 admin 角色即可,不需要自定义太多花样,因为 admin 角色已经覆盖了命名空间内绝大多数管理权限:
kubectl create rolebinding rb-team-a-admin \ --serviceaccount=ns-team-a:sa-team-a \ --clusterrole=admin \ -n ns-team-a第三步,如果需要让租户成员只能操作工作负载但不能删除 PV 等敏感资源,可以额外创建自定义 Role。我通常的做法是允许租户管理 Deployment、Service、ConfigMap、Secret,但不放行对 PVC 和 NetworkPolicy 的删除权限:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: role-team-a-limited namespace: ns-team-a rules: - apiGroups: ["apps"] resources: ["deployments", "statefulsets", "daemonsets"] verbs: ["get", "list", "watch", "create", "update", "patch"] - apiGroups: [""] resources: ["services", "configmaps", "secrets"] verbs: ["get", "list", "watch", "create", "update", "patch"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch"]这里有个坑要特别强调:ClusterRole 和 Role 的作用范围完全不同,给租户授权时务必使用 Role + RoleBinding,千万别用 ClusterRoleBinding。否则这个租户的权限会扩散到整个集群,其他租户的 Namespace 对他来说就变成裸奔了。我在生产环境见过不止一次因为图省事用了 ClusterRoleBinding 导致租户之间权限串味的事故。
2.3 Sealos 用户体系与 Kubernetes 账号的映射关系
Sealos 在私有化部署中通常有自己的账号认证模块,用户的登录态由 Sealos 管理。这时候需要理解一个关键点:Sealos 应用层用户在操作底层资源时,实际上是通过某个 kubeconfig 身份完成的。在部署 Sealos 时,集群管理员一般会配置一个高权限 kubeconfig 用于 Sealos 内部组件通信;但租户业务不应该沿用这份高权限配置,而是走“Sealos 用户绑定 RBAC 身份”的机制。
具体落地时,我会为每个租户创建独立的 kubeconfig 文件,里面使用该租户专属的 ServiceAccount token。这样租户通过 kubectl 直连集群也不会有越权风险。Sealos 界面上的操作默认走平台自身的 API Server 转发,如果租户在界面上只能看到自己的 Namespace,说明 Sealos 的租户权限配置已经生效;如果能看到别的租户资源,那就需要检查 Sealos 内部使用的高权限 kubeconfig 是否被错误地暴露给了前端服务。
小程序级别的客户往往忽略这一点,他们直接让 Sealos 用默认的 cluster-admin 权限做所有转发,租户隔离等于形同虚设。虽然界面看起来绕过了底层 RBAC,但一旦租户拿到任何可以直连 kube-apiserver 的入口,整个集群对他都不设防。正确的做法是为 Sealos 的租户模块配置一个具备“创建 Namespace 且只具备该 Namespace 管理权”的动态授权模型,而不是一个固定的高权限账号。
3. 实操过程与核心环节实现
3.1 租户创建自动化脚本:从手动到一键
当租户数量少时,手工创建 Namespace 和配额还能接受;一旦租户超过十个,手工操作就是灾难。我建议写成脚本,把上面的所有步骤一次性串联起来。这里给出一份基于 Bash 和 kubectl 的自动化脚本框架,配合变量批量创建租户资源。
#!/bin/bash # 一键创建租户隔离资源脚本 TENANT_NAME=$1 if [ -z "$TENANT_NAME" ]; then echo "用法: $0 <租户名>" exit 1 fi NS_NAME="ns-$TENANT_NAME" SA_NAME="sa-$TENANT_NAME" RB_NAME="rb-$TENANT_NAME-admin" # 1. 创建命名空间 kubectl create namespace $NS_NAME # 2. 创建资源配额(使用配置文件,便于后期变更) cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ResourceQuota metadata: name: quota-$TENANT_NAME namespace: $NS_NAME spec: hard: requests.cpu: "20" requests.memory: 40Gi limits.cpu: "40" limits.memory: 80Gi persistentvolumeclaims: "30" requests.storage: 500Gi EOF # 3. 创建 LimitRange cat <<EOF | kubectl apply -f - apiVersion: v1 kind: LimitRange metadata: name: limit-range-$TENANT_NAME namespace: $NS_NAME spec: limits: - default: cpu: 500m memory: 512Mi defaultRequest: cpu: 100m memory: 128Mi type: Container EOF # 4. 创建专属服务账号 kubectl create serviceaccount $SA_NAME -n $NS_NAME # 5. 绑定租户管理员角色 kubectl create rolebinding $RB_NAME \ --serviceaccount=$NS_NAME:$SA_NAME \ --clusterrole=admin \ -n $NS_NAME # 6. 生成租户 kubeconfig TOKEN=$(kubectl create token $SA_NAME -n $NS_NAME --duration=720h) kubectl config set-credentials ${TENANT_NAME}-sa \ --token=$TOKEN kubectl config set-context ${TENANT_NAME}-ctx \ --cluster=$(kubectl config current-context | sed 's/.*@//') \ --user=${TENANT_NAME}-sa \ --namespace=$NS_NAME echo "租户 $TENANT_NAME 隔离资源创建完成"这里有个细节说明一下:kubectl create token生成的 token 默认有时效,我这里设了 720 小时,也就是 30 天过期。如果租户需要长期访问,建议改用 Kubernetes 的 Secret 自动续期方式,或者在 Sealos 层面做统一的身份认证入口,租户不直接接触 cluster API。token 过期本身不是问题,问题是过期后租户业务无缘无故中断,所以生成 token 时一定要把续期机制同步告知运维同学。
3.2 网络隔离:NetworkPolicy 配置实操
有了资源配额和 RBAC 之后,很多团队会觉得隔离已经做完了,但网络层面的隔离往往被忽略。在默认情况下,Kubernetes 集群内的 Pod 网络是互通的,这意味着租户 A 的 Pod 可以直接通过 IP 访问租户 B 的 Pod,这在多租户环境下是绝不能接受的。
前提是集群必须使用支持 NetworkPolicy 的网络插件,比如 Calico、Cilium。如果你的 Sealos 部署环境网络插件是 Flannel,那 NetworkPolicy 默认是无效的,这一点必须提前确认。我遇到过不止一个现场,网络插件用的是 Flannel,配了 NetworkPolicy 却不生效,排查半天才发现是网络插件不支持。
下面是一个比较稳妥的默认拒绝策略,放在每个租户命名空间里,只允许同命名空间内的流量以及来自集群监控组件的流量通过:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress namespace: ns-team-a spec: podSelector: {} policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: ns-team-a egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: ns-team-a - ports: - port: 53 protocol: UDP - port: 53 protocol: TCP这个策略的核心逻辑是:Ingress 只允许来源命名空间等于本租户命名空间的流量进入;Egress 只允许访问同命名空间的 Pod,并额外放行 DNS 解析端口。为什么要专门放行 53 端口?因为很多服务器环境里的 DNS 解析依赖集群 DNS 服务,而集群 DNS(通常是 kube-system 命名空间)不在本租户命名空间内,如果不放行 53 端口,Pod 内部的域名解析会全部失败,业务起不来。这种细节是测试环境和真实生产环境最大的区别,网上很多 NetworkPolicy 模板都没有考虑 DNS 放行问题,直接套用会翻车。
如果你需要允许租户访问某些公共基础设施,比如监控服务、日志服务,可以额外放行特定的 Namespace 选择器或者 IP 段。但原则是“默认拒绝,显式放行”,绝对不要在租户命名空间里使用宽松的 allow-all 策略。
3.3 存储隔离:StorageClass 与 PVC 的权限控制
存储隔离是很多人最后才意识到的一层,但往往是事故最严重的一层。多租户场景下,存储隔离的核心目标是:租户 A 不能读写租户 B 的 PV 数据。要做到这一点,需要从两个维度入手:存储类隔离和回收策略。
存储类隔离方面,我建议为不同租户创建独立的 StorageClass,底层指向不同的存储池或独立的子目录。以常见 NFS 存储为例,可以针对每个租户挂载不同的 NFS 服务器或不同目录:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: sc-team-a provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: path: /data/nfs/team-a server: 192.168.1.100 archiveOnDelete: "false" reclaimPolicy: RetainStorageClass 中把archiveOnDelete设为 false,并且在回收策略上用 Retain 而不是 Delete,是为了防止租户在删除 PVC 时把 PV 里的数据一并物理删除。这个细节在多租户环境中非常重要:租户删除应用时,如果数据被自动清理,一旦需要回溯或恢复就麻烦了;而 Retain 策略虽然会留下一些待回收的 PV,但至少数据安全可控。你可以定期通过 CronJob 检查旧的 PV 状态,确认没有租户绑定后再手动清理。
此外,光配 StorageClass 还不够,还要通过配额限制 PVC 数量和存储容量,这一点前面已经提到。我再次强调:ResourceQuota 里的persistentvolumeclaims和requests.storage是存储隔离的第一道闸门,没有这道闸门,存储池再大也会被打爆。
4. 常见问题与排查技巧实录
4.1 租户 Pod 一直 Pending 怎么办
租户建好之后,遇到最多的问题就是 Pod 一直 Pending,Events 里报错五花八门。我总结下来主要有三种典型场景。
第一种,ResourceQuota 限制导致无法创建。报错信息里会明确出现exceeded quota字样。排查思路是查看该命名空间的资源使用情况,优先确认是 CPU 内存配额不足还是 PVC 数量超限:
kubectl get resourcequota -n ns-team-a -o yaml kubectl describe resourcequota quota-team-a -n ns-team-a如果确实是配额不足,就需要与租户管理员确认是调高配额还是缩减工作负载。很多团队喜欢直接把配额拉得特别大,这等于隔离失效,我的原则是配额只能逐步调增,必须有审批记录。
第二种,节点资源不足导致无法调度。报错信息多半是0/3 nodes are available: insufficient cpu, insufficient memory。这种情况跟配额无关,是节点本身没资源了。排查办法是查看节点资源水位,确定是整体扩容还是提前预留资源:
kubectl describe nodes | grep -A 5 "Allocated resources"如果某租户长期占满节点资源,而其他租户没有资源可用,说明第一层隔离(基础设施层)还需要加强。这时候就该考虑 taint 和 toleration 方案,把特定租户的 Pod 调度到指定节点池。
第三种,镜像拉取失败导致无法调度。这种往往被误以为是隔离问题,实际是私有镜像仓库认证没配置或节点无法访问镜像仓库。排查时先看 Pod 的 Events,通常会有Failed to pull image的明确提示。
4.2 租户之间网络不通但又需要连通怎么办
默认拒绝策略生效后,租户之间完全隔离,但业务上偶尔会出现合理的跨租户访问需求,比如基础数据服务被多个租户共享。这时候不要直接修改 NetworkPolicy 到全局放行,而是用更精确的解法:给共享服务打特定标签,然后在租户的 NetworkPolicy 里通过podSelector精确放行。
具体做法是,在共享服务的 Deployment 上增加标签,例如shared:>ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: ns-team-a ports: - port: 9092 protocol: TCP
这样既满足业务需求,又不会把隔离墙整体拆除。实际运维中,跨租户访问的申请一定要走审批机制,记录在案,而不是在 NetworkPolicy 上随手加规则,否则隔离边界会逐渐模糊,最终形同虚设。
4.3 kubectl 操作提示 forbidden 的排查思路
租户反馈 kubectl 操作资源提示 forbidden,是最常见的权限问题。排查分三步:第一步,确认 kubeconfig 里的 context 是否指向正确的命名空间;第二步,确认当前 ServiceAccount 是否存在于目标命名空间;第三步,确认 RoleBinding 是否绑定正确。
我遇到过的比较隐蔽的问题是:给租户创建 ServiceAccount 时,忘记把该 ServiceAccount 的namespace指定为租户专属命名空间,导致它默认创建在某个公共命名空间里。这个看似无害的失误造成的后果是,租户的权限认证正常,但操作目标资源时,RBAC 检查无法在正确命名空间内找到对应身份,所有请求都会被拒绝。排查时可以这样快速验证:
kubectl auth can-i list pods -n ns-team-a --as=system:serviceaccount:ns-team-a:sa-team-a这个命令会直接返回 yes 或 no,可以快速判断是 RBAC 配置问题还是 kubeconfig 使用问题。
4.4 多租户隔离踩坑速查表
| 症状 | 根因 | 解决方案 |
|---|---|---|
| Pod 长时间 Pending | ResourceQuota 超限或节点资源不足 | 检查 resourcequota 和节点已分配资源 |
| 跨命名空间服务访问被拒 | NetworkPolicy 未放行或网络插件不支持 | 确认网络插件能力,按需精确放行 |
| 租户能看其他命名空间资源 | 误用 ClusterRoleBinding | 改用 Role + RoleBinding |
| PVC 无法动态创建 | StorageClass 不存在或配额超限 | 确认 StorageClass 与 PVC 配额 |
| 应用域名解析超时 | NetworkPolicy 未放行 DNS 53 端口 | 在 Egress 中追加 53 端口放行 |
| 删除 PVC 后数据彻底消失 | reclaimPolicy 为 Delete | 改用 Retain 策略并定期回收 |
这张表是我多轮排障之后沉淀下来的,建议直接贴在运维文档里。很多时候返工不是因为方案不对,而是栽在这些看起来不起眼的细节上。
5. 经验总结与后续扩展方向
5.1 从“能用”到“好用”的隔离运维建议
说实话,把多租户隔离的资源都配好,只是做到了“能用”。要让平台在几十个租户并发使用下依然“好用”,至少还要补两件事:一是监控告警,二是自动化治理。
监控方面,要把资源配额使用率、PV 数量、网络策略命中情况全部纳入可视化平台。我的习惯是针对每个租户命名空间配置独立的告警规则:配额使用率超过 80% 就提前告警,而不是等配额耗尽导致业务中断才去处理。推荐组合是 Prometheus + Alertmanager,Sealos 本身对这套监控体系支持得很好。
自动化治理方面,上面给到的租户创建脚本可以进一步扩展成平台化的自助申请入口。租户管理员通过内部工单系统申请资源,审批通过后自动触发脚本创建隔离资源,整个过程不经过人工敲命令。这样既提高了效率,也留下审计记录,后续排查问题时有据可查。
5.2 后续可扩展:节点级硬隔离与数据面隔离
如果你们团队对隔离等级要求更高,比如金融、政务这类对数据安全极度敏感的行业,Namespace 级别的软隔离可能不够。这时候可以考虑节点级硬隔离:为高安全等级的租户规划独占节点,给节点打上专用标签,并对租户命名空间配置节点选择器和容忍度,确保该租户的 Pod 只能调度到独占节点上,其他租户的 Pod 永远不可能运行在独占节点上。
具体操作是给节点打上专用标签tenant=team-a-dedicated,并添加污点dedicated=team-a:NoSchedule。然后在租户命名空间中的每个工作负载模板里配置:
spec: template: spec: nodeSelector: tenant: team-a-dedicated tolerations: - key: "dedicated" operator: "Equal" value: "team-a" effect: "NoSchedule"做完这一步,该租户的所有工作负载只会调度到专属节点上,与平台内其他租户在物理层面完全隔离。不过要接受两个代价:一是节点利用率会下降,二是专属节点的故障会导致该租户业务全部不可用,必须额外规划高可用。一般来说,只有当合规要求明确不允许共享节点时才建议这么做。
根据我的实践经验,大多数企业内部部署做到 Namespace + 配额 + RBAC + NetworkPolicy + 存储隔离这五层,就已经能满足 90% 以上的多租户需求。节点级硬隔离留下作为合规场景的定制方案,不必默认启用。最后还是那句话:隔离体系建起来不难,难的是让它伴随业务增长持续有效运转。把资源申请、审计、告警这三件事制度化,比任何技术方案都重要。