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 策略在其中的验证位置:
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)为上述策略编写测试用例,覆盖节点标签缺失、拓扑键非法、亲和性权重越界等边界场景。每个测试用例应包含明确的输入和预期输出。