文档

CPU拓扑感知调度

更新时间:

ACK基于新版的Scheduling framework实现CPU拓扑感知调度,针对CPU敏感型的工作负载提供更好的性能。本文介绍如何使用CPU拓扑感知调度。

原理介绍

Kubernetes的节点会运行多个Pod,其中部分Pod属于CPU密集型工作负载。在这种情况下,Pod之间会争抢节点的CPU资源。当争抢剧烈时,Pod会在不同的CPU Core之间进行频繁的切换,更糟糕的是在NUMA Node之间切换。这种大量的上下文切换,会影响程序运行的性能。Kubernetes虽然有CPU Manager解决方案处理以上问题,但是因为CPU Manager特性是节点级别的CPU调度选择,所以无法在集群维度中选择最优的CPU Core组合。同时CPU Manager特性要求Pod是Guaranteed时(Pod中的每个容器必须指定CPU请求和CPU限制,并且两者要相等)才能生效,且无法适用于所有类型的Pod。

在以下场景中,建议使用CPU拓扑感知调度:

  • 工作负载为计算密集型。

  • 应用程序对CPU敏感。

  • 运行在神龙裸金属(Intel、AMD)等多核机器上。

    通过在物理机上部署两个4核8 GB的Nginx并进行压测,发现在Intel(104核)、AMD(256核)的物理机上,使用CPU拓扑感知调度能够将应用性能提升22%~43%。具体如以下两个图所示:12

    性能指标

    Intel

    AMD

    QPS

    提升22.9%

    提升43.6%

    AVG RT

    下降26.3%

    下降42.5%

    重要

    不同应用对CPU绑核的敏感程度存在差异,上述实验中的数据仅供参考,建议您将应用部署在稳定的环境中,根据机型等环境情况适当调节实验压力水位,确保应用运行正常,并在充分对比性能情况后评估是否开启CPU拓扑感知调度功能。

在激活CPU拓扑感知调度时,可通过在所创建Deployment对象的template.metadata.annotations(或者Pod对象的metadata.annotations)下,设置cpu-policystatic-burst,从而控制自动绑核策略。该策略能够对计算密集性负载,有效避免进程的物理核心争用及跨片内存访问,最大化利用碎片化CPU,在不改变硬件及虚拟机资源的前提下为密集计算型负载优化可用资源,进一步提升CPU使用率。

关于CPU拓扑感知调度的实现原理,请参见Practice of Fine-grained Cgroups Resources Scheduling in Kubernetes

前提条件

  • 已创建ACK Pro版集群。具体操作,请参见创建ACK Pro版集群

  • 已安装ack-koordinator(原ack-slo-manager)。具体操作,请参见ack-koordinator

    说明

    ack-koordinator在原resource-controller组件的基础上做了升级优化,如果您正在使用resource-controller,需在安装ack-koordinator后,卸载resource-controller。关于卸载的具体操作,请参见resource-controller

使用限制

系统组件版本要求具体如下表所示。

组件

版本要求

Kubernetes

≥v1.18

ack-koordinator

≥0.2.0

费用说明

ack-koordinator组件本身的安装和使用是免费的,不过需要注意的是,在以下场景中可能产生额外的费用:

  • ack-koordinator是非托管组件,安装后将占用Worker节点资源。您可以在安装组件时配置各模块的资源申请量。

  • ack-koordinator默认会将资源画像、精细化调度等功能的监控指标以Prometheus的格式对外透出。若您配置组件时开启了ACK-Koordinator开启Prometheus监控指标选项并使用了阿里云Prometheus服务,这些指标将被视为自定义指标并产生相应费用。具体费用取决于您的集群规模和应用数量等因素。建议您在启用此功能前,仔细阅读阿里云Prometheus计费说明,了解自定义指标的免费额度和收费策略。您可以通过账单和用量查询,监控和管理您的资源使用情况。

注意事项

  • 在使用CPU拓扑感知调度前,请保证已部署组件ack-koordinator。

  • 在使用CPU拓扑感知调度时,请保证节点的cpu-policy=none

  • 若需要限制应用的调度,请使用nodeSelector字段。

    重要

    请不要使用nodeName字段,该方式将无法进入调度器逻辑使用CPU拓扑感知调度。

使用CPU拓扑感知调度

