2.2. 配置默认调度程序以控制 pod 放置
OpenShift Container Platform 的默认 pod 调度程序负责确定在集群的节点中放置新的 pod。它从 pod 读取数据,并尝试根据配置的策略寻找最适合的节点。它完全独立存在,作为单机或可插拔的解决方案。它不会修改 pod,只是为 pod 创建将 pod 与特定节点衔接起来的绑定。
调度程序的策略由一组 predicates 和 priorities 来定义。如需 predicates 和 priorities 的列表,请参阅修改调度程序策略。
默认调度程序对象示例
apiVersion: config.openshift.io/v1 kind: Scheduler metadata: annotations: release.openshift.io/create-only: "true" creationTimestamp: 2019-05-20T15:39:01Z generation: 1 name: cluster resourceVersion: "1491" selfLink: /apis/config.openshift.io/v1/schedulers/cluster uid: 6435dd99-7b15-11e9-bd48-0aec821b8e34 spec: policy: 1 name: scheduler-policy defaultNodeSelector: type=user-node,region=east 2
2.2.1. 了解默认调度
现有的通用调度程序是平台默认提供的调度程序引擎,它可通过三步操作来选择托管 pod 的节点:
- 过滤节点
- 根据指定的约束或要求过滤可用的节点。这可以通过名为predicates的过滤函数列表筛选每个节点来完成。
- 排列过滤后节点列表的优先顺序
- 实现方式是让每个节点通过一系列优先级函数,以为其分配从 0 到 10 的一个分数。0 代表不适合的节点,10 则代表最适合托管该 pod。调度程序配置还可以为每个优先级函数使用简单的权重(正数值)。每个优先级函数提供的节点分数乘以权重(大多数优先级的默认权重为 1),然后将每个节点从所有优先级获得的分数相加。管理员可以使用这个权重属性,为一些优先级赋予更高的重要性。
- 选择最适合的节点
- 节点按照分数排序,系统选择分数最高的节点来托管该 pod。如果多个节点的分数相同,则随机选择其中一个。
2.2.1.1. 了解调度程序策略
调度程序的策略由一组 predicates 和 priorities 来定义。
调度程序配置文件是一个 JSON 文件,必须命名为 policy.cfg
,用于指定调度程序将要考量的 predicates 和 priorities。
如果没有调度程序策略文件,则使用默认的调度程序行为。
调度程序配置文件中定义的 predicates 和 priorities 会完全覆盖默认的调度程序策略。如果需要任何默认的 predicates 和 priorities,必须在策略配置中明确指定对应的函数。
调度程序配置映射示例
apiVersion: v1
data:
policy.cfg: |
{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [
{"name" : "MaxGCEPDVolumeCount"},
{"name" : "GeneralPredicates"}, 1
{"name" : "MaxAzureDiskVolumeCount"},
{"name" : "MaxCSIVolumeCountPred"},
{"name" : "CheckVolumeBinding"},
{"name" : "MaxEBSVolumeCount"},
{"name" : "MatchInterPodAffinity"},
{"name" : "CheckNodeUnschedulable"},
{"name" : "NoDiskConflict"},
{"name" : "NoVolumeZoneConflict"},
{"name" : "PodToleratesNodeTaints"}
],
"priorities" : [
{"name" : "LeastRequestedPriority", "weight" : 1},
{"name" : "BalancedResourceAllocation", "weight" : 1},
{"name" : "ServiceSpreadingPriority", "weight" : 1},
{"name" : "NodePreferAvoidPodsPriority", "weight" : 1},
{"name" : "NodeAffinityPriority", "weight" : 1},
{"name" : "TaintTolerationPriority", "weight" : 1},
{"name" : "ImageLocalityPriority", "weight" : 1},
{"name" : "SelectorSpreadPriority", "weight" : 1},
{"name" : "InterPodAffinityPriority", "weight" : 1},
{"name" : "EqualPriority", "weight" : 1}
]
}
kind: ConfigMap
metadata:
creationTimestamp: "2019-09-17T08:42:33Z"
name: scheduler-policy
namespace: openshift-config
resourceVersion: "59500"
selfLink: /api/v1/namespaces/openshift-config/configmaps/scheduler-policy
uid: 17ee8865-d927-11e9-b213-02d1e1709840`
- 1
GeneralPredicates
predicate 代表PodFitsResources
、HostName
、PodFitsHostPorts
和MatchNodeSelector
predicate。由于无法多次配置同一 predicate,所以GeneralPredicates
predicate 不能与四个代表的 predicate 之一一起使用。
2.2.2. 创建调度程序策略文件
您可以通过创建 JSON 文件并使用所需的 predicates 和 priorities 来控制或更改默认的调度行为。然后,您可以通过 JSON 文件生成配置映射,并将 cluster
调度程序对象指定为使用该配置映射。
流程
配置调度程序策略:
使用所需的 predicates 和 priorities,创建一个名为
policy.cfg
的 JSON 文件。调度程序 JSON 文件示例
{ "kind" : "Policy", "apiVersion" : "v1", "predicates" : [ 1 {"name" : "MaxGCEPDVolumeCount"}, {"name" : "GeneralPredicates"}, {"name" : "MaxAzureDiskVolumeCount"}, {"name" : "MaxCSIVolumeCountPred"}, {"name" : "CheckVolumeBinding"}, {"name" : "MaxEBSVolumeCount"}, {"name" : "MatchInterPodAffinity"}, {"name" : "CheckNodeUnschedulable"}, {"name" : "NoDiskConflict"}, {"name" : "NoVolumeZoneConflict"}, {"name" : "PodToleratesNodeTaints"} ], "priorities" : [ 2 {"name" : "LeastRequestedPriority", "weight" : 1}, {"name" : "BalancedResourceAllocation", "weight" : 1}, {"name" : "ServiceSpreadingPriority", "weight" : 1}, {"name" : "NodePreferAvoidPodsPriority", "weight" : 1}, {"name" : "NodeAffinityPriority", "weight" : 1}, {"name" : "TaintTolerationPriority", "weight" : 1}, {"name" : "ImageLocalityPriority", "weight" : 1}, {"name" : "SelectorSpreadPriority", "weight" : 1}, {"name" : "InterPodAffinityPriority", "weight" : 1}, {"name" : "EqualPriority", "weight" : 1} ] }
根据调度程序 JSON 文件创建配置映射:
$ oc create configmap -n openshift-config --from-file=policy.cfg <configmap-name> 1
- 1
- 输入配置映射的名称。
例如:
$ oc create configmap -n openshift-config --from-file=policy.cfg scheduler-policy configmap/scheduler-policy created
编辑调度程序 Operator 自定义资源以添加配置映射:
$ oc patch Scheduler cluster --type='merge' -p '{"spec":{"policy":{"name":"<configmap-name>"}}}' --type=merge 1
- 1
- 指定配置映射的名称。
例如:
$ oc patch Scheduler cluster --type='merge' -p '{"spec":{"policy":{"name":"scheduler-policy"}}}' --type=merge
在修改了
Scheduler
配置资源后,等待openshift-kube-apiserver
pod 重新部署。这可能需要几分钟。只有重新部署 pod 后,新的调度程序才会生效。通过查看
openshift-kube-scheduler
命名空间中调度程序 pod 的日志,来验证已配置调度程序策略。以下命令检查由调度程序注册的 predicates 和 priorities:$ oc logs <scheduler-pod> | grep predicates
例如:
$ oc logs openshift-kube-scheduler-ip-10-0-141-29.ec2.internal | grep predicates Creating scheduler with fit predicates 'map[MaxGCEPDVolumeCount:{} MaxAzureDiskVolumeCount:{} CheckNodeUnschedulable:{} NoDiskConflict:{} NoVolumeZoneConflict:{} GeneralPredicates:{} MaxCSIVolumeCountPred:{} CheckVolumeBinding:{} MaxEBSVolumeCount:{} MatchInterPodAffinity:{} PodToleratesNodeTaints:{}]' and priority functions 'map[InterPodAffinityPriority:{} LeastRequestedPriority:{} ServiceSpreadingPriority:{} ImageLocalityPriority:{} SelectorSpreadPriority:{} EqualPriority:{} BalancedResourceAllocation:{} NodePreferAvoidPodsPriority:{} NodeAffinityPriority:{} TaintTolerationPriority:{}]'
2.2.3. 修改调度程序策略
您可以通过在 openshift-config
项目中创建或编辑调度程序策略配置映射来更改调度行为。在配置映射中添加和移除 predicates 和 priorities,以创建 调度程序策略。
流程
要修改当前的自定义调度,请使用以下方法之一:
编辑调度程序策略配置映射:
$ oc edit configmap <configmap-name> -n openshift-config
例如:
$ oc edit configmap scheduler-policy -n openshift-config apiVersion: v1 data: policy.cfg: | { "kind" : "Policy", "apiVersion" : "v1", "predicates" : [ 1 {"name" : "MaxGCEPDVolumeCount"}, {"name" : "GeneralPredicates"}, {"name" : "MaxAzureDiskVolumeCount"}, {"name" : "MaxCSIVolumeCountPred"}, {"name" : "CheckVolumeBinding"}, {"name" : "MaxEBSVolumeCount"}, {"name" : "MatchInterPodAffinity"}, {"name" : "CheckNodeUnschedulable"}, {"name" : "NoDiskConflict"}, {"name" : "NoVolumeZoneConflict"}, {"name" : "PodToleratesNodeTaints"} ], "priorities" : [ 2 {"name" : "LeastRequestedPriority", "weight" : 1}, {"name" : "BalancedResourceAllocation", "weight" : 1}, {"name" : "ServiceSpreadingPriority", "weight" : 1}, {"name" : "NodePreferAvoidPodsPriority", "weight" : 1}, {"name" : "NodeAffinityPriority", "weight" : 1}, {"name" : "TaintTolerationPriority", "weight" : 1}, {"name" : "ImageLocalityPriority", "weight" : 1}, {"name" : "SelectorSpreadPriority", "weight" : 1}, {"name" : "InterPodAffinityPriority", "weight" : 1}, {"name" : "EqualPriority", "weight" : 1} ] } kind: ConfigMap metadata: creationTimestamp: "2019-09-17T17:44:19Z" name: scheduler-policy namespace: openshift-config resourceVersion: "15370" selfLink: /api/v1/namespaces/openshift-config/configmaps/scheduler-policy
调度程序需要几分钟时间来使用更新后的策略重启 pod。
更改所用的 predicates 和 priorities:
删除调度程序策略配置映射:
$ oc delete configmap -n openshift-config <name>
例如:
$ oc delete configmap -n openshift-config scheduler-policy
根据需要,编辑
policy.cfg
文件来添加和移除 policies 和 predicates。例如:
$ vi policy.cfg
apiVersion: v1 data: policy.cfg: | { "kind" : "Policy", "apiVersion" : "v1", "predicates" : [ {"name" : "MaxGCEPDVolumeCount"}, {"name" : "GeneralPredicates"}, {"name" : "MaxAzureDiskVolumeCount"}, {"name" : "MaxCSIVolumeCountPred"}, {"name" : "CheckVolumeBinding"}, {"name" : "MaxEBSVolumeCount"}, {"name" : "MatchInterPodAffinity"}, {"name" : "CheckNodeUnschedulable"}, {"name" : "NoDiskConflict"}, {"name" : "NoVolumeZoneConflict"}, {"name" : "PodToleratesNodeTaints"} ], "priorities" : [ {"name" : "LeastRequestedPriority", "weight" : 1}, {"name" : "BalancedResourceAllocation", "weight" : 1}, {"name" : "ServiceSpreadingPriority", "weight" : 1}, {"name" : "NodePreferAvoidPodsPriority", "weight" : 1}, {"name" : "NodeAffinityPriority", "weight" : 1}, {"name" : "TaintTolerationPriority", "weight" : 1}, {"name" : "ImageLocalityPriority", "weight" : 1}, {"name" : "SelectorSpreadPriority", "weight" : 1}, {"name" : "InterPodAffinityPriority", "weight" : 1}, {"name" : "EqualPriority", "weight" : 1} ] }
根据调度程序 JSON 文件重新创建调度程序策略配置映射:
$ oc create configmap -n openshift-config --from-file=policy.cfg <configmap-name> 1
- 1
- 输入配置映射的名称。
例如:
$ oc create configmap -n openshift-config --from-file=policy.cfg scheduler-policy configmap/scheduler-policy created
2.2.3.1. 了解调度程序 predicates
predicates 是用于过滤掉不合格节点的规则。
OpenShift Container Platform 中默认提供一些 predicates。其中的一些 predicates 可以通过提供特定参数来自定义。可以组合多个 predicates 来提供更多节点过滤。
2.2.3.1.1. 静态 predicates
此类 predicates 不接受任何来自于用户的配置参数或输入。它们通过其确切的名称在调度程序配置中指定。
2.2.3.1.1.1. 默认 predicates
默认的调度程序策略包括以下 predicates:
NoVolumeZoneConflict
predicate 检查区中是否有 pod 请求的卷。
{"name" : "NoVolumeZoneConflict"}
MaxEBSVolumeCount
predicate 检查可附加到 AWS 实例的最大卷数量。
{"name" : "MaxEBSVolumeCount"}
MaxAzureDiskVolumeCount
predicate 会检查 Azure 磁盘卷的最大数量。
{"name" : "MaxAzureDiskVolumeCount"}
PodToleratesNodeTaints
predicate 检查 pod 是否可以容忍节点污点。
{"name" : "PodToleratesNodeTaints"}
CheckNodeUnschedulable
predicate 会检查 pod 是否可以调度到具有 Unschedulable
规格的节点。
{"name" : "CheckNodeUnschedulable"}
CheckVolumeBinding
predicate 根据卷(它请求的卷)评估 pod 是否可以适合绑定和未绑定 PVC。
- 对于绑定的 PVC, predicate 会检查给定节点是否满足对应 PV 的节点关联性。
- 对于未绑定 PVC,该 predicate 会搜索可满足 PVC 要求且给定节点满足 PV 节点关联性的可用 PV。
如果所有绑定 PVC 都有与节点兼容的 PV,且所有未绑定 PVC 都可与可用并兼容节点的 PV 匹配,该 predicate 会返回 true。
{"name" : "CheckVolumeBinding"}
NoDiskConflict
predicate 检查 pod 请求的卷是否可用。
{"name" : "NoDiskConflict"}
MaxGCEPDVolumeCount
predicate 检查 Google Compute Engine(GCE)持久磁盘(PD)的最大数量。
{"name" : "MaxGCEPDVolumeCount"}
MaxCSIVolumeCountPred
MatchInterPodAffinity
predicate 检查 pod 关联性/反关联性规则是否允许该 pod。
{"name" : "MatchInterPodAffinity"}
2.2.3.1.1.2. 其他静态 predicates
OpenShift Container Platform 还支持下列 predicates:
如果启用了 Taint Nodes By Condition 功能,则无法使用 CheckNode-*
predicates。Taint Nodes By Condition 功能默认启用。
CheckNodeCondition
predicate 检查 pod 是否可以调度到报告 磁盘 不足、网络不可用 或 未就绪 状况的节点。
{"name" : "CheckNodeCondition"}
CheckNodeLabelPresence
predicate 检查节点上是否存在所有指定的标签,而不考虑其值。
{"name" : "CheckNodeLabelPresence"}
checkServiceAffinity
predicate 检查 ServiceAffinity 标签是否对于节点上调度的 pod 来说是相同的。
{"name" : "checkServiceAffinity"}
PodToleratesNodeNoExecuteTaints
predicate 检查 pod 容限是否容忍节点 NoExecute
污点。
{"name" : "PodToleratesNodeNoExecuteTaints"}
2.2.3.1.2. 常规 predicates
下列常规 predicates 检查是否通过非关键 predicates 和必要 predicates 的检查。非关键 predicates 是指只有非关键 pod 必须通过检查的 predicates,而必要 predicates 是指所有 pod 都必须通过检查的 predicates。
默认调度程序策略包含常规 predicates。
非关键常规 predicates
PodFitsResources
predicate 根据资源可用性(CPU、内存和 GPU 等)决定适合性。节点可以声明其资源容量,然后 pod 可以指定它们所需要的资源。适合性基于请求的资源,而非使用的资源。
{"name" : "PodFitsResources"}
必要常规 predicates
PodFitsHostPorts
predicate 决定节点是否有空闲端口用于请求的 pod 端口(不存在端口冲突)。
{"name" : "PodFitsHostPorts"}
HostName
predicate 根据 Host 参数以及与主机名称匹配的字符串来确定适合性。
{"name" : "HostName"}
MatchNodeSelector
predicate 根据 pod 中定义的节点选择器(nodeSelector)查询来确定适合性。
{"name" : "MatchNodeSelector"}
2.2.3.2. 了解调度程序优先级
优先级是根据偏好对节点进行排序的规则。
可以指定一组自定义优先级来配置调度程序。OpenShift Container Platform 中默认提供一些优先级。也可通过提供某些参数来自定义其他优先级。可将多个优先级合并,并为每个优先级赋予不同的权重来影响优先顺序。
2.2.3.2.1. 静态优先级
静态优先级不使用用户提供的配置参数,但权重除外。权重必须指定,且不能为 0 或负数。
这些是在 openshift-config
项目中的调度程序策略配置映射中指定的。
2.2.3.2.1.1. 默认优先级
默认调度程序策略包括以下优先级。每个优先级函数的权重为 1
,但 NodePreferAvoidPodsPriority
除外,它的权重是 10000
。
NodeAffinityPriority
优先级根据节点关联性调度偏好来排列节点的优先顺序
{"name" : "NodeAffinityPriority", "weight" : 1}
TaintTolerationPriority
优先级为 pod 优先选择那些在节点上具有较少 intolerable(不可容忍)污点的节点。不可容忍的污点是具有 PreferNoSchedule
键的污点。
{"name" : "TaintTolerationPriority", "weight" : 1}
ImageLocalityPriority
优先级优先选择已请求的 pod 容器镜像的节点。
{"name" : "ImageLocalityPriority", "weight" : 1}
SelectorSpreadPriority
优先级查找服务、复制控制器(RC)、复制集(RS)和与 pod 匹配的有状态的设置,然后找到与这些选择器匹配的现有 pod。调度程序优先选择具有较少现有匹配 pod 的节点。然后,它会将 pod 调度到具有与所调度 pod 的选择器匹配的 pod 数量最少的节点上。
{"name" : "SelectorSpreadPriority", "weight" : 1}
InterPodAffinityPriority
计算迭代 weightedPodAffinityTerm
元素,并在节点满足对应的 PodAffinityTerm 时加上权重的总和。总和最高的节点是优先级最高的节点。
{"name" : "InterPodAffinityPriority", "weight" : 1}
LeastRequestedPriority
优先级会优先选择请求资源较少的节点。它计算节点上调度的 pod 所请求的内存和 CPU 百分比,并优先选择可用/剩余容量最高的节点。
{"name" : "LeastRequestedPriority", "weight" : 1}
BalancedResourceAllocation
优先级会优先选择资源使用率均衡的节点。它以占容量比形式计算 CPU 和内存已使用量的差值,并基于两个指标相互接近的程度来优先选择节点。这应该始终与 LeastRequestedPriority
一同使用。
{"name" : "BalancedResourceAllocation", "weight" : 1}
NodePreferAvoidPodsPriority
优先级忽略复制控制器以外的控制器拥有的 pod。
{"name" : "NodePreferAvoidPodsPriority", "weight" : 10000}
2.2.3.2.1.2. 其他静态优先级
OpenShift Container Platform 还支持下列优先级:
如果没有提供优先级配置,则会为所有节点都提供一个权重为 1
优先级。建议您仅在测试环境中使用此优先级。
的
EqualPriority
{"name" : "EqualPriority", "weight" : 1}
MostRequestedPriority
优先级会优先选择具有最多请求资源的节点。它计算节点上调度的 pod 所请求的内存与 CPU 百分比,并根据请求量对容量的平均占比的最大值来排列优先级。
{"name" : "MostRequestedPriority", "weight" : 1}
ServiceSpreadingPriority
优先级通过将属于同一服务的 pod 数量最大化到同一台机器来分散 pod。
{"name" : "ServiceSpreadingPriority", "weight" : 1}
2.2.3.2.2. 可配置优先级
您可以在 openshift-config
项目的调度程序策略的配置映射中配置这些优先级,以添加可影响优先级的标记(label)。
优先级函数的类型由它们所使用的参数来标识。由于它们是可配置的,因此可以组合类型相同(但配置参数不同)的多个优先级,但前提是它们的用户定义名称不同。
有关使用这些优先级的详情,请参考“修改调度程序策略”。
ServiceAntiAffinity
接受一个标签,确保将属于同一服务的 pod 正常地分散到基于标签值的一组节点。它为指定标签值相同的所有节点赋予相同的分数。它将较高的分数给予组内 pod 密度最低的节点。
{ "kind": "Policy", "apiVersion": "v1", "priorities":[ { "name":"<name>", 1 "weight" : 1 2 "argument":{ "serviceAntiAffinity":{ "label": "<label>" 3 } } } ] }
例如:
{ "kind": "Policy", "apiVersion": "v1", "priorities": [ { "name":"RackSpread", "weight" : 1, "argument": { "serviceAntiAffinity": { "label": "rack" } } } ] }
在某些情况下,基于自定义标签的 ServiceAntiAffinity
参数不能按预期分散 pod。请参考此红帽解决方案。
labelPreference
参数根据指定的标签赋予优先级。如果节点上存在该标签,则该节点被赋予优先级。如果未指定标签,则为没有标签的节点赋予优先级。如果设置了带 labelPreference
参数的多个优先级,则所有优先级都必须具有相同的权重。
{ "kind": "Policy", "apiVersion": "v1", "priorities":[ { "name":"<name>", 1 "weight" : 1 2 "argument":{ "labelPreference":{ "label": "<label>", 3 "presence": true 4 } } } ] }
2.2.4. 策略配置示例
如果使用调度程序策略文件进行了指定,则如下配置会指定默认的调度程序配置。
{ "kind": "Policy", "apiVersion": "v1", "predicates": [ { "name": "RegionZoneAffinity", 1 "argument": { "serviceAffinity": { 2 "labels": ["region, zone"] 3 } } } ], "priorities": [ { "name":"RackSpread", 4 "weight" : 1, "argument": { "serviceAntiAffinity": { 5 "label": "rack" 6 } } } ] }
在下方的所有示例配置中,predicates 和 priorities 函数的列表都已截断,仅包含与指定用例相关的内容。在实践中,完整/有意义的调度程序策略应当包含前文所述的大部分(若非全部)默认 predicates 和 priorities。
以下示例定义了三个拓扑级别,即 region(关联性)-> zone(关联性)-> rack(反关联性):
{ "kind": "Policy", "apiVersion": "v1", "predicates": [ { "name": "RegionZoneAffinity", "argument": { "serviceAffinity": { "labels": ["region, zone"] } } } ], "priorities": [ { "name":"RackSpread", "weight" : 1, "argument": { "serviceAntiAffinity": { "label": "rack" } } } ] }
以下示例定义了三个拓扑级别,即 city
(affinity) building
(anti-affinity) room
(anti-affinity):
{ "kind": "Policy", "apiVersion": "v1", "predicates": [ { "name": "CityAffinity", "argument": { "serviceAffinity": { "label": "city" } } } ], "priorities": [ { "name":"BuildingSpread", "weight" : 1, "argument": { "serviceAntiAffinity": { "label": "building" } } }, { "name":"RoomSpread", "weight" : 1, "argument": { "serviceAntiAffinity": { "label": "room" } } } ] }
以下示例定义了一个策略,以仅使用定义了“region”标签的节点,并且优先选择定义有“zone”标签的节点:
{ "kind": "Policy", "apiVersion": "v1", "predicates": [ { "name": "RequireRegion", "argument": { "labelPreference": { "labels": ["region"], "presence": true } } } ], "priorities": [ { "name":"ZonePreferred", "weight" : 1, "argument": { "labelPreference": { "label": "zone", "presence": true } } } ] }
以下示例组合使用静态和可配置的 predicates 和 priorities:
{ "kind": "Policy", "apiVersion": "v1", "predicates": [ { "name": "RegionAffinity", "argument": { "serviceAffinity": { "labels": ["region"] } } }, { "name": "RequireRegion", "argument": { "labelsPresence": { "labels": ["region"], "presence": true } } }, { "name": "BuildingNodesAvoid", "argument": { "labelsPresence": { "label": "building", "presence": false } } }, {"name" : "PodFitsPorts"}, {"name" : "MatchNodeSelector"} ], "priorities": [ { "name": "ZoneSpread", "weight" : 2, "argument": { "serviceAntiAffinity":{ "label": "zone" } } }, { "name":"ZonePreferred", "weight" : 1, "argument": { "labelPreference":{ "label": "zone", "presence": true } } }, {"name" : "ServiceSpreadingPriority", "weight" : 1} ] }