3. 多租户模型设计:租户定义、生命周期与资源隔离策略

多租户是云原生平台的核心能力之一。在 Operator 控制面中,租户不仅仅是逻辑上的组织单位,更是资源配额、网络策略、安全边界和幂等控制策略的载体。本章将围绕 Tenant CRD 的定义、租户从创建到销毁的完整生命周期管理、命名空间策略的自动编排,以及资源配额与限制范围的设计展开,构建一套可复用的多租户隔离模型。

3.1 租户定义:Tenant CRD 设计

Tenant CRD 是整个多租户模型的根资源。每个 Tenant 实例代表一个独立的租户,Operator 根据 Tenant 的 spec 自动生成对应的命名空间、RBAC 规则、网络策略和资源配额。以下是一个精简但完整的 Tenant CRD 定义示例:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: tenants.tenant.example.com
spec:
  group: tenant.example.com
  names:
    kind: Tenant
    listKind: TenantList
    plural: tenants
    singular: tenant
    shortNames:
      - tn
  scope: Cluster
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required:
                - displayName
                - namespacePrefix
              properties:
                displayName:
                  type: string
                  maxLength: 63
                namespacePrefix:
                  type: string
                  pattern: '^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$'
                  maxLength: 20
                adminUsers:
                  type: array
                  items:
                    type: string
                resourceQuota:
                  type: object
                  properties:
                    hard:
                      type: object
                      additionalProperties:
                        type: string
                limitRange:
                  type: object
                  properties:
                    limits:
                      type: array
                      items:
                        type: object
                networkPolicy:
                  type: object
                  properties:
                    ingressIsolation:
                      type: boolean
                      default: true
                    egressIsolation:
                      type: boolean
                      default: false
                labels:
                  type: object
                  additionalProperties:
                    type: string
            status:
              type: object
              properties:
                phase:
                  type: string
                  enum:
                    - Pending
                    - Active
                    - Degraded
                    - Terminating
                namespaces:
                  type: array
                  items:
                    type: string
                observedGeneration:
                  type: integer
                conditions:
                  type: array
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                      status:
                        type: string
                      reason:
                        type: string
                      message:
                        type: string
                      lastTransitionTime:
                        type: string
                        format: date-time
设计要点: Tenant CRD 使用 Cluster 作用域,因为租户需要跨命名空间管理资源。namespacePrefix 字段用于自动生成命名空间名称(如 tn-prod-001),确保命名空间可预测且不冲突。observedGeneration 用于幂等控制,避免重复处理。

3.2 租户生命周期管理

租户的生命周期分为四个阶段:创建(Pending → Active)、运行(Active)、降级(Degraded) 和 销毁(Terminating → 删除)。Operator 的 Reconciler 根据 Tenant 对象的 metadata.deletionTimestamp 和 status.phase 决定当前执行的动作。

阶段 触发条件 Operator 动作 幂等保证
Pending Tenant 创建,无 deletionTimestamp 创建命名空间、RBAC、NetworkPolicy、ResourceQuota、LimitRange 检查命名空间是否已存在,跳过已创建的资源
Active 所有子资源就绪 定期校验子资源状态,更新 conditions 对比 spec 与当前状态,仅更新差异部分
Degraded 子资源缺失或配置不一致 重新创建或修复子资源,记录错误原因 使用 resourceVersion 避免并发覆盖
Terminating deletionTimestamp 不为空 清理命名空间及关联资源,移除 finalizer 幂等删除:忽略已不存在的资源
幂等控制关键点: 在 Reconciler 中,每次 Reconcile 开始时先检查 deletionTimestamp,若不为空则进入 Terminating 分支;否则根据 status.phase 和子资源状态决定是创建还是修复。所有写操作前都执行 Get 检查,避免重复创建。

3.3 命名空间策略:自动编排与隔离

每个租户可以拥有一个或多个命名空间。Operator 根据 Tenant CRD 的 namespacePrefix 和 labels 自动生成命名空间,并附加以下策略:

  • 命名空间命名规则: {namespacePrefix}-{environment},例如 tn-prod-001、tn-staging-001。environment 由 Operator 根据 Tenant 的 spec.environments 字段生成。
  • 标签继承: 自动为命名空间添加 tenant-id: {tenant.Name}、tenant-display-name: {spec.displayName} 标签,便于后续资源查询和审计。
  • 网络隔离: 默认创建 DenyAllIngress 的 NetworkPolicy,仅允许同命名空间内的流量;若 spec.networkPolicy.ingressIsolation 为 false,则创建允许所有入站流量的策略。
  • RBAC 绑定: 为 spec.adminUsers 中的每个用户创建 RoleBinding,绑定到 admin 角色,确保租户管理员拥有命名空间内的管理权限。