激活CPU拓扑感知调度前,您需要在提交Pod时设置annotationsContainers的值。具体操作步骤如下:

  • 在Deployment对象的template.metadata.annotations(或者在Pod对象的metadata.annotations)下,设置cpuset-schedulertrue,激活CPU拓扑感知调度。

  • Containers下,设置resources.limit.cpu的值,且该值为整数。

  1. 使用以下模板创建go-demo.yaml文件,在Deployment中使用CPU拓扑感知调度。

    重要
    • 针对Deployment对象,您需要在template.metadata下配置Pod的annotations

    • 在配置CPU拓扑感知调度时,可通过在annotations下设置cpu-policystatic-burst,从而控制自动绑核策略。需要配置时,请删除cpu-policy前的#

    展开查看YAML文件内容

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: go-demo
    spec:
      replicas: 4
      selector:
        matchLabels:
          app: go-demo
      template:
        metadata:
          annotations:
            cpuset-scheduler: "true" #表示CPU拓扑感知调度。
          labels:
            app: go-demo
        spec:
          containers:
          - name: go-demo
            image: registry.cn-hangzhou.aliyuncs.com/polinux/stress/go-demo:1k
            imagePullPolicy: Always
            ports:
            - containerPort: 8080
            resources:
              requests:
                cpu: 1
              limits: 
                cpu: 4  #需要设置resources.limit.cpu值。
  2. 执行以下命令,创建Deployment。

    kubectl create -f go-demo.yaml

验证CPU拓扑感知对应用性能的提升

本示例使用场景为:

  • ACK Pro版集群版本为1.20。

  • 需要包含两个节点:一台压测机、一台测试机。

重要

