3. Kubernetes 节点拓扑:节点标签(region/zone)、内置拓扑键、节点亲和性

在 Kubernetes 集群中,节点拓扑是故障域划分的物理基础。OPA 策略需要验证节点标签的完整性、拓扑键的合规性以及亲和性规则的边界条件。本节将系统梳理这三层概念及其在策略测试中的关键作用。

3.1 节点标签:region 与 zone 的语义约定

Kubernetes 节点通过标签(Label)标识其所在的故障域。社区和云厂商普遍遵循以下约定:

标签键 语义 示例值 策略关注点
topology.kubernetes.io/region 地理区域(如华北、华东) cn-north-1 必须存在且非空
topology.kubernetes.io/zone 可用区(如机房A、机房B) cn-north-1a 必须存在且非空
failure-domain.beta.kubernetes.io/region 旧版 region 标签(已弃用) cn-north-1 兼容性检查
failure-domain.beta.kubernetes.io/zone 旧版 zone 标签(已弃用) cn-north-1a 兼容性检查
策略测试要点: 验证所有节点是否同时具备 region 和 zone 标签;检查同一 zone 内的节点数量是否超过集群总节点数的 50%(防止单点故障域过大)。

3.2 内置拓扑键:Kubernetes 的故障域抽象

Kubernetes 预定义了一组内置拓扑键,用于在 Pod 调度、拓扑分布约束(TopologySpreadConstraints)和卷拓扑(CSI Topology)中引用故障域。常见的内置拓扑键包括:

  • kubernetes.io/hostname — 节点主机名,代表最细粒度的故障域(单个物理机/虚拟机)。
  • topology.kubernetes.io/zone — 可用区,代表中等粒度的故障域。
  • topology.kubernetes.io/region — 地理区域,代表最粗粒度的故障域。

在 OPA 策略中,需要验证用户定义的拓扑分布约束是否使用了合法的内置拓扑键,避免使用拼写错误或自定义的无效键名。

最佳实践: 在集群准入控制层(Admission Webhook)中,使用 OPA 策略拦截使用了非内置拓扑键的 Pod 或 Deployment,强制团队使用标准化拓扑键。

3.3 节点亲和性:调度约束与故障域绑定

节点亲和性(Node Affinity)允许用户将 Pod 调度到特定拓扑域的节点上。亲和性规则分为两种:

类型 子类型 语义 策略验证场景
requiredDuringSchedulingIgnoredDuringExecution 硬约束 Pod 必须调度到满足条件的节点,否则无法调度 验证 region/zone 标签是否与集群实际拓扑一致
preferredDuringSchedulingIgnoredDuringExecution 软约束 调度器优先选择满足条件的节点,但不强制 验证权重值是否在合法范围(1-100)

OPA 策略需要检查以下边界条件:

  • 硬约束中引用的 region 或 zone 值是否存在于集群节点标签中。
  • 软约束的权重是否为正整数且不超过 100。
  • 是否同时使用了 region 和 zone 的亲和性规则,导致调度约束过于严格。

3.4 拓扑关系与策略验证流程

下图展示了节点标签、内置拓扑键和节点亲和性之间的逻辑关系,以及 OPA 策略在其中的验证位置:

节点拓扑与 OPA 策略验证流程 Kubernetes 节点 标签: region=cn-north-1 标签: zone=cn-north-1a 标签: hostname=node-01 内置拓扑键 topology.kubernetes.io/region topology.kubernetes.io/zone kubernetes.io/hostname 节点亲和性 required: region=cn-north-1 preferred: zone=cn-north-1a 权重: 80 OPA 策略验证引擎 验证标签完整性 | 检查拓扑键合法性 | 校验亲和性边界 拒绝非法拓扑键 | 防止单 zone 过载 | 强制 region/zone 一致性 准入通过 Pod 允许创建 准入拒绝 返回错误信息 策略测试边界:节点标签缺失、拓扑键拼写错误、亲和性规则冲突、单 zone 节点数超限 OPA 应在 Admission Webhook 中拦截不符合拓扑规范的 Pod 创建请求

3.5 策略测试的边界条件清单

针对节点拓扑的 OPA 策略测试,需要覆盖以下边界条件:

编号 测试场景 预期结果 测试方法
TC-01 节点缺少 topology.kubernetes.io/region 标签 拒绝调度到该节点 模拟节点标签缺失,验证 OPA 规则触发
TC-02 节点缺少 topology.kubernetes.io/zone 标签 拒绝调度到该节点 模拟节点标签缺失,验证 OPA 规则触发
TC-03 Pod 使用自定义拓扑键(如 mycompany.io/rack) 拒绝创建 Pod 在 Pod 的 topologySpreadConstraints 中使用非法键
TC-04 节点亲和性硬约束引用了不存在的 zone 值 拒绝创建 Pod 设置 requiredDuringScheduling 匹配不存在的标签值
TC-05 节点亲和性软约束权重为 0 或负数 拒绝创建 Pod 设置 weight: 0 或 weight: -10
TC-06 同一 zone 内节点数超过集群总节点数的 50% 告警或拒绝新节点加入 模拟节点分布,验证 OPA 策略的计数逻辑

3.6 节点拓扑与 Pod 拓扑分布约束的联动

除了节点亲和性,Pod 拓扑分布约束(TopologySpreadConstraints)也依赖节点拓扑键。OPA 策略需要同时验证:

  • topologyKey 字段是否使用了内置拓扑键。
  • maxSkew 值是否在合理范围(通常为 1-3)。
  • whenUnsatisfiable 字段是否为 DoNotSchedule 或 ScheduleAnyway。
常见陷阱: 如果集群节点标签不一致(例如部分节点缺少 zone 标签),Pod 拓扑分布约束可能无法正确计算分布偏差,导致调度器行为异常。OPA 策略应在准入阶段强制所有节点具备完整的拓扑标签。

3.7 策略代码示例:验证节点标签完整性

以下 OPA 策略片段用于验证节点是否同时具备 region 和 zone 标签:

package kubernetes.admission

import future.keywords.in

# 验证节点标签完整性
deny[msg] {
    input.request.kind.kind == "Node"
    node := input.request.object
    required_labels := {"topology.kubernetes.io/region", "topology.kubernetes.io/zone"}
    missing := [label | label := required_labels[_]; not node.metadata.labels[label]]
    count(missing) > 0
    msg := sprintf("节点 %v 缺少必要拓扑标签: %v", [node.metadata.name, missing])
}

# 验证 Pod 拓扑分布约束中的拓扑键
deny[msg] {
    input.request.kind.kind == "Pod"
    pod := input.request.object
    constraints := pod.spec.topologySpreadConstraints
    some i
    key := constraints[i].topologyKey
    not key in ["kubernetes.io/hostname", "topology.kubernetes.io/zone", "topology.kubernetes.io/region"]
    msg := sprintf("Pod %v 使用了非法的拓扑键: %v", [pod.metadata.name, key])
}
测试建议: 使用 OPA 的单元测试框架(opa test)为上述策略编写测试用例,覆盖节点标签缺失、拓扑键非法、亲和性权重越界等边界场景。每个测试用例应包含明确的输入和预期输出。