En las cargas de trabajo de entrenamiento de IA distribuida y big data, los pods se comunican con frecuencia. El programador predeterminado de Kubernetes distribuye los pods uniformemente entre los nodos, por lo que estos pueden terminar en nodos separados por múltiples saltos de switch; cada salto adicional aumenta la latencia y reduce el throughput. La programación consciente de la topología de red resuelve este problema al asignar todos los pods de un trabajo a nodos dentro del mismo dominio de reenvío de Capa 1 o Capa 2, lo que minimiza los saltos de switch y acelera la finalización del trabajo.
Funcionamiento
La programación consciente de la topología de red utiliza un algoritmo voraz para asignar trabajos a nodos Lingjun con la menor extensión topológica posible.
Los clústeres ACK Lingjun tienen una topología de red de dos niveles:
Access Switch (ASW): es la interfaz directa para los nodos Lingjun. Los nodos bajo el mismo ASW requieren como mínimo un salto para comunicarse.
Point of Delivery (Pod): es la topología de nivel superior que agrupa varios ASW. Los nodos bajo diferentes ASW en el mismo Pod requieren como mínimo dos saltos.
Cuantas menos capas de switch existan entre los nodos, menor será la latencia. El programador intenta ubicar primero todos los pods dentro del nivel topológico más bajo posible:
Trabajo de 2 nodos: se asigna a nodos dentro del mismo ASW (por ejemplo, par de nodos A-B o E-F).
Trabajo de 4 nodos: se asigna a nodos dentro del mismo Pod (por ejemplo, par de nodos A-D o E-H).
Las etiquetas de nodo indican a qué ASW y Pod pertenece cada nodo:
alibabacloud.com/asw-id: identifica el ASW.alibabacloud.com/point-of-delivery: identifica el Pod.
En los clústeres ACK Lingjun, el componente lingjun-networktopology-collector recopila automáticamente esta información y aplica estas etiquetas a los nodos Lingjun. Para otros tipos de nodos o clústeres, agregue las etiquetas manualmente y asegúrese de que las claves de etiqueta coincidan con los valores de labelKey en su configuración de ClusterNetworkTopology.
Estrategias de programación
Dos estrategias controlan qué tan estrictamente se agrupan los pods dentro de un nivel topológico:
|
Estrategia |
Comportamiento |
|
|
Agrupa los pods dentro de la capa cuando es posible; permite la programación entre capas si los recursos son insuficientes |
|
|
Requiere que todos los pods estén dentro de la misma capa; la programación falla si ninguna capa individual puede alojar el trabajo |
Requisitos previos
Antes de empezar, asegúrese de contar con lo siguiente:
Un clúster ACK Lingjun
kubectl configurado para conectarse al clúster
Nodos suficientes con etiquetas de topología aplicadas
Configuración y despliegue de la programación consciente de la topología de red
Configurar la programación consciente de la topología de red requiere tres archivos YAML: uno que define la estructura de topología a nivel de clúster, otro que declara las restricciones de programación para un trabajo específico y uno para el trabajo en sí.
Paso 1: Definir la topología del clúster
Cree el archivo cluster-network-topology.yaml para declarar la estructura de topología de dos niveles del clúster:
apiVersion: scheduling.koordinator.sh/v1alpha1
kind: ClusterNetworkTopology
metadata:
# Keep unchanged.
name: default
spec:
networkTopologySpec:
# parentTopologyLayer declares the upper topology structure.
- parentTopologyLayer: ASWTopologyLayer
# The lowest level must be NodeTopologyLayer for Lingjun nodes.
topologyLayer: NodeTopologyLayer
# The following defines the cross-layer topology mapping. Normally, no modification is needed.
- labelKey:
- alibabacloud.com/point-of-delivery
topologyLayer: PoDTopologyLayer
- labelKey:
- alibabacloud.com/asw-id
parentTopologyLayer: PoDTopologyLayer
topologyLayer: ASWTopologyLayer
Paso 2: Declarar las restricciones de topología del trabajo
Cree el archivo sample-network-topology.yaml para especificar la estrategia de programación de cada nivel topológico:
apiVersion: scheduling.koordinator.sh/v1alpha1
kind: JobNetworkTopology
metadata:
labels:
network-topology-permit-wait-time: "999999"
# The job name.
name: sample-network-topology
# The namespace the job belongs to.
namespace: sample-network-topology
spec:
topologyStrategy:
# PreferGather: allows cross-ASW scheduling when resources are insufficient.
- layer: ASWTopologyLayer
strategy: PreferGather
- layer: NodeTopologyLayer
strategy: PreferGather
# MustGather: cross-Pod scheduling is not allowed.
- layer: PoDTopologyLayer
strategy: MustGather
# Must match spec.parallelism and pod-group.scheduling.sigs.k8s.io/min-available in the Job manifest.
workerNum: 2
Paso 3: Crear el trabajo
Cree el archivo pi.yaml para definir el trabajo y hacer referencia a las restricciones de topología:
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
# Must match workerNum in JobNetworkTopology.
parallelism: 2
template:
metadata:
labels:
# Must match workerNum in JobNetworkTopology.
pod-group.scheduling.sigs.k8s.io/min-available: "2"
pod-group.scheduling.sigs.k8s.io/name: sample-gang
# Reference the JobNetworkTopology created in Step 2.
network-topology-job-name: sample-network-topology
network-topology-job-namespace: sample-network-topology
spec:
schedulerName: default-scheduler
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
resources:
limits:
# This example uses a single GPU. Adjust as needed.
nvidia.com/gpu: 1
restartPolicy: Never
backoffLimit: 4
Paso 4: Desplegar la configuración
Aplique los tres archivos al clúster:
kubectl apply -f cluster-network-topology.yaml
kubectl apply -f sample-network-topology.yaml
kubectl apply -f pi.yaml
Comprobación de los resultados de programación
Verificar la topología del clúster
Obtenga las etiquetas de topología de sus nodos para confirmar la estructura topológica:
# Network topology:
# test-pod-1 test-pod-2
# / | \ |
# test-1 test-2 test-3 test-4
# / \ | | |
# 0.12 0.14 0.15 0.16 0.17
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"}'
Salida esperada:
{
"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"
}
Escenario 1: Trabajo de 2 pods
Con workerNum: 2, ambos pods se programan en nodos dentro del mismo ASW (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>
Ambos pods se ubican en test-1 (ASW) y comparten la ruta de red con el menor número de saltos.
Escenario 2: Trabajo de 4 pods
Actualice parallelism y pod-group.scheduling.sigs.k8s.io/min-available en pi.yaml, así como workerNum en sample-network-topology.yaml a 4. Vuelva a aplicar ambos archivos.
El programador asigna los 4 pods dentro de test-pod-1, ya que MustGather en PoDTopologyLayer impide la programación entre Pods. El nodo bajo test-pod-2 permanece sin programar:
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>
Escenario 3: Trabajo de 5 pods (fallo de programación)
Actualice los mismos parámetros a 5. El trabajo falla porque test-pod-1 solo tiene 4 espacios disponibles y MustGather en PoDTopologyLayer prohíbe usar el único nodo en test-pod-2.
Todos los pods permanecen en estado Pending:
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
Inspeccione el mensaje de fallo de programación:
kubectl get pod -ojson | jq '.items[].status'
El mensaje de fallo de programación contiene:
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...
Campos clave en el mensaje:
|
Campo |
Valor |
Significado |
|
|
|
El programador necesita 5 espacios dentro de un solo dominio de nivel Pod |
|
|
|
|
|
|
|
|
|
|
— |
No existe ningún plan de topología válido; la programación está bloqueada |
Para resolver esto, reduzca workerNum para que quepa dentro de un solo Pod, o cambie la estrategia de PoDTopologyLayer de MustGather a PreferGather para permitir la programación entre Pods.