以下部署和压测命令仅供参考,您可结合自身实验环境调节资源规格和请求压力,确保应用处于正常工作状态后再进行实验。

  1. 执行以下命令,为测试机打上标签。

     kubectl label node 192.168.XX.XX policy=intel/amd
  2. 在测试机上部署Nginx服务。

    1. 使用以下YAML示例,创建Nginx服务配置文件。

      展开查看service.yaml文件内容

      apiVersion: v1
      kind: Service
      metadata:
        name: nginx-service-nodeport
      spec:
        selector:
            app: nginx
        ports:
          - name: http
            port: 8000
            protocol: TCP
            targetPort: 80
            nodePort: 32257
        type: NodePort

      展开查看configmap.yaml文件内容

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: nginx-configmap
      data:
        nginx_conf: |-
          user  nginx;
          worker_processes  4;
          error_log  /var/log/nginx/error.log warn;
          pid        /var/run/nginx.pid;
          events {
              worker_connections  65535;
          }
          http {
              include       /etc/nginx/mime.types;
              default_type  application/octet-stream;
              log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                            '$status $body_bytes_sent "$http_referer" '
                            '"$http_user_agent" "$http_x_forwarded_for"';
              access_log  /var/log/nginx/access.log  main;
              sendfile        on;
              #tcp_nopush     on;
              keepalive_timeout  65;
              #gzip  on;
              include /etc/nginx/conf.d/*.conf;
          }

      展开查看nginx.yaml文件内容

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nginx-deployment
        labels:
          app: nginx
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            annotations:
              #cpuset-scheduler: "true"    默认情况,不使用CPU拓扑感知调度。
            labels:
              app: nginx
          spec:
            nodeSelector:
              policy: intel7
            containers:
            - name: nginx
              image: nginx:latest
              ports:
              - containerPort: 80
              resources:
                requests:
                  cpu: 4
                  memory: 8Gi
                limits:
                  cpu: 4
                  memory: 8Gi
              volumeMounts:
                 - mountPath: /etc/nginx/nginx.conf
                   name: nginx
                   subPath: nginx.conf
            volumes:
              - name: nginx
                configMap:
                  name: nginx-configmap
                  items:
                    - key: nginx_conf
                      path: nginx.conf
    2. 执行以下命令,创建Nginx服务。

      • kubectl create -f service.yaml
      • kubectl create -f configmap.yaml
      • kubectl create -f nginx.yaml
  3. 登录压测机,下载wrk2开源测试工具并解压安装。具体操作,请参见wrk2官方网站

    说明

    关于如何登录到节点,请参见使用VNC登录实例通过密码认证登录Windows实例

  4. 执行以下命令,在压测机上进行测试并记录数据。

    wrk --timeout 2s -t 20 -c 100 -d 60s --latency http://<测试机IP>:32257

    预期输出:

    20 threads and 100 connections
    Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   600.58us    3.07ms 117.51ms   99.74%
    Req/Sec    10.67k     2.38k   22.33k    67.79%
    Latency Distribution
    50%  462.00us
    75%  680.00us
    90%  738.00us
    99%    0.90ms
    12762127 requests in 1.00m, 10.10GB read
    Requests/sec: 212350.15Transfer/sec:    172.13MB
  5. 执行以下命令,删除已部署的Nginx。

    kubectl delete deployment nginx

    预期输出:

    deployment "nginx" deleted
  6. 使用以下YAML示例,部署开启CPU拓扑感知调度的Nginx。

    展开查看YAML文件内容

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          annotations:
            cpuset-scheduler: "true"
          labels:
            app: nginx
        spec:
          nodeSelector:
            policy: intel7
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
            resources:
              requests:
                cpu: 4
                memory: 8Gi
              limits:
                cpu: 4
                memory: 8Gi
            volumeMounts:
               - mountPath: /etc/nginx/nginx.conf
                 name: nginx
                 subPath: nginx.conf
          volumes:
            - name: nginx
              configMap:
                name: nginx-configmap
                items:
                  - key: nginx_conf
                    path: nginx.conf
  7. 执行以下命令,在压测机上进行测试并记录数据进行对比。

    wrk --timeout 2s -t 20 -c 100 -d 60s --latency http://<测试机IP>:32257

    预期输出:

    20 threads and 100 connections
    ls  Thread Stats   Avg         Stdev     Max       +/- Stdev
    Latency            345.79us    1.02ms    82.21ms   99.93%
    Req/Sec            15.33k      2.53k     25.84k    71.53%
    Latency Distribution
    50%  327.00us
    75%  444.00us
    90%  479.00us
    99%  571.00us
    18337573 requests in 1.00m, 14.52GB read
    Requests/sec: 305119.06Transfer/sec:    247.34MB

    通过两次数据的对比可以看出,开启CPU拓扑感知调度后,Nginx的性能提升43%。

验证自动绑核策略对应用性能的提升

本示例介绍在一个64核的节点上设置CPU Policy。在设置控制自动绑核策略后,相比于CPU拓扑感知调度一般可以提升7%~8%的CPU使用效率。

重要

以下部署和压测命令仅供参考,您可结合自身实验环境调节资源规格,确保应用处于正常工作状态后再进行实验。

  1. 执行以下命令,查看Pod。

    kubectl get pods | grep cal-pi

    预期输出:

    NAME                 READY     STATUS    RESTARTS   AGE
    cal-pi-d****         1/1       Running   1          9h
  2. 执行以下命令,查看应用cal-pi-d****的日志。

    kubectl logs cal-pi-d****

    预期输出:

    computing Pi with 3000 Threads...computed the first 20000 digets of pi in 620892 ms! 
    the first digets are: 3.14159264
    writing to pi.txt...
    finished!
  3. 使用CPU拓扑感知调度。

    在Deployment中使用CPU拓扑感知调度并控制自动绑核策略。更多信息,请参见使用CPU拓扑感知调度

    1. 使用以下模板创建go-demo.yaml文件,在Deployment中使用CPU拓扑感知调度。

      重要

      针对Deployment对象,您需要在template.metadata下配置Pod的annotations

      展开查看YAML文件内容

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: go-demo
      spec:
        replicas: 4
        selector:
          matchLabels:
            app: go-demo
        template:
          metadata:
            annotations:
              cpuset-scheduler: "true" #表示CPU拓扑感知调度。
              cpu-policy: 'static-burst' #控制自动绑核策略,提升CPU碎片利用率。
            labels:
              app: go-demo
          spec:
            containers:
            - name: go-demo
              image: registry.cn-hangzhou.aliyuncs.com/polinux/stress/go-demo:1k
              imagePullPolicy: Always
              ports:
              - containerPort: 8080
              resources:
                requests:
                  cpu: 1
                limits: 
                  cpu: 4  #需要设置resources.limit.cpu值。
    2. 执行以下命令,创建Deployment。

      kubectl create -f go-demo.yaml
  4. 执行以下命令,查看Pod。

    kubectl get pods | grep go-demo

    预期输出:

    NAME                 READY     STATUS    RESTARTS   AGE
    go-demo-e****        1/1       Running   1          9h
  5. 执行以下命令,查看应用go-demo-e****的日志。

    kubectl logs go-demo-e****

    预期输出:

    computing Pi with 3000 Threads...computed the first 20000 digets of pi in 571221 ms!
    the first digets are: 3.14159264
    writing to pi.txt...
    finished!

    通过与步骤2的日志数据对比,设置CPU Policy后Pod的性能提升8%。

相关文档

Kubernetes对节点的GPU拓扑信息不感知,调度过程中对GPU的选择比较随机,选择不同的GPU组合训练速度会存在较大的差异。基于以上问题,ACK基于Scheduling Framework机制,实现GPU拓扑感知调度,在节点的GPU组合中选择具有最优训练速度的组合。详细信息,请参见GPU拓扑感知调度