// Reconciler 中创建命名空间的伪代码
func (r *TenantReconciler) ensureNamespace(ctx context.Context, tenant *tenantv1.Tenant) error {
    nsName := fmt.Sprintf("%s-%s", tenant.Spec.NamespacePrefix, "default")
    ns := &corev1.Namespace{
        ObjectMeta: metav1.ObjectMeta{
            Name: nsName,
            Labels: map[string]string{
                "tenant-id":           tenant.Name,
                "tenant-display-name": tenant.Spec.DisplayName,
                "managed-by":          "tenant-operator",
            },
        },
    }
    // 幂等创建:先 Get,不存在则 Create
    existing := &corev1.Namespace{}
    err := r.Get(ctx, types.NamespacedName{Name: nsName}, existing)
    if err != nil && apierrors.IsNotFound(err) {
        return r.Create(ctx, ns)
    }
    return err
}
租户命名空间自动编排流程 Tenant CR Reconciler Namespace NetworkPolicy RoleBinding ResourceQuota LimitRange 幂等控制策略 • 先 Get 再 Create/Update • 使用 resourceVersion 防冲突 • 删除时忽略 NotFound 错误

3.4 资源配额与限制范围设计

资源配额(ResourceQuota)和限制范围(LimitRange)是防止租户资源滥用、保障集群稳定性的关键手段。Operator 根据 Tenant CRD 的 spec.resourceQuota 和 spec.limitRange 自动生成对应的 Kubernetes 资源。

3.4.1 ResourceQuota 设计

每个租户命名空间自动创建一个 ResourceQuota,默认配置如下:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: tenant-quota
  namespace: tn-prod-001
spec:
  hard:
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"
    persistentvolumeclaims: "10"
    pods: "50"
    services: "20"
    secrets: "100"
    configmaps: "100"
注意: ResourceQuota 的 hard 字段必须与集群实际容量匹配。建议在 Tenant CRD 中提供默认值,同时允许租户管理员通过 spec.resourceQuota.hard 覆盖。Operator 在更新配额时,需校验新值是否超过集群剩余容量,避免超卖。

3.4.2 LimitRange 设计

LimitRange 用于约束单个 Pod 或容器的资源上下限,防止某个 Pod 占用过多资源。默认配置如下:

apiVersion: v1
kind: LimitRange
metadata:
  name: tenant-limits
  namespace: tn-prod-001
spec:
  limits:
    - type: Container
      default:
        cpu: "500m"
        memory: "512Mi"
      defaultRequest:
        cpu: "100m"
        memory: "128Mi"
      max:
        cpu: "4"
        memory: "8Gi"
      min:
        cpu: "10m"
        memory: "32Mi"
    - type: PersistentVolumeClaim
      max:
        storage: "100Gi"
      min:
        storage: "1Gi"

3.4.3 配额与限制的联动策略

维度 ResourceQuota LimitRange 联动效果
CPU 限制命名空间总 CPU 请求/限制 限制单个容器 CPU 上下限 防止单个 Pod 占满整个命名空间配额
内存 限制命名空间总内存请求/限制 限制单个容器内存上下限 避免内存泄漏 Pod 影响其他租户
存储 限制 PVC 总数 限制单个 PVC 大小 防止大量小 PVC 或超大 PVC
对象数量 限制 Pod/Service/Secret 数量 不适用 防止 etcd 压力过大
幂等更新策略: 当 Tenant CRD 的 spec.resourceQuota 或 spec.limitRange 发生变化时,Operator 先获取当前 ResourceQuota/LimitRange 对象,对比 spec 字段,仅在有差异时执行 Update。更新前使用 resourceVersion 确保乐观锁,避免并发覆盖。

3.5 租户状态与条件管理

Tenant 的 status 字段记录了当前阶段和详细条件,便于用户和运维人员了解租户健康状态。Operator 在每次 Reconcile 结束时更新 status.conditions,包含以下类型:

  • NamespaceReady: 所有命名空间已创建且状态正常。
  • QuotaApplied: ResourceQuota 和 LimitRange 已创建或更新成功。
  • NetworkPolicyApplied: NetworkPolicy 已就绪。
  • RBACReady: 所有 RoleBinding 已创建。

当所有条件均为 True 时,status.phase 切换为 Active;若任一条件为 False,则切换为 Degraded,并在 status.conditions[].message 中记录错误原因。

租户状态机与条件流转 Pending Active Degraded Terminating 条件流转规则 • Pending → Active:所有 conditions 为 True • Active → Degraded:任一 condition 变为 False(如 QuotaApplied=False) • 任意阶段 → Terminating:deletionTimestamp 不为空

3.6 幂等控制与错误恢复

在多租户场景中,Operator 必须处理网络抖动、API Server 限流、资源冲突等异常情况。以下是幂等控制的核心策略:

  • 先读后写: 所有创建/更新操作前先执行 Get,若资源已存在且 spec 一致则跳过。
  • 使用 resourceVersion: 更新操作携带 resourceVersion,避免并发覆盖。
  • Finalizer 机制: 在 Tenant 创建时添加 tenant-cleanup finalizer,删除时先清理子资源再移除 finalizer,确保资源不会残留。
  • 重试与退避: 当 API 返回 429 或 500 时,Reconciler 自动重试,重试间隔指数退避(1s、2s、4s…最大 30s)。
  • 状态持久化: 每次 Reconcile 结束时更新 status.observedGeneration,避免重复处理相同版本的 Tenant。
最佳实践: 在 Tenant CRD 的 status.conditions 中记录每次 Reconcile 的详细错误信息,便于运维人员快速定位问题。同时,建议为 Operator 添加 Prometheus 指标(如 tenant_reconcile_total、tenant_reconcile_errors_total),实现可观测性。