ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Sealos私有化部署多租户隔离实战:从Namespace到网络存储全覆盖

Sealos私有化部署多租户隔离实战:从Namespace到网络存储全覆盖 接私有化部署这种活儿多了之后你会发现一个特别现实的规律单机部署能跑通只是万里长征第一步。真正让运维头疼的往往是“跑起来之后怎么管”。尤其是企业内部把 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 \ --serviceaccountns-team-a:sa-team-a \ --clusterroleadmin \ -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_NAMEns-$TENANT_NAME SA_NAMEsa-$TENANT_NAME RB_NAMErb-$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 \ --clusterroleadmin \ -n $NS_NAME # 6. 生成租户 kubeconfig TOKEN$(kubectl create token $SA_NAME -n $NS_NAME --duration720h) 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 一直 PendingEvents 里报错五花八门。我总结下来主要有三种典型场景。第一种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 --assystem:serviceaccount:ns-team-a:sa-team-a这个命令会直接返回 yes 或 no可以快速判断是 RBAC 配置问题还是 kubeconfig 使用问题。4.4 多租户隔离踩坑速查表症状根因解决方案Pod 长时间 PendingResourceQuota 超限或节点资源不足检查 resourcequota 和节点已分配资源跨命名空间服务访问被拒NetworkPolicy 未放行或网络插件不支持确认网络插件能力按需精确放行租户能看其他命名空间资源误用 ClusterRoleBinding改用 Role RoleBindingPVC 无法动态创建StorageClass 不存在或配额超限确认 StorageClass 与 PVC 配额应用域名解析超时NetworkPolicy 未放行 DNS 53 端口在 Egress 中追加 53 端口放行删除 PVC 后数据彻底消失reclaimPolicy 为 Delete改用 Retain 策略并定期回收这张表是我多轮排障之后沉淀下来的建议直接贴在运维文档里。很多时候返工不是因为方案不对而是栽在这些看起来不起眼的细节上。5. 经验总结与后续扩展方向5.1 从“能用”到“好用”的隔离运维建议说实话把多租户隔离的资源都配好只是做到了“能用”。要让平台在几十个租户并发使用下依然“好用”至少还要补两件事一是监控告警二是自动化治理。监控方面要把资源配额使用率、PV 数量、网络策略命中情况全部纳入可视化平台。我的习惯是针对每个租户命名空间配置独立的告警规则配额使用率超过 80% 就提前告警而不是等配额耗尽导致业务中断才去处理。推荐组合是 Prometheus AlertmanagerSealos 本身对这套监控体系支持得很好。自动化治理方面上面给到的租户创建脚本可以进一步扩展成平台化的自助申请入口。租户管理员通过内部工单系统申请资源审批通过后自动触发脚本创建隔离资源整个过程不经过人工敲命令。这样既提高了效率也留下审计记录后续排查问题时有据可查。5.2 后续可扩展节点级硬隔离与数据面隔离如果你们团队对隔离等级要求更高比如金融、政务这类对数据安全极度敏感的行业Namespace 级别的软隔离可能不够。这时候可以考虑节点级硬隔离为高安全等级的租户规划独占节点给节点打上专用标签并对租户命名空间配置节点选择器和容忍度确保该租户的 Pod 只能调度到独占节点上其他租户的 Pod 永远不可能运行在独占节点上。具体操作是给节点打上专用标签tenantteam-a-dedicated并添加污点dedicatedteam-a:NoSchedule。然后在租户命名空间中的每个工作负载模板里配置spec: template: spec: nodeSelector: tenant: team-a-dedicated tolerations: - key: dedicated operator: Equal value: team-a effect: NoSchedule做完这一步该租户的所有工作负载只会调度到专属节点上与平台内其他租户在物理层面完全隔离。不过要接受两个代价一是节点利用率会下降二是专属节点的故障会导致该租户业务全部不可用必须额外规划高可用。一般来说只有当合规要求明确不允许共享节点时才建议这么做。根据我的实践经验大多数企业内部部署做到 Namespace 配额 RBAC NetworkPolicy 存储隔离这五层就已经能满足 90% 以上的多租户需求。节点级硬隔离留下作为合规场景的定制方案不必默认启用。最后还是那句话隔离体系建起来不难难的是让它伴随业务增长持续有效运转。把资源申请、审计、告警这三件事制度化比任何技术方案都重要。
返回列表