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