在机器学习或大数据分析类作业中,Pod与Pod间通常有较大的网络通信需求。在默认情况下原生Kubernetes调度器会将Pod均匀打散在集群的每台机器上,但是这样会增大Pod间的通信距离,导致作业完成时间变长。在灵骏集群中可以通过网络拓扑感知调度,将Pod声明调度到相同的一层转发域或二层转发域下,以此减少机器间的网络通信时延,进而缩短作业完成时间。
方案概述
网络拓扑感知调度通过贪心策略将任务放置在跨越更少拓扑的节点上。
假设当前灵骏集群中存在Pod和ASW两层网络拓扑,ASW为灵骏机器的直接接口,Pod为范围更大的网络拓扑,所以跨灵骏节点的网络传输需要至少1跳的网络转发,跨ASW的网络传输需要至少2跳的网络转发。
若您提交了一个需要两台节点的任务,网络拓扑感知调度会将任务放置在Node A-B或Node E-F上。
若您提交了一个需要四台节点的任务,网络拓扑感知调度会将任务放置在Node A-D或Node E-H上。
在ACK灵骏集群中,如何判断节点之间是否在相同的ASW下或相同的Pod下呢?
您可以查看节点上已配置包含网络位置信息的标签alibabacloud.com/asw-id
和alibabacloud.com/point-of-delivery
。
声明配置拓扑结构
在灵骏集群中使用网络拓扑感知调度,需要定义集群级别的拓扑调度需求,最后在任务中标识网络拓扑感知调度信息。
创建
cluster-network-topology.yaml
声明两级的拓扑结构。创建
sample-network-topology.yaml
声明任务中的网络拓扑感知调度需求。创建
pi.yaml
标识网络拓扑感知调度信息。提交任务时需要指定关联的
JobNetworkTopology
信息。执行命令在集群中部署上述YAML文件。
kubectl apply -f cluster-network-topology.yaml kubectl apply -f sample-network-topology.yaml kubectl apply -f pi.yaml
调度效果展示
通过kubectl连接集群获取当前网络拓扑结构信息。
在灵骏集群中,集群中组件lingjun-networktopology-collector从节点上采集并以label的方式标记在节点上。
而对于其他节点或其他类型集群,需要您手动标记label,标记时使用的labelkey需要与上文中ClusterNetworkTopology中的labelKey对应。
# network topology is like: # test-pod-1 test-pod-2 # / | \ | # test-1 test-2 test-3 test-4 # / \ | | | # 0.12 0.14 0.15 0.16 0.17 ➜ network kubectl get no -l alibabacloud.com/asw-id,alibabacloud.com/point-of-delivery -ojson | jq '.items[] | {"Name":.metadata.name, "ASW":.metadata.labels."alibabacloud.com/asw-id", "POD":.metadata.labels."alibabacloud.com/point-of-delivery"}' { "Name": "cn-hongkong.10.1.0.12", "ASW": "test-1", "POD": "test-pod-1" } { "Name": "cn-hongkong.10.1.0.14", "ASW": "test-1", "POD": "test-pod-1" } { "Name": "cn-hongkong.10.1.0.15", "ASW": "test-2", "POD": "test-pod-1" } { "Name": "cn-hongkong.10.1.0.16", "ASW": "test-3", "POD": "test-pod-1" } { "Name": "cn-hongkong.10.1.0.17", "ASW": "test-4", "POD": "test-pod-2" }
提交上文中配置的Job任务,可以看到任务运行在test-1下的两个节点上。
➜ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pi-8p89l 1/1 Running 0 4s 172.30.240.197 cn-hongkong.10.1.0.14 <none> <none> pi-p8swv 0/1 ContainerCreating 0 4s <none> cn-hongkong.10.1.0.12 <none> <none>
若将任务中的Pod数量设置为4。
(需要同时更改上文Job的parallelism以及label中的pod-group.scheduling.sigs.k8s.io/min-available和JobNetworkTopology的workerNum),可以看到只有test-pod-2上的节点没有被调度。
➜ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pi-2kwq9 1/1 Running 0 4s 172.30.241.123 cn-hongkong.10.1.0.12 <none> <none> pi-87hm5 0/1 ContainerCreating 0 4s <none> cn-hongkong.10.1.0.16 <none> <none> pi-bsvx8 1/1 Running 0 4s 172.30.240.198 cn-hongkong.10.1.0.14 <none> <none> pi-dvwhl 0/1 ContainerCreating 0 4s <none> cn-hongkong.10.1.0.15 <none> <none>
若将任务中的Pod数量设置为5。
(需要同时更改上文Job的parallelism以及label中的pod-group.scheduling.sigs.k8s.io/min-available和JobNetworkTopology的workerNum),可以看到任务调度失败,其中第一个调度的Pod中带有调度失败信息,其中说明了调度失败是由于跨Pod调度被禁止(
all fail topology paths by MustGather reason: [path:RootNode->test-pod-1, freeSlotNum:4], [path:RootNode->DefaultTopologyName, freeSlotNum:0], [path:RootNode->test-pod-2, freeSlotNum:1]
)。➜ kubectl get pod NAME READY STATUS RESTARTS AGE pi-75qf5 0/1 Pending 0 2s pi-8k4nd 0/1 Pending 0 2s pi-b2pmc 0/1 Pending 0 2s pi-n7c2b 0/1 Pending 0 2s pi-wf4zn 0/1 Pending 0 2s ➜ kubectl get pod -ojson | jq '.items[].status' { "conditions": [ { "lastProbeTime": null, "lastTransitionTime": "2024-05-29T07:46:27Z", "message": "0/6 nodes are available: 1 Insufficient nvidia.com/gpu, 1 [NetworkTopology begin] cluster total nodes:6, 5 node provide 5 freeSlot, 1 node unavailable cause Insufficient nvidia.com/gpu, job desireNum:5, all fail topology paths by MustGather reason: [path:RootNode->test-pod-1, freeSlotNum:4], [path:RootNode->DefaultTopologyName, freeSlotNum:0], [path:RootNode->test-pod-2, freeSlotNum:1] [NetworkTopology end], 4 NetworkTopology bestPlan empty. network topology job sample-network-topology/sample-network-topology gets rejected due to pod is unschedulable, preemption: 0/6 nodes are available: 1 No victims found on node cn-hongkong.10.1.0.10 for preemptor pod pi-75qf5, 5 Preemption is not helpful for scheduling..", "reason": "Unschedulable", "status": "False", "type": "PodScheduled" } ], "phase": "Pending", "qosClass": "BestEffort" } { "phase": "Pending", "qosClass": "BestEffort" } { "phase": "Pending", "qosClass": "BestEffort" } { "phase": "Pending", "qosClass": "BestEffort" } { "phase": "Pending", "qosClass": "BestEffort" }