Skip to main content
Version: v25.06

NUMA亲和调度开发指南

特性简介

在现代高性能计算和大规模分布式系统中,非统一内存访问(NUMA)架构越来越普遍。NUMA架构通过将内存划分到不同的节点(NUMA节点),每个节点有其自己的局部内存和CPU,旨在减少内存访问延迟,提高系统性能。然而,NUMA架构的复杂性增加了系统资源管理的难度,特别是在多任务、多线程环境中。为了充分利用NUMA架构的优势,需要对系统资源进行精细化管理和监控。NUMA资源监控可视化旨在通过直观的图形界面,实时展示系统中NUMA资源的分配和使用情况,帮助用户更好地理解和管理NUMA资源,从而提升系统性能和资源利用率。在容器集群中,丰富多样的调度器提供了资源调度能力。本特性将分为三个角度,提供针对优先级高、中、低三种不同类型的任务,不同种类的优化方式,在集群调度以及节点内NUMA调度的层面上,保证Pod达到最优的分配效果,在性能提升上达到了显著效果。 详情请参见《NUMA亲和调度》

约束与限制

  • 使用该模块时,需要修改Kubelet的Config文件,具体配置步骤如下所示,此步骤会在开启NUMA亲和策略或最优NUMA Distance策略时自动完成。

    1. 打开Kubelet配置文件。
    vi /var/lib/kubelet/config.yaml
    1. 新增或修改配置项。
    cpuManagerPolicy:static
    topologyManagerPolicy:xxx
    1. 修改应用。
    rm -rf /var/lib/kubelet/cpu_manager_state
    systemctl daemon-reload
    systemctl restart kubelet
    1. 查看Kubelet运行状态。
    systemctl status kubelet

    Kubelet运行状态为running,则表示成功。

输入图片说明说明:
修改节点的拓扑策略时会重启Kubelet,可能会导致部分Pod重新调度,现网环境请谨慎使用。

  • 使用运行时Pod亲和优化功能时,需要提前修改系统配置,具体步骤如下。

    1. 修改操作系统cmdline参数。

      vim /etc/grub2-efi.cfg

      在当前操作系统镜像对应的linux参数后添加容器运行时:

      mem_sampling_on numa_icon=enable

      例如:

      linux   /vmlinuz-5.10.0-216.0.0.115.oe2203sp4.aarch64 root=/dev/mapper/openeuler-root ro rd.lvm.lv=openeuler/root rd.lvm.lv=openeuler/swap video=VGA-1:640x480-32@60me cgroup_disable=files apparmor=0 crashkernel=1024M,high smmu.bypassdev=0x1000:0x17 smmu.bypassdev=0x1000:0x15 arm64.nopauth console=tty0 kpti=off mem_sampling_on numa_icon=enable
    2. 修改containerd的配置文件。

      vi /etc/containerd/config.toml

      将其中的内容修改:

      [plugins."io.containerd.nri.v1.nri"]
      disable = false

      重启containerd以应用更新:

      systemctl restart containerd

输入图片说明说明:
运行时Pod亲和优化功能目前只支持ARM环境+openEuler 22.03 LTS SP4环境使用。且优先级低于绑核,在集群中有绑核的Pod的情况下,自动关闭特性。

  • 使用集群NUMA监控功能时,需要提前配置普罗米修斯。

    1. 配置Prometheus Operator接入NUMA Exporter参考任务场景2。

    2. 配置Prometheu接入NUMA Exporter参考任务场景3。

环境准备

环境要求

  • 已部署Kubernetes v1.21及以上。
  • 已部署Prometheus。
  • 已部署Containerd v1.7及以上。

环境部署

参考NUMA亲和调度用户指南用户指南

检验搭建环境

以使用Volcano为例:当所有volcano-system namespace下的Pod状态为“Running”时,表示环境搭建成功。

volcano-system volcano-admission-xx-xx           1/1  Running  0  xmxxs
volcano-system volcano-admission-init-xx 1/1 Running 0 xmxxs
volcano-system volcano-controllers-xx-xx 1/1 Running 0 xmxxs
volcano-system volcano-schedulers-xx-xx 1/1 Running 0 xmxxs
volcano-system volcano-exporter-daemonset-xx n/n Running 0 xmxxs
volcano-system volcano-config-website-xx-xx 1/1 Running 0 xmxxs
volcano-system volcano-config-xx-xx 1/1 Running 0 xmxxs
volcano-system numa-exporter-xx n/n Running 0 xmxxs
default numaadj-xx n/n Running 0 xmxxs

任务场景1:修改Volcano调度策略

任务场景概述

配置Volcano调度策略。

系统架构

