FEATURE STATE: Kubernetes v1.19 [stable]
你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pods 在集群内故障域 之间的分布,例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。
拓扑分布约束依赖于节点标签来标识每个节点所在的拓扑域。 例如,某节点可能具有标签:node=node1,zone=us-east-1a,region=us-east-1
假设你拥有具有以下标签的一个 4 节点集群:
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready <none> 4m26s v1.16.0 node=node1,zone=zoneA
node2 Ready <none> 3m58s v1.16.0 node=node2,zone=zoneA
node3 Ready <none> 3m17s v1.16.0 node=node3,zone=zoneB
node4 Ready <none> 2m43s v1.16.0 node=node4,zone=zoneB
那么,从逻辑上看集群如下:
你可以复用在大多数集群上自动创建和填充的常用标签, 而不是手动添加标签。
pod.spec.topologySpreadConstraints
字段定义如下所示:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
topologySpreadConstraints:
- maxSkew: <integer>
topologyKey: <string>
whenUnsatisfiable: <string>
labelSelector: <object>
你可以定义一个或多个 topologySpreadConstraint
来指示 kube-scheduler 如何根据与现有的 Pod 的关联关系将每个传入的 Pod 部署到集群中。字段包括:
whenUnsatisfiable
的 取值,其语义会有不同。whenUnsatisfiable
等于 "DoNotSchedule" 时,maxSkew
是目标拓扑域 中匹配的 Pod 数与全局最小值之间可存在的差异。whenUnsatisfiable
等于 "ScheduleAnyway" 时,调度器会更为偏向能够降低 偏差值的拓扑域。DoNotSchedule
(默认)告诉调度器不要调度。ScheduleAnyway
告诉调度器仍然继续调度,只是根据如何能将偏差最小化来对 节点进行排序。当 Pod 定义了不止一个 topologySpreadConstraint
,这些约束之间是逻辑与的关系。 kube-scheduler 会为新的 Pod 寻找一个能够满足所有约束的节点。
你可以执行 kubectl explain Pod.spec.topologySpreadConstraints
命令以 了解关于 topologySpreadConstraints 的更多信息。
假设你拥有一个 4 节点集群,其中标记为 foo:bar
的 3 个 Pod 分别位于 node1、node2 和 node3 中:
如果希望新来的 Pod 均匀分布在现有的可用区域,则可以按如下设置其规约:
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: k8s.gcr.io/pause:3.1
topologyKey: zone
意味着均匀分布将只应用于存在标签键值对为 "zone:<any value>" 的节点。 whenUnsatisfiable: DoNotSchedule
告诉调度器如果新的 Pod 不满足约束, 则让它保持悬决状态。
如果调度器将新的 Pod 放入 "zoneA",Pods 分布将变为 [3, 1],因此实际的偏差 为 2(3 - 1)。这违反了 maxSkew: 1
的约定。此示例中,新 Pod 只能放置在 "zoneB" 上:
或者
你可以调整 Pod 规约以满足各种要求:
maxSkew
更改为更大的值,比如 "2",这样新的 Pod 也可以放在 "zoneA" 上。topologyKey
更改为 "node",以便将 Pod 均匀分布在节点上而不是区域中。 在上面的例子中,如果 maxSkew
保持为 "1",那么传入的 Pod 只能放在 "node4" 上。whenUnsatisfiable: DoNotSchedule
更改为 whenUnsatisfiable: ScheduleAnyway
, 以确保新的 Pod 始终可以被调度(假设满足其他的调度 API)。 但是,最好将其放置在匹配 Pod 数量较少的拓扑域中。 (请注意,这一优先判定会与其他内部调度优先级(如资源使用率等)排序准则一起进行标准化。)下面的例子建立在前面例子的基础上。假设你拥有一个 4 节点集群,其中 3 个标记为 foo:bar
的 Pod 分别位于 node1、node2 和 node3 上:
可以使用 2 个 TopologySpreadConstraint 来控制 Pod 在 区域和节点两个维度上的分布:
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
- maxSkew: 1
topologyKey: node
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: k8s.gcr.io/pause:3.1
在这种情况下,为了匹配第一个约束,新的 Pod 只能放置在 "zoneB" 中;而在第二个约束中, 新的 Pod 只能放置在 "node4" 上。最后两个约束的结果加在一起,唯一可行的选择是放置 在 "node4" 上。
多个约束之间可能存在冲突。假设有一个跨越 2 个区域的 3 节点集群:
如果对集群应用 "two-constraints.yaml",会发现 "mypod" 处于 Pending
状态。 这是因为:为了满足第一个约束,"mypod" 只能放在 "zoneB" 中,而第二个约束要求 "mypod" 只能放在 "node2" 上。Pod 调度无法满足两种约束。
为了克服这种情况,你可以增加 maxSkew
或修改其中一个约束,让其使用 whenUnsatisfiable: ScheduleAnyway
。
如果 Pod 定义了 spec.nodeSelector
或 spec.affinity.nodeAffinity
, 调度器将在偏差计算中跳过不匹配的节点。
假设你有一个跨越 zoneA 到 zoneC 的 5 节点集群:
而且你知道 "zoneC" 必须被排除在外。在这种情况下,可以按如下方式编写 YAML, 以便将 "mypod" 放置在 "zoneB" 上,而不是 "zoneC" 上。同样,spec.nodeSelector
也要一样处理。
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: NotIn
values:
- zoneC
containers:
- name: pause
image: k8s.gcr.io/pause:3.1
调度器不会预先知道集群拥有的所有区域和其他拓扑域。拓扑域由集群中存在的节点确定。 在自动伸缩的集群中,如果一个节点池(或节点组)的节点数量为零, 而用户正期望其扩容时,可能会导致调度出现问题。 因为在这种情况下,调度器不会考虑这些拓扑域信息,因为它们是空的,没有节点。
这里有一些值得注意的隐式约定:
为集群设置默认的拓扑分布约束也是可能的。默认拓扑分布约束在且仅在以下条件满足 时才会应用到 Pod 上:
.spec.topologySpreadConstraints
设置任何约束;你可以在 调度方案(Scheduling Profile) 中将默认约束作为 PodTopologySpread
插件参数的一部分来设置。 约束的设置采用如前所述的 API,只是 labelSelector
必须为空。 选择算符是根据 Pod 所属的服务、副本控制器、ReplicaSet 或 StatefulSet 来设置的。
配置的示例可能看起来像下面这个样子:
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: PodTopologySpread
args:
defaultConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
defaultingType: List
默认调度约束所生成的评分可能与 SelectorSpread 插件 所生成的评分有冲突。 建议你在为
PodTopologySpread
设置默认约束是禁用调度方案中的该插件。
FEATURE STATE: Kubernetes v1.20 [beta]
当你使用了默认启用的 DefaultPodTopologySpread
特性门控时,原来的 SelectorSpread
插件会被禁用。 kube-scheduler 会使用下面的默认拓扑约束作为 PodTopologySpread
插件的 配置:
defaultConstraints:
- maxSkew: 3
topologyKey: "kubernetes.io/hostname"
whenUnsatisfiable: ScheduleAnyway
- maxSkew: 5
topologyKey: "topology.kubernetes.io/zone"
whenUnsatisfiable: ScheduleAnyway
此外,原来用于提供等同行为的 SelectorSpread
插件也会被禁用。
对于分布约束中所指定的拓扑键而言,PodTopologySpread
插件不会为不包含这些主键的节点评分。 这可能导致在使用默认拓扑约束时,其行为与原来的 SelectorSpread
插件的默认行为不同,
如果你的节点不会 同时 设置 kubernetes.io/hostname
和 topology.kubernetes.io/zone
标签,你应该定义自己的约束而不是使用 Kubernetes 的默认约束。
如果你不想为集群使用默认的 Pod 分布约束,你可以通过设置 defaultingType
参数为 List
并将 PodTopologySpread
插件配置中的 defaultConstraints
参数置空来禁用默认 Pod 分布约束。
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: PodTopologySpread
args:
defaultConstraints: []
defaultingType: List
在 Kubernetes 中,与“亲和性”相关的指令控制 Pod 的调度方式(更密集或更分散)。
PodAffinity
,你可以尝试将任意数量的 Pod 集中到符合条件的拓扑域中。PodAntiAffinity
,只能将一个 Pod 调度到某个拓扑域中。要实现更细粒度的控制,你可以设置拓扑分布约束来将 Pod 分布到不同的拓扑域下, 从而实现高可用性或节省成本。这也有助于工作负载的滚动更新和平稳地扩展副本规模。
CronJobCronJob用于执行周期性的动作,例如备份、报告生成等。这些任务中的每一个都应该配置为周期性重复的(例如:每天/每周/每...
拓扑感知提示FEATURESTATE:Kubernetesv1.23[beta]拓扑感知提示包含客户怎么使用服务端点的建议,从而实现了拓扑感知的路由功能。...
Windows节点的安全性本页介绍特定于Windows操作系统的安全注意事项和最佳实践。保护节点上的机密数据在Windows上,来自Secrets的...
垃圾收集垃圾收集是Kubernetes用于清理集群资源的各种机制的统称。垃圾收集允许系统清理如下资源:失败的Pod已完成的Job不再存在...
Kubernetes系统组件指标通过系统组件指标可以更好地了解系统组个内部发生的情况。系统组件指标对于构建仪表板和告警特别有用。Ku...