4. 租户标识设计:基于命名空间的租户映射、自定义资源(CR)中的租户字段、标签(Label)与注解(Annotation)的租户标记

租户标识是 Operator 多租户隔离的“第一道门”。没有统一的租户标识方案,复核流程就无法确定某个资源属于哪个租户、是否越权。本章从三个层次构建租户标识体系:命名空间映射(基础设施层)、CR 字段(数据模型层)、标签与注解(元数据层)。

4.1 三层租户标识模型

三种标识方式各有侧重,在实际复核中需要组合使用。下表对比了它们的核心差异:

标识层次 载体 作用域 复核用途 变更成本
命名空间映射 Namespace 名称/标签 集群级 资源归属边界判定 低(命名空间创建时确定)
CR 字段 自定义资源 spec.tenant 资源实例级 租户身份显式声明 中(需修改 CRD 定义)
标签与注解 Label / Annotation 任意资源级 快速筛选与审计追溯 低(运行时动态增删)
最佳实践:命名空间映射作为“硬边界”,CR 字段作为“显式声明”,标签与注解作为“灵活标记”。复核时优先检查命名空间映射,再验证 CR 字段与标签的一致性。

4.2 基于命名空间的租户映射

命名空间是 Kubernetes 内置的隔离边界。Operator 通常采用两种映射策略:

  • 一对一映射:每个租户独占一个命名空间,命名空间名称包含租户标识(如 tenant-alpha-prod)。
  • 标签映射:多个命名空间通过固定标签关联到同一租户(如 tenant-id: alpha)。

复核时需验证命名空间是否携带正确的租户标签:

# 检查命名空间的租户标签
kubectl get namespace tenant-alpha-prod -o json | jq '.metadata.labels'
{
  "tenant-id": "alpha",
  "environment": "prod",
  "managed-by": "operator-tenant-controller"
}

# 复核:所有属于租户 alpha 的命名空间必须包含 tenant-id: alpha
kubectl get namespace -l tenant-id=alpha
常见陷阱:不要仅依赖命名空间名称做租户判定。名称可以被伪造或误写,标签 + 命名空间名称双重校验更可靠。Operator 复核时应当读取命名空间的标签,而非解析字符串。

4.3 自定义资源(CR)中的租户字段

CR 作为 Operator 的核心数据模型,必须在 spec 中显式声明租户归属。推荐字段设计如下:

apiVersion: example.io/v1
kind: Workload
metadata:
  name: app-01
  namespace: tenant-alpha-prod
spec:
  tenant: "alpha"                # 必填:租户标识
  tenantRef:                     # 可选:租户资源引用
    apiGroup: tenant.example.io
    kind: Tenant
    name: alpha-tenant
  resources:
    cpu: "4"
    memory: "8Gi"

复核规则:

  • 必填校验:CRD 中 spec.tenant 字段必须设置为 required,拒绝未指定租户的 CR。
  • 一致性校验:CR 的 spec.tenant 必须与所在命名空间的 tenant-id 标签一致。
  • 引用校验:如果使用 tenantRef,需确保引用的 Tenant 资源存在且状态为 Active。

4.4 标签(Label)与注解(Annotation)的租户标记

标签用于资源筛选和复核策略绑定,注解用于存储审计元数据。推荐的标准标记集:

键 类型 值示例 用途
tenant.operator.io/id Label alpha 租户标识,用于复核策略匹配
tenant.operator.io/environment Label prod 环境隔离,防止跨环境访问
tenant.operator.io/created-by Annotation user:alice 审计追溯,记录创建者
tenant.operator.io/last-reconciled Annotation 2025-03-15T10:30:00Z 复核时间戳,用于过期检测

Operator 在 reconcile 循环中自动注入这些标记:

// 在 Operator 控制器中自动注入租户标签
labels := map[string]string{
    "tenant.operator.io/id":          tenantID,
    "tenant.operator.io/environment": env,
}
annotations := map[string]string{
    "tenant.operator.io/created-by":      username,
    "tenant.operator.io/last-reconciled": time.Now().UTC().Format(time.RFC3339),
}

// 创建或更新资源时合并标签与注解
if err := r.Client.Update(ctx, resource); err != nil {
    // 处理错误
}

4.5 租户标识复核流程

下图展示了 Operator 在复核时如何串联三层标识:

租户标识复核流程 CR 实例 步骤1:提取 spec.tenant 字段 步骤2:读取命名空间 tenant-id 标签 步骤3:CR字段 vs 命名空间标签 不一致 → 拒绝并告警 一致 → 注入标签/注解 复核通过后,Operator 自动注入 tenant.operator.io/id 标签和 last-reconciled 注解

4.6 标识设计复核清单

在 Operator 开发完成后,使用以下清单逐项复核租户标识设计:

  1. 命名空间映射完整性:所有租户命名空间是否都包含 tenant-id 标签?是否有一致的前缀命名规范?
  2. CR 字段必填性:CRD 中 spec.tenant 是否设置为 required?是否配置了 +kubebuilder:validation:Required 标记?
  3. 一致性校验逻辑:Operator 的 webhook 或 reconcile 循环是否校验 CR 字段与命名空间标签的一致性?
  4. 标签自动注入:Operator 是否在创建/更新资源时自动注入租户标签和注解?
  5. 审计追溯能力:注解中是否记录了创建者、最后复核时间等审计信息?
  6. 跨命名空间访问控制:是否通过 RBAC 或策略引擎限制了跨命名空间的资源访问?
关键结论:租户标识设计不是单一维度的选择,而是三层标识的协同工作。命名空间提供边界,CR 字段提供声明,标签与注解提供可操作性。Operator 复核时,三层标识必须全部对齐,任何一层的不一致都应触发告警或拒绝操作。