云服务器内容精选

  • 使用Volcano设置NUMA亲和性调度 以下为使用Volcano设置NUMA亲和性调度的示例。 示例一:在无状态工作负载中配置NUMA亲和性。 kind: Deployment apiVersion: apps/v1 metadata: name: numa-tset spec: replicas: 1 selector: matchLabels: app: numa-tset template: metadata: labels: app: numa-tset annotations: volcano.sh/numa-topology-policy: single-numa-node # set the topology policy spec: containers: - name: container-1 image: nginx:alpine resources: requests: cpu: 2 # 必须为整数,且需要与limits中一致 memory: 2048Mi limits: cpu: 2 # 必须为整数,且需要与requests中一致 memory: 2048Mi imagePullSecrets: - name: default-secret 示例二:创建一个Volcano Job,并使用NUMA亲和性。 apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: vj-test spec: schedulerName: volcano minAvailable: 1 tasks: - replicas: 1 name: "test" topologyPolicy: best-effort # set the topology policy for task template: spec: containers: - image: alpine command: ["/bin/sh", "-c", "sleep 1000"] imagePullPolicy: IfNotPresent name: running resources: limits: cpu: 20 memory: "100Mi" restartPolicy: OnFailure NUMA调度分析。 假设NUMA节点情况如下: 工作节点 节点策略拓扑管理器策略 NUMA 节点 0 上的可分配 CPU NUMA 节点 1 上的可分配 CPU node-1 single-numa-node 16U 16U node-2 best-effort 16U 16U node-3 best-effort 20U 20U 则根据以上示例, 示例一中,Pod的CPU申请值为2U,设置拓扑策略为“single-numa-node”,因此会被调度到相同策略的node-1。 示例二中,Pod的CPU申请值为20U,设置拓扑策略为“best-effort”,它将被调度到node-3,因为node-3可以在单个NUMA节点上分配Pod的CPU请求,而node-2需要在两个NUMA节点上执行此操作。
  • Volcano开启NUMA亲和性调度 开启静态(static)CPU管理策略,具体请参考 开启CPU管理策略。 配置CPU拓扑策略。 登录CCE控制台,单击集群名称进入集群,在左侧选择“节点管理”,在右侧选择“节点池”页签,单击节点池名称后的“ 配置管理”。 将kubelet的拓扑管理策略(topology-manager-policy)的值修改为需要的CPU拓扑策略即可。 有效拓扑策略为“none”、“best-effort”、“restricted”、“single-numa-node”,具体策略对应的调度行为请参见Pod调度预测。 开启numa-aware插件功能和resource_exporter功能。 Volcano 1.7.1及以上版本 登录CCE控制台,单击集群名称进入集群,单击左侧导航栏的“插件中心”,在右侧找到Volcano,单击“编辑”,并在“参数配置”中设置Volcano调度器配置参数。 { "ca_cert": "", "default_scheduler_conf": { "actions": "allocate, backfill, preempt", "tiers": [ { "plugins": [ { "name": "priority" }, { "name": "gang" }, { "name": "conformance" } ] }, { "plugins": [ { "name": "drf" }, { "name": "predicates" }, { "name": "nodeorder" } ] }, { "plugins": [ { "name": "cce-gpu-topology-predicate" }, { "name": "cce-gpu-topology-priority" }, { "name": "cce-gpu" }, { // add this also enable resource_exporter "name": "numa-aware", // the weight of the NUMA Aware Plugin "arguments": { "weight": "10" } } ] }, { "plugins": [ { "name": "nodelocalvolume" }, { "name": "nodeemptydirvolume" }, { "name": "nodeCSIscheduling" }, { "name": "networkresource" } ] } ] }, "server_cert": "", "server_key": "" } Volcano 1.7.1以下版本 Volcano插件开启resource_exporter_enable参数,用于收集节点numa拓扑信息。 { "plugins": { "eas_service": { "availability_zone_id": "", "driver_id": "", "enable": "false", "endpoint": "", "flavor_id": "", "network_type": "", "network_virtual_subnet_id": "", "pool_id": "", "project_id": "", "secret_name": "eas-service-secret" } }, "resource_exporter_enable": "true" } 开启后可以查看当前节点的numa拓扑信息。 kubectl get numatopo NAME AGE node-1 4h8m node-2 4h8m node-3 4h8m 启用Volcano numa-aware算法插件。 kubectl edit cm -n kube-system volcano-scheduler-configmap kind: ConfigMap apiVersion: v1 metadata: name: volcano-scheduler-configmap namespace: kube-system data: default-scheduler.conf: |- actions: "allocate, backfill, preempt" tiers: - plugins: - name: priority - name: gang - name: conformance - plugins: - name: overcommit - name: drf - name: predicates - name: nodeorder - plugins: - name: cce-gpu-topology-predicate - name: cce-gpu-topology-priority - name: cce-gpu - plugins: - name: nodelocalvolume - name: nodeemptydirvolume - name: nodeCSIscheduling - name: networkresource arguments: NetworkType: vpc-router - name: numa-aware # add it to enable numa-aware plugin arguments: weight: 10 # the weight of the NUMA Aware Plugin
  • 确认NUMA使用情况 您可以通过lscpu命令查看当前节点的CPU概况: # 查看当前节点的CPU概况 lscpu ... CPU(s): 32 NUMA node(s): 2 NUMA node0 CPU(s): 0-15 NUMA node1 CPU(s): 16-31 然后查看NUMA节点使用情况。 # 查看当前节点的CPU分配 cat /var/lib/kubelet/cpu_manager_state {"policyName":"static","defaultCpuSet":"0,10-15,25-31","entries":{"777870b5-c64f-42f5-9296-688b9dc212ba":{"container-1":"16-24"},"fb15e10a-b6a5-4aaa-8fcd-76c1aa64e6fd":{"container-1":"1-9"}},"checksum":318470969} 以上示例中表示,节点上运行了两个容器,一个占用了NUMA node0的1-9核,另一个占用了NUMA node1的16-24核。
  • Pod调度预测 当Pod设置了拓扑策略时,Volcano会根据Pod设置的拓扑策略预测匹配的节点列表。调度过程如下: 根据Pod设置的Volcano拓扑策略,筛选具有相同策略的节点。Volcano提供的拓扑策略与拓扑管理器相同。 在设置了相同策略的节点中,筛选CPU拓扑满足该策略要求的节点进行调度。 Volcano拓扑策略 节点调度行为 1.筛选具有相同策略的节点 2.节点的CPU拓扑满足该策略的要求 none 无筛选行为: none:可调度 best-effort:可调度 restricted:可调度 single-numa-node:可调度 - best-effort 筛选拓扑策略同样为“best-effort”的节点: none:不可调度 best-effort:可调度 restricted:不可调度 single-numa-node:不可调度 尽可能满足策略要求进行调度: 优先调度至单NUMA节点,如果单NUMA节点无法满足CPU申请值,允许调度至多个NUMA节点。 restricted 筛选拓扑策略同样为“restricted”的节点: none:不可调度 best-effort:不可调度 restricted:可调度 single-numa-node:不可调度 严格限制的调度策略: 单NUMA节点的CPU容量上限大于等于CPU的申请值时,仅允许调度至单NUMA节点。此时如果单NUMA节点剩余的CPU可使用量不足,则Pod无法调度。 单NUMA节点的CPU容量上限小于CPU的申请值时,可允许调度至多个NUMA节点。 single-numa-node 筛选拓扑策略同样为“single-numa-node”的节点: none:不可调度 best-effort:不可调度 restricted:不可调度 single-numa-node:可调度 仅允许调度至单NUMA节点。 假设单个节点CPU总量为32U,由2个NUMA节点提供资源,分配如下: 工作节点 节点拓扑策略 NUMA节点1上的CPU总量 NUMA节点2上的CPU总量 节点-1 best-effort 16 16 节点-2 restricted 16 16 节点-3 restricted 16 16 节点-4 single-numa-node 16 16 Pod设置拓扑策略后,调度情况如图1所示。 当Pod的CPU申请值为9U时,设置拓扑策略为“best-effort”,Volcano会匹配拓扑策略同样为“best-effort”的节点-1,且该策略允许调度至多个NUMA节点,因此9U的申请值会被分配到2个NUMA节点,该Pod可成功调度至节点-1。 当Pod的CPU申请值为9U时,设置拓扑策略为“restricted”,Volcano会匹配拓扑策略同样为“restricted”的节点-2/节点-3,且单NUMA节点CPU总量满足9U的申请值,但单NUMA节点剩余可用的CPU量无法满足,因此该Pod无法调度。 当Pod的CPU申请值为17U时,设置拓扑策略为“restricted”,Volcano会匹配拓扑策略同样为“restricted”的节点-2/节点-3,且单NUMA节点CPU总量无法满足17U的申请值,可允许分配到2个NUMA节点,该Pod可成功调度至节点-3。 当Pod的CPU申请值为17U时,设置拓扑策略为“single-numa-node”,Volcano会匹配拓扑策略同样为“single-numa-node”的节点,但由于单NUMA节点CPU总量均无法满足17U的申请值,因此该Pod无法调度。 图1 NUMA调度策略对比
  • 背景信息 当节点运行许多CPU绑定的Pod时,工作负载可以迁移到不同的CPU核心,这取决于Pod是否被限制以及调度时哪些CPU核心可用。许多工作负载对此迁移不敏感,因此在没有任何干预的情况下工作正常。但是,在CPU缓存亲和性和调度延迟显著影响工作负载性能的工作负载中,如果CPU是从不同的NUMA节点分配的,会导致额外的延迟。因此kubelet允许使用拓扑管理器(Topology Manager)替代CPU管理策略来确定节点的分配。 CPU Manager和拓扑管理器都是kubelet组件,但有以下限制: K8s默认调度器不感知NUMA拓扑。因此,可能会调度到不满足NUMA拓扑要求的节点上,然后工作负载实例启动失败。这对于Tensorflow作业来说是不可接受的。如果节点上有任何工作进程或ps失败,则作业将失败。 管理器是节点级的,导致无法匹配整个集群中NUMA拓扑的最佳节点。 Volcano的目标是解决调度程序NUMA拓扑感知的限制,以便实现以下目标: 避免将Pod调度到NUMA拓扑不匹配的节点。 将Pod调度到NUMA拓扑的最佳节点。 更多资料请查看社区NUMA亲和性插件指导链接:https://github.com/volcano-sh/volcano/blob/master/docs/design/numa-aware.md
  • 调度优先级 不管是什么拓扑策略,都是希望把Pod调度到当时最优的节点上,这里通过给每一个节点进行打分的机制来排序筛选最优节点。 原则:尽可能把Pod调度到需要跨NUMA节点最少的工作节点上。 打分公式如下: score = weight * (100 - 100 * numaNodeNum / maxNumaNodeNum) 参数说明: weight:NUMA Aware Plugin的权重。 numaNodeNum:表示工作节点上运行该Pod需要NUMA节点的个数。 maxNumaNodeNum:表示所有工作节点中该Pod的最大NUMA节点个数。 例如,假设有三个节点满足Pod的CPU拓扑策略,且NUMA Aware Plugin的权重设为10: Node A:由1个NUMA节点提供Pod所需的CPU资源,即numaNodeNum=1 Node B:由2个NUMA节点提供Pod所需的CPU资源,即numaNodeNum=2 Node C:由4个NUMA节点提供Pod所需的CPU资源,即numaNodeNum=4 则根据以上公式,maxNumaNodeNum=4 score(Node A) = 10 * (100 - 100 * 1 / 4) = 750 score(Node B) = 10 * (100 - 100 * 2 / 4) = 500 score(Node C) = 10 * (100 - 100 * 4 / 4) = 0 因此最优节点为Node A。
  • 使用NPU 创建工作负载申请NPU资源,可按如下方法配置,指定显卡的数量。 kind: Deployment apiVersion: apps/v1 metadata: name: npu-test namespace: default spec: replicas: 1 selector: matchLabels: app: npu-test template: metadata: labels: app: npu-test spec: containers: - name: container-0 image: nginx:perl resources: limits: cpu: 250m huawei.com/ascend-310: '1' memory: 512Mi requests: cpu: 250m huawei.com/ascend-310: '1' memory: 512Mi imagePullSecrets: - name: default-secret 通过huawei.com/ascend-310指定申请NPU的数量。 使用huawei.com/ascend-310参数指定NPU数量时,requests和limits值需要保持一致。 指定huawei.com/ascend-310后,在调度时不会将负载调度到没有NPU的节点。如果缺乏NPU资源,会报类似“0/2 nodes are available: 2 Insufficient huawei.com/ascend-310.”的Kubernetes事件。 在CCE控制台使用NPU资源,只需在创建工作负载时,勾选NPU配额,并指定使用NPU芯片的数量。 图1 使用NPU
  • Binpack功能介绍 Binpack调度算法的目标是尽量把已有的节点填满(即尽量不往空白节点分配)。具体实现上,Binpack调度算法为满足调度条件的节点打分,节点的资源利用率越高得分越高。Binpack算法能够尽可能填满节点,将应用负载靠拢在部分节点,这非常有利于集群节点的自动扩缩容功能。 Binpack为调度器的多个调度插件之一,与其他插件共同为节点打分,用户可以自定义该插件整体权重和各资源维度打分权重,用以提高或降低Binpack在整体调度中的影响力。调度器在计算Binpack策略得分时,会考虑Pod请求的各种资源,如:CPU、Memory和GPU等扩展资源,并根据各种资源所配置的权重做平均。
  • Binpack算法原理 Binpack在对一个节点打分时,会根据Binpack插件自身权重和各资源设置的权重值综合打分。首先,对Pod请求资源中的每类资源依次打分,以CPU为例,CPU资源在待调度节点的得分信息如下: CPU.weight * (request + used) / allocatable 即CPU权重值越高,得分越高,节点资源使用量越满,得分越高。Memory、GPU等资源原理类似。其中: CPU.weight为用户设置的CPU权重 request为当前pod请求的CPU资源量 used为当前节点已经分配使用的CPU量 allocatable为当前节点CPU可用总量 通过Binpack策略的节点总得分如下: binpack.weight * (CPU.score + Memory.score + GPU.score) / (CPU.weight+ Memory.weight+ GPU.weight) * 100 即binpack插件的权重值越大,得分越高,某类资源的权重越大,该资源在打分时的占比越大。其中: binpack.weight为用户设置的装箱调度策略权重 CPU.score为CPU资源得分,CPU.weight为CPU权重 Memory.score为Memory资源得分,Memory.weight为Memory权重 GPU.score为GPU资源得分,GPU.weight为GPU权重 图1 Binpack策略示例
  • 前提条件 创建GPU类型节点,具体请参见创建节点。 安装gpu-device-plugin(原gpu-beta)插件,安装时注意要选择节点上GPU对应的驱动,具体请参见CCE AI套件(NVIDIA GPU)。 gpu-device-plugin(原gpu-beta)插件会把驱动的目录挂载到/usr/local/nvidia/lib64,在容器中使用GPU资源需要将/usr/local/nvidia/lib64追加到LD_LIBRARY_PATH环境变量中。 通常可以通过如下三种方式追加。 制作镜像的Dockerfile中配置LD_LIBRARY_PATH。(推荐) ENV LD_LIBRARY_PATH /usr/local/nvidia/lib64:$LD_LIBRARY_PATH 镜像的启动命令中配置LD_LIBRARY_PATH。 /bin/bash -c "export LD_LIBRARY_PATH=/usr/local/nvidia/lib64:$LD_LIBRARY_PATH && ..." 创建工作负载时定义LD_LIBRARY_PATH环境变量(需确保容器内未配置该变量,不然会被覆盖)。 ... env: - name: LD_LIBRARY_PATH value: /usr/local/nvidia/lib64 ...
  • 使用GPU 创建工作负载申请GPU资源,可按如下方法配置,指定显卡的数量。 apiVersion: apps/v1 kind: Deployment metadata: name: gpu-test namespace: default spec: replicas: 1 selector: matchLabels: app: gpu-test template: metadata: labels: app: gpu-test spec: containers: - image: nginx:perl name: container-0 resources: requests: cpu: 250m memory: 512Mi nvidia.com/gpu: 1 # 申请GPU的数量 limits: cpu: 250m memory: 512Mi nvidia.com/gpu: 1 # GPU数量的使用上限 imagePullSecrets: - name: default-secret 通过nvidia.com/gpu指定申请GPU的数量,支持申请设置为小于1的数量,比如nvidia.com/gpu: 0.5,这样可以多个Pod共享使用GPU。GPU数量小于1时,不支持跨GPU分配,如0.5 GPU只会分配到一张卡上。 使用nvidia.com/gpu参数指定GPU数量时,requests和limits值需要保持一致。 指定nvidia.com/gpu后,在调度时不会将负载调度到没有GPU的节点。如果缺乏GPU资源,会报类似如下的Kubernetes事件。 0/2 nodes are available: 2 Insufficient nvidia.com/gpu. 0/4 nodes are available: 1 InsufficientResourceOnSingleGPU, 3 Insufficient nvidia.com/gpu. 在CCE控制台使用GPU资源,只需在创建工作负载时,选择使用的GPU配额即可。 图1 使用GPU
  • 优先级调度与抢占介绍 用户在集群中运行的业务丰富多样,包括核心业务、非核心业务,在线业务、离线业务等,根据业务的重要程度和SLA要求,可以对不同业务类型设置相应的高优先级。比如对核心业务和在线业务设置高优先级,可以保证该类业务优先获取集群资源。当集群资源被非核心业务占用,整体资源不足时,如果有新的核心业务提交部署请求,可以通过抢占的方式驱逐部分非核心业务,释放集群资源用于核心业务的调度运行。 CCE集群支持的优先级调度如表1所示。 表1 业务优先级保障调度 调度类型 说明 支持的调度器 基于优先级调度 调度器优先保障高优先级业务运行,但不会主动驱逐已运行的低优先级业务。基于优先级调度配置默认开启,不支持关闭。 kube-scheduler调度器/Volcano调度器 基于优先级抢占调度 当集群资源不足时,调度器主动驱逐低优先级业务,保障高优先级业务正常调度。 Volcano调度器
  • 基于优先级抢占调度的亲和/反亲和示例 在Pod间亲和场景中,不推荐Pod与比其优先级低的Pod亲和。如果pending状态的Pod与节点上的一个或多个较低优先级Pod具有Pod间亲和性,对较低优先级的Pod发起抢占时,会无法满足Pod间亲和性规则,抢占规则和亲和性规则产生矛盾。 在这种情况下,调度程序无法保证pending状态的Pod可以被调度。详情请参见与低优先级Pod之间的Pod间亲和性。 在Pod间反亲和场景中,如果启用优先级抢占,当deploy1与比其优先级低的deploy2亲和,volcano-scheduler为保证业务自运维,将驱逐deploy3,并将deploy1调度到节点上。被驱逐的deploy3将会在新节点准备好后,调度到新节点上。 图2 与低优先级的Pod亲和场景 在Pod间反亲和场景中,volcano-scheduler为减少对其它业务的影响,将不驱逐deploy2和deploy3,而是在新节点准备好后,调度到新节点上。 图3 与低优先级Pod反亲和场景
  • 基于优先级调度示例 如果集群中存在两个空闲节点,存在3个优先级的工作负载,分别为high-priority,med-priority,low-priority,首先运行high-priority占满集群资源,然后提交med-priority,low-priority的工作负载,由于集群资源全部被更高优先级工作负载占用,med-priority,low-priority的工作负载为pending状态,当high-priority工作负载结束,按照优先级调度原则,med-priority工作负载将优先调度。 通过priority.yaml创建3个优先级定义(PriorityClass),分别为:high-priority,med-priority,low-priority。 priority.yaml文件内容如下: apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 100 globalDefault: false description: "This priority class should be used for volcano job only." --- apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: med-priority value: 50 globalDefault: false description: "This priority class should be used for volcano job only." --- apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: low-priority value: 10 globalDefault: false description: "This priority class should be used for volcano job only." 创建PriorityClass: kubectl apply -f priority.yaml 查看优先级定义信息。 kubectl get PriorityClass 回显如下: NAME VALUE GLOBAL-DEFAULT AGE high-priority 100 false 97s low-priority 10 false 97s med-priority 50 false 97s system-cluster-critical 2000000000 false 6d6h system-node-critical 2000001000 false 6d6h 创建高优先级工作负载high-priority-job,占用集群的全部资源。 high-priority-job.yaml apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: priority-high spec: schedulerName: volcano minAvailable: 4 priorityClassName: high-priority tasks: - replicas: 4 name: "test" template: spec: containers: - image: alpine command: ["/bin/sh", "-c", "sleep 1000"] imagePullPolicy: IfNotPresent name: running resources: requests: cpu: "1" restartPolicy: OnFailure 执行以下命令下发作业: kubectl apply -f high_priority_job.yaml 通过 kubectl get pod 查看Pod运行信息,如下: NAME READY STATUS RESTARTS AGE priority-high-test-0 1/1 Running 0 3s priority-high-test-1 1/1 Running 0 3s priority-high-test-2 1/1 Running 0 3s priority-high-test-3 1/1 Running 0 3s 此时,集群节点资源已全部被占用。 创建中优先级工作负载med-priority-job和低优先级工作负载low-priority-job。 med-priority-job.yaml apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: priority-medium spec: schedulerName: volcano minAvailable: 4 priorityClassName: med-priority tasks: - replicas: 4 name: "test" template: spec: containers: - image: alpine command: ["/bin/sh", "-c", "sleep 1000"] imagePullPolicy: IfNotPresent name: running resources: requests: cpu: "1" restartPolicy: OnFailure low-priority-job.yaml apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: priority-low spec: schedulerName: volcano minAvailable: 4 priorityClassName: low-priority tasks: - replicas: 4 name: "test" template: spec: containers: - image: alpine command: ["/bin/sh", "-c", "sleep 1000"] imagePullPolicy: IfNotPresent name: running resources: requests: cpu: "1" restartPolicy: OnFailure 执行以下命令下发作业: kubectl apply -f med_priority_job.yaml kubectl apply -f low_priority_job.yaml 通过 kubectl get pod 查看Pod运行信息,集群资源不足,Pod处于Pending状态,如下: NAME READY STATUS RESTARTS AGE priority-high-test-0 1/1 Running 0 3m29s priority-high-test-1 1/1 Running 0 3m29s priority-high-test-2 1/1 Running 0 3m29s priority-high-test-3 1/1 Running 0 3m29s priority-low-test-0 0/1 Pending 0 2m26s priority-low-test-1 0/1 Pending 0 2m26s priority-low-test-2 0/1 Pending 0 2m26s priority-low-test-3 0/1 Pending 0 2m26s priority-medium-test-0 0/1 Pending 0 2m36s priority-medium-test-1 0/1 Pending 0 2m36s priority-medium-test-2 0/1 Pending 0 2m36s priority-medium-test-3 0/1 Pending 0 2m36s 删除high_priority_job工作负载,释放集群资源,med_priority_job会被优先调度。 执行 kubectl delete -f high_priority_job.yaml 释放集群资源,查看Pod的调度信息,如下: NAME READY STATUS RESTARTS AGE priority-low-test-0 0/1 Pending 0 5m18s priority-low-test-1 0/1 Pending 0 5m18s priority-low-test-2 0/1 Pending 0 5m18s priority-low-test-3 0/1 Pending 0 5m18s priority-medium-test-0 1/1 Running 0 5m28s priority-medium-test-1 1/1 Running 0 5m28s priority-medium-test-2 1/1 Running 0 5m28s priority-medium-test-3 1/1 Running 0 5m28s
  • Volcano Scheduler Volcano Scheduler是负责Pod调度的组件,它由一系列action和plugin组成。action定义了调度各环节中需要执行的动作;plugin根据不同场景提供了action 中算法的具体实现细节。Volcano Scheduler具有高度的可扩展性,您可以根据需要实现自己的action和plugin。 图1 Volcano Scheduler工作流 Volcano Scheduler的工作流程如下: 客户端提交的Job被调度器识别到并缓存起来。 周期性开启会话,一个调度周期开始。 将没有被调度的Job发送到会话的待调度队列中。 遍历所有的待调度Job,按照定义的次序依次执行enqueue、allocate、preempt、reclaim、backfill等动作,为每个Job找到一个最合适的节点。将该Job 绑定到这个节点。action中执行的具体算法逻辑取决于注册的plugin中各函数的实现。 关闭本次会话。