管控层借用openFuyao平台console-website能力,前端展示NUMA拓扑、状态监控以及调度策略配置前端界面。管理调度器在集群中不仅可以使用Kubernetes scheduler,也支持用户自定义调度器。通过调度器,配合Kubelet能力,将资源调度至节点上。Pod运行时使用NUMA FAST能力,进行Pod亲和优化,把网络亲缘关系较强的Pod调度至同一NUMA上,提高性能,完成整体端到端的调度优化方案。

图 1 NUMA亲和调度架构关系

系统架构

接口说明(以numa-aware为例)

表 1 主要接口说明

接口名描述
GET /rest/scheduling/v1/numaaware查询NUMA亲和性调度策略。
PUT /rest/scheduling/v1/numaaware修改NUMA亲和性调度策略。

开发步骤

通过查看配置文件的方法,判断numa-aware插件是否可用。同时可对配置文件进行修改,即开启/关闭numa-aware扩展调度策略,核心代码如下。

func methodPut(clientset kubernetes.Interface, plugin string, status string) (*httputil.ResponseJson, int) {
// 获取volcano配置文件
configmap, err := k8sutil.GetConfigMap(clientset, constant.VolcanoConfigServiceConfigmap,
constant.VolcanoConfigServiceDefaultNamespace)
if err != nil {
zlog.Errorf("GetConfigMap failed, %v", err)
return httputil.GetDefaultServerFailureResponseJson(), http.StatusInternalServerError
}

// 获取配置文件具体内容
configContent, exists := configmap.Data[constant.VolcanoConfigServiceConfigmapName]
zlog.Infof("configContent: %v", configContent)
if !exists {
zlog.Errorf("GetConfigMap failed, content is empty")
return httputil.GetDefaultServerFailureResponseJson(), http.StatusInternalServerError
}

// 修改策略状态
str := "name: " + plugin
if status == "open" {
if !strings.Contains(configContent, str) {
configContent = insertPlugin(configContent)
}
} else if status == "close" {
if strings.Contains(configContent, str) {
configContent = removePlugin(configContent, plugin)
}
} else {
zlog.Errorf("status is not open or close, %v", err)
return httputil.GetDefaultServerFailureResponseJson(), http.StatusInternalServerError
}

// 更新配置文件
configmap.Data[constant.VolcanoConfigServiceConfigmapName] = configContent
configmap, err = k8sutil.UpdateConfigMap(clientset, configmap)
if err != nil {
zlog.Errorf("UpdateConfigMap failed, %v", err)
return httputil.GetDefaultServerFailureResponseJson(), http.StatusInternalServerError
}

// 重启调度器
err = k8sutil.DeleteVolcanoPod(clientset, constant.VolcanoConfigServiceDefaultNamespace)
if err != nil {
zlog.Errorf("delete pod volcano-scheduler failed, %v", err)
return httputil.GetDefaultServerFailureResponseJson(), http.StatusInternalServerError
}

return &httputil.ResponseJson{
Code: constant.Success,
Msg: "success",
}, http.StatusOK
}

调测验证

调用本任务场景对应的接口,确认能否正常查看或修改调度策略。

  1. 调用接口。 将service中ClusterIP改为NodePort,并调用NodePort端口号。

    curl -X GET  "http://192.168.100.59:NodePort/rest/scheduling/v1/numaaware"
  2. 在集群中查看Volcano配置文件。

    kubectl edit configmaps volcano-scheduler-configmap -n volcano-system

    查看其中是否有- numa-aware项,有则表示NUMA亲和调度策略已开启。

  3. 验证调度策略是否正确生效。

    创建一个Deployment,设置亲和到single-numa-node的节点上,使用调度器为Volcano,观察是否按预期进行调度。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: nginx
    template:
    metadata:
    labels:
    app: nginx
    annotations:
    # 指定numa亲和性
    volcano.sh/numa-topology-policy: single-numa-node
    spec:
    # 指定调度器为Volcano
    schedulerName: volcano
    containers:
    - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    resources:
    limits:
    cpu: 1
    memory: 100Mi
    requests:
    cpu: 1
    memory: 100Mi

任务场景2:配置Prometheus Operator接入NUMA Exporter

任务场景概述

Prometheus Operator主要使用自定义资源(CRD)来管理Prometheus的配置,包括Prometheus、Alertmanager、ServiceMonitor和PodMonitor等。ServiceMonitor旨在定义Prometheus应该如何发现和抓取特定服务的监控数据。它用来指定一个或多个Kubernetes服务作为Prometheus的抓取目标。

