在容器服务 Kubernetes 版中,可以结合Gang调度能力以及拓扑感知调度能力,实现Pod在多个拓扑域中重试,直到找到一个能够满足整个作业拓扑域的功能。更进一步,可以结合容器服务 Kubernetes 版的节点池以及ECS的部署集能力,将Pod调度到属于同一低延时部署集的ECS中,实现低维度拓扑域的亲和调度。本文介绍如何实现拓扑感知调度。
拓扑感知调度介绍
在机器学习或大数据分析类作业中,Pod与Pod间通常有较大的网络通信需求。默认情况下,原生Kubernetes调度器会将Pod均匀打散在集群的每台机器上,这样会增大Pod间的通信距离,导致作业完成时间变长。
为了优化此类作业的执行时间,一种有效办法是将Pod尽量部署在同一个可用区,甚至在同一个机架上,通过减少Pod之间通信的跳数减少Pod间网络通信时延,从而减少作业执行时间。目前Kubernetes原生支持了通过NodeAffinity以及PodAffinity的方式实现Pod亲和调度,然而原生的节点或Pod亲和调度方式存在以下的弊端。
Pod无法做到在多个不同的拓扑域上进行重试。在原生Kubernetes亲和调度策略中,作业会以其中第一个Pod的调度结果为准进行调度。若第一个Pod的调度位置无法满足所有Pod的需求,则部分Pod将会处于Pending状态,且后续调度时即使有其他拓扑域能够满足整个作业需求也不会进行可用区的变更。
当前节点上仅有可用区级别的标签,即Pod调度时仅能够调度亲和到同一个可用区下,而无法做到更进一步的亲和到更低维度的拓扑域中。
在容器服务 Kubernetes 版中,可以结合Gang调度能力以及拓扑感知调度能力,实现Pod在多个拓扑域中重试,直到找到一个能够满足整个作业拓扑域的功能。更进一步,可以结合容器服务 Kubernetes 版的节点池以及ECS的部署集能力,将Pod调度到属于同一低延时部署集的ECS中,实现低维度拓扑域的亲和调度。
Pod在多个拓扑域中重试
容器服务 Kubernetes 版调度器kube-scheduler可以通过为作业添加Gang调度标识来限制Pod必须同时获得需求的资源,并通过保存之前的调度结果来实现在多个拓扑域中进行重试的效果。启用该功能需要在Pod上同时添加以下标记。
在Pod Label中添加Gang调度标识。关于Gang调度的信息,请参见使用Gang scheduling。
labels: pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu # tf-smoke-gpu是PodGroup的名称,需要自定义。 pod-group.scheduling.sigs.k8s.io/min-available: "3" # 该值可以保持与作业的Pod数相等。
在Pod的Annotation中添加拓扑调度约束。
annotations: alibabacloud.com/topology-aware-constraint: {\"name\":\"test\",\"required\":{\"topologies\":[{\"key\":\"kubernetes.io/hostname\"}],\"nodeSelectors\":[{\"matchLabels\":{\"test\":\"abc\"}}]}}
alibabacloud.com/topology-aware-constraint
的值必须是一个合法的JSON字符串,否则将无法正常解析拓扑调度约束,同时该值需要满足以下结构。{ "name": xxx # 任意的名称。 "required": { "topologies": [ { "key": xxx # 需要亲和的拓扑域的Key。 } ], "nodeSelectors": [ { # 该结构可以参考kubernetes原生的nodeaffinity中labelSelector的结构。 "matchLabels": {}, "matchExpressions": {} } ] } }
在设定了上述例子后,所有的Pod将会在满足
test=abc
的所有节点中寻找一个能够调度所有带有pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu
标签的Pod。最终结果如下所示:> capacity k get po -ojson | jq '.items[] | {"name":.metadata.name,"ann":.metadata.annotations["alibabacloud.com/topology-aware-constraint"], "node": spec.nodeName}' { "name": "nginx-deployment-basic-69f47fc6db-6****" "ann": "{\"name\": \"test\", \"required\": {\"topologies\":[{\"key\": \"kubernetes.io/hostname\"}], \"nodeSelectors\": [{\"matchLabels\": {\"test\": \"a\"}}]}} " "node": "cn-shenzhen.10.0.2.4" } { "name":"nginx-deployment-basic-69f47fc6db-h****" "ann": "{\"name\": \"test\", \"required\": {\"topologies\":[{\"key\": \"kubernetes.io/hostname\"}], \"nodeSelectors\": [{\"matchLabels\": {\"test\": \"a\"}}]}} " "node": "cn-shenzhen.10.0.2.4" } { "name":"nginx-deployment-basic-69f47fc6db-j****" "ann": "{\"name\": \"test\", \"required\": {\"topologies\":[{\"key\": \"kubernetes.io/hostname\"}], \"nodeSelectors\": [{\"matchLabels\": {\"test\": \"a\"}}]}} " "node": "cn-shenzhen.10.0.2.4" } { "name":"nainx-deployment-basic-69f47fc6db-I****" "ann": "{\"name\": \"test\", \"required\": {\"topologies\":[{\"key\": \"kubernetes.io/hostname\"}], \"nodeSelectors\": [{\"matchLabels\": {\"test\": \"a\"}}]}} " "node": "cn-shenzhen.10.0.2.4" } { "name" :"nainx-deplovment-basic-69f47fc6db-x****" "ann": "{\"name\": \"test\", \"required\": {\"topologies\":[{\"key\": \"kubernetes.io/hostname\"}], \"nodeSelectors\": [{\"matchLabels\": {\"test\": \"a\"}}]}} " "node": "cn-shenzhen.10.0.2.4" }
将Pod调度到同一低延时部署集中
在一些特定场景中,仅仅在某一可用区中亲和并不能满足作业的需求,作业需要在更低维度的拓扑域中亲和来达成更优的性能。ECS提供了部署集的功能,您可以通过申明低延时部署集来限制ECS节点的创建位置。关于在ACK中使用部署集的相关信息,请参见节点池部署集最佳实践。
当需要创建关联一个低延时部署集的节点池时,您还需要在节点池的节点标签中添加您自定义的节点标签,用于将低延时部署集的节点池与其他节点池做区分,如下图所示。
完成上述步骤后,您可以通过以下Annotation以及Label来申明一个低延时部署集中调度的作业。
在Pod Label中添加Gang调度标识。关于Gang调度的信息,请参见使用Gang scheduling。
labels: pod-group.scheduling.sigs.k8s.io/name: xxx # xxx是PodGroup的名称,需要自定义。 pod-group.scheduling.sigs.k8s.io/min-available: "x" # 该值可以保持与作业的Pod数相等。
在Pod的Annotation中添加拓扑调度约束。
重要该例子中的
matchLabels
需要更换成您自定义的低延时部署集的节点标签,name
也需要按需修改。annotations: alibabacloud.com/topology-aware-constraint: {\"name\":\"test\",\"required\":{\"topologies\":[{\"key\":\"alibabacloud.com/nodepool-id\"}],\"nodeSelectors\":[{\"matchLabels\":{\"np-type\":\"low-latency\"}}]}}