开发步骤

  1. 编写ServiceMonitor的YAML文件。

    创建一个名为numaExporter-serviceMonitor.yaml的YAML文件,内容如下。

       apiVersion: monitoring.coreos.com/v1
    kind: ServiceMonitor
    metadata:
    labels:
    name: numa-exporter
    name: numa-exporter
    namespace: volcano-system
    spec:
    endpoints:
    - interval: 30s
    port: https
    selector:
    matchLabels:
    app: numa-exporter
    namespaceSelector:
    matchNames:
    - volcano-system
  2. 应用ServiceMonitor资源,使Prometheus可以发现NUMA Exporter并抓取监控数据。

    执行如下命令,使用kubectl将ServiceMonitor资源应用到Kubernetes集群中。

    kubectl apply -f numaExporter-serviceMonitor.yaml

调测验证

  1. 登录Prometheus。

    访问Prometheus界面:浏览器中输入“http://prometheus-server:9090”。

    输入图片说明说明:
    如果Prometheus配置了身份验证或其他安全措施,输入必要的凭据登录。

  2. 验证监控目标。

    2.1 检查Prometheus Targets。

    在Prometheus web界面中,访问Targets页面(通常在“Status”菜单下),检查“numa-exporter”监控目标是否已被正确发现和抓取。

    2.2 查看数据。

    在查询输入框中输入“numa_node_cpus_count”以获取指标数据。

任务场景3:配置Prometheu接入NUMA Exporter

任务场景概述

在Kubernetes集群中,通常使用ConfigMap来管理和更新Prometheus的配置文件(prometheus.yml)。

开发步骤

  1. 编辑ConfigMap。

    找到存储prometheus.yml的ConfigMap,执行以下命令。

    kubectl get configmap -n monitoring

    使用编辑器打开并修改这个ConfigMap。

    kubectl edit configmap <prometheus-config-name> -n monitoring

    在编辑器中,添加或修改scrape_configs部分。

    scrape_configs:
    - job_name: 'numa-exporter'
    static_configs:
    - targets: ['numa-exporter.volcano-system:9201']
  2. 重新加载Prometheus配置。

    保存并退出编辑器,并通过热加载来重新加载配置。

    curl -X POST http://prometheus-server:9090/-/reload #替换为Prometheus实际运行的主机名或IP地址及其端口号。

调测验证

  1. 登录Prometheus。

    访问Prometheus界面:浏览器中输入“http://prometheus-server:9090”。

    输入图片说明说明:
    如果Prometheus配置了身份验证或其他安全措施,输入必要的凭据登录。

  2. 验证监控目标。

    • 检查Prometheus Targets:在Prometheus web界面中,访问“Targets”页面(通常在“Status”菜单下),检查“numa-exporter”监控目标是否已被正确发现和抓取。

    • 查看数据:在查询输入框中输入“numa_node_cpus_count”以获取指标数据。

任务场景4:非独占的Pod亲和至同一NUMA上

任务场景概述

Pod之间存在很多的网络互访行为,可以将他们视作一组具有亲缘关系的Pod。如果将这一组具有亲缘关系的Pod分配在同一Numa节点上,可以有效的降低他们跨Numa访问的次数,从而提升系统的吞吐率。

系统架构

  • 获取Pod亲和性:通过OS的NUMA-FAST模块统计线程亲和性,进而转化为Pod亲和性。
  • 运行时亲和优化:利用NRI感知resource-aware并存储Pod间亲和关系,创建时修改Pod的cpuset/memset,从而将亲和性Pod的资源分配至同一NUMA。 Pod亲和性实现原理如下图所示。

图 2 Pod亲和性优化原理图
系统架构

开发步骤

执行如下命令,可查看pod间亲缘关系。

kubectl get oenuma podafi -n tcs -o yaml

在spec.node.numa字段中,可以看到pod间亲缘关系。

调测验证

比较部署前后,具有线程亲和关系的Pod是否被调度到同一NUMA上。

  1. 执行如下命令,查找线程号,以部署nginx为例。
ps -ef | grep nginx
  1. 执行如下命令,获取到pid。
cat /proc/#pid/status

查看Cpus_allowed字段,如果修改到了一个NUMA上,则具有亲和关系的pod绑核成功。

FAQ

怎么处理NUMA亲和策略不生效?

  • 现象描述

    工作负载配置了NUMA亲和策略为single-numa-node,使用调度器为Volcano,但是没有正确调度到拓扑策略为single-numa-node的节点上。

  • 可能原因

    1. 未在配置文件中开启numa-aware插件。
    2. 工作负载配置了反亲和或者污点,导致无法分配到该节点上。
    3. 节点资源已满。
  • 解决办法

    开启numa-aware插件,并查看是否有反亲和配置,清理节点资源。