Koordinator 最佳实践系列:精细化 CPU 编排

原创
06/13 16:20
阅读数 2.2K

作者:乔普、申信

介绍

在云原生环境中,集群提供者常常将不同类型的工作负载部署在同一个集群中,利用不同业务的不同峰值效果,实现资源分时复用,避免资源浪费。然而,不同类型负载之间混合部署常常会导致资源竞争和相互干扰。最为典型的场景便是在线和离线负载的混合部署。当离线较多的占用计算资源时,在线负载的响应时间就会受到影响;当在线长时间较多的占用计算资源时,离线负载的任务完成时间不能得到保证。这种现象属于 Noisy Neighbor 问题。

根据混合部署的程度、资源类型的不同,解决该问题有许多不同的思路。Quota 管理可从整个集群维度限制负载的资源使用量,Koordinator 在这方面提供了多层次弹性 Quota 管理功能 [ 1] 。单机维度上看,CPU、内存、磁盘 IO,网络资源都有可能被不同负载共享。Koordinator 在 CPU、内存上已经提供了一些资源隔离和保障的能力,磁盘 IO 和网络资源方面的相关能力正在建设中。

本文主要介绍当不同类型工作负载混合部署在同一个节点上时,Koordinator 如何帮助负载之间(在线和在线、在线和离线)协同地共享 CPU 资源。

问题描述

CPU 资源 Noisy Neighbor 的本质是不同的负载之间无协同地共享 CPU 资源。

  1. Kubernetes 默认的资源模型利用 cgroup(cfs quota) 从 CPU 时间使用量上来限制不同负载对于 CPU 资源的访问。这种情况下,一些负载就可能会被操作系统调度器切换所在的 CPU 核。由于不同 CPU 核对不同物理位置的内存访问时间不同,切换大概率会导致更长的内存访问时间,从而影响负载性能。
  2. 在 NUMA 架构中,SMT 线程(逻辑核)共享物理核的执行单元和 L2 缓存。当同一个物理核中有多种工作负载时,不同工作负载间就会产生资源争抢,导致负载性能下降。

Kubernetes 在单机侧提供了拓扑管理器和 CPU 管理器来尝试解决上述问题。然而,该功能只有在 Pod 已经调度到机器上之后才会尝试生效。这样就有可能导致 Pod 会被调度到 CPU 资源满足但是 CPU 拓扑不满足负载要求的情况。

解决方案

面向应用的 CPU 编排 QoS 语义

针对上述问题和不足,Koordinator 设计了面向应用的 QoS 语义和 CPU 编排协议,如下图所示。

image.png

LS(Latency Sensitive)应用于典型的微服务负载,Koordinator 将其与其它的延迟敏感型负载隔离保障其性能。LSR(Latency Sensitive Reserved)类似于 Kubernetes 的 Guaranteed,在 LS 的基础上增加了应用要求预留绑核的语义。LSE(Latency Sensitive Exclusive)则常见于中间件等对 CPU 特别敏感的应用,Koordinator 除了满足其类似于 LSR 要求绑核的语义外,还确保其所被分配的 CPU 不与任何其它负载共享。

另外,为提高资源利用率,BE 负载可与 LSR 和 LS 共享CPU。为了确保与 BE 共享的延迟敏感型应用不受其干扰,Koordinator 提供了如干扰检测、BE 压制等策略。本文重点不在此,读者可关注后续文章。

丰富的 CPU 编排策略

对于 LSE 类型的应用,当机器是超线程架构时,只能保证负载独占逻辑核。这样当同一个物理核中有其它负载时,应用性能仍会受干扰。为此,Koordinator 支持用户在 Pod Annotation 上配置丰富的 CPU 编排策略来提高性能。

CPU 编排策略分为 CPU 绑定策略和 CPU 独占策略。CPU 绑定策略决定应用所被分配逻辑核在物理核间的分布,可采用物理核间打散或者堆叠。堆叠(FullPCPU)的方式指为应用分配完整的物理内核,可以有效地缓解 Noisy Neighbor 问题。打散(SpreadByPCPU)则主要应用于一些具有多种不同峰谷特性的延迟敏感型应用,可以让应用程序在特定时间充分使用 CPU。CPU 独占策略决定应用所被分配逻辑核的独占级别,可尽量避开已经同独占策略申请的物理核或 NUMANode。

增强的 CPU 调度能力

Koordinator 支持配置 NUMA 的分配策略,决定在调度时如何选择满意的 NUMA 节点。MostAllocated 表示从可用资源最少的 NUMA 节点分配,可以尽可能减少碎片,为后续的负载留下更大的分配空间。但是,这种方式可能会导致依赖 Barrier 的并行代码性能收到影响。DistributeEvenly 表示在 NUMA 节点上平均分配 CPU,可以提高上述并行代码的性能。LeastAllocated 表示从可用资源最多的 NUMA 节点分配。

另外,Koordinator 对 CPU 的分配逻辑是在中心调度器完成的。这样就会有一个全局的视角,避免了 Kubernetes 单机方案可能导致的 CPU 资源量满足但是拓扑不满足的窘境。

最佳实践

由上文可知,Koordinator 精细化 CPU 编排能力能够显著提高多应用混合部署场景下 CPU 敏感型工作负载的性能。为了让读者能够更清楚地使用和直观感受 Koordinator 的精细化 CPU 编排能力,本文将在线应用采用不同方式部署到集群中,观察压测中服务的延迟,来判断 CPU 编排能力的效果。

本文会在同一个机器上部署多个在线应用,压测 10 分钟,以充分模拟生产实践中可能出现的 CPU 核切换场景。对于在线应用和离线应用混合部署的情况,Koordinator 提供了如干扰检测、BE 压制等策略。本文重点不在此,读者可关注后续文章中的实践。

image

本次实验采用以下指标,评估应用不同部署方式下 Nginx 应用的性能表现:

  • 响应时间 RT(Response Time)分位值RT 是在线应用通常关注的性能指标,RT 越低代表在线服务性能越好。RT 指标通过收集 wrk 压测结束后打印的信息获得,在实验中反映了 Nginx 应用响应 wrk 请求所花费的时间。例如 RT-p50 表示 Nginx 响应前 50% wrk 请求最大所花费的时间(中位数),RT-p90 表示 Nginx 响应前 90% wrk 请求最大所花费的时间。
  • 每秒请求数 RPS(Request Per Second)RPS 是在线应用每秒服务的请求数量,服务承受的 RPS 越多代表在线服务的性能越好。

实验结果如下:

image.png

  • 对比 B 和 A,可以发现采用 LSE QoS 绑核之后,服务响应时间 P99 明显减小,很好地减轻了长尾现象
  • 对比 C 和 B,可以发现采用 LSR QoS 绑核且允许逻辑核占用更多物理核资源之后,在服务响应时间更好的情况下可以承受更多的请求

综上,在线服务部署在同一机器的场景下,采用 koordinator 精细化 CPU 编排能够有效抑制 Noisy Neighbor 问题,减少 CPU 核切换带来的性能下降。

环境

首先,要先准备一个 Kubernetes 集群并安装 Koordinator [ 2] 。本文选择一个 Kubernetes 集群的两个节点来做实验,其中一个节点作为测试机,将运行 Nginx 在线服务器;另一节点作为压测机,将运行客户端的 wrk,向 Nginx 请求 Web 服务,制造压测请求。

在线应用

  1. 使用 ColocationProfile [ 3] 为应用注入精细化 CPU 编排协议

B 组精细化 CPU 编排协议:

apiVersion: config.koordinator.sh/v1alpha1
kind: ClusterColocationProfile
metadata:
  name: colocation-profile-example
spec:
  selector:
    matchLabels:
      app: nginx
  # 采用 LSE QoS
  qosClass: LSE
  annotations:
  # 采用物理核间堆叠
    scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy":"FullPCPUs"}'
  priorityClassName: koord-prod

C 组 CPU 精细化编排协议:

apiVersion: config.koordinator.sh/v1alpha1
kind: ClusterColocationProfile
metadata:
  name: colocation-profile-example
spec:
  selector:
    matchLabels:
      app: nginx
  # 采用 LSR QoS
  qosClass: LSR
  annotations:
  # 采用物理核间打散且独占物理核
    scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy":"SpreadByPCPUs", "preferredCPUExclusivePolicy":"PCPULevel"}'
  priorityClassName: koord-prod
  1. 在线服务本文选用 Nginx 在线服务器,Pod YAML 如下:
---
# nginx应用配置
apiVersion: v1
data:
  config: |-
    user  nginx;
    worker_processes  4; # Nginx的Worker个数,影响Nginx Server的并发。

    events {
        worker_connections  1024;  # 默认值为1024。
    }

    http {
        server {
            listen  8000;

            gzip off;
            gzip_min_length 32;
            gzip_http_version 1.0;
            gzip_comp_level 3;
            gzip_types *;
        }
    }

    #daemon off;
kind: ConfigMap
metadata:
  name: nginx-conf-0
---
# Nginx实例,作为在线类型服务应用。
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx-0
  namespace: default
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - "${node_name}"    
  schedulerName: koord-scheduler
  priorityClassName: koord-prod
  containers:
    - image: 'koordinatorsh/nginx:v1.18-koord-exmaple'
      imagePullPolicy: IfNotPresent
      name: nginx
      ports:
        - containerPort: 8000
          hostPort: 8000 # 压测请求访问的端口。
          protocol: TCP
      resources:
        limits:
          cpu: '4'
          memory: 8Gi
        requests:
          cpu: '4'
          memory: 8Gi
      volumeMounts:
        - mountPath: /apps/nginx/conf
          name: config
  hostNetwork: true
  restartPolicy: Never
  volumes:
    - configMap:
        items:
          - key: config
            path: nginx.conf
        name: nginx-conf-0
      name: config
  1. 执行以下命令,部署 Nginx 应用
kubectl apply -f nginx-0.yaml
  1. 执行以下命令,查看 Nginx 应用的 Pod 状态
kubectl get pod -l app=nginx -o wide

可以看到输出如下,表示 Nginx 应用已经在测试机上正常运行

NAME      READY   STATUS    RESTARTS   AGE     IP           NODE                    NOMINATED NODE   READINESS GATES
nginx-0   1/1     Running   0          2m46s   10.0.0.246   cn-beijing.10.0.0.246   <none>           <none>
  1. 在压测机上,执行以下命令,部署压测工具 wrk
wget -O wrk-4.2.0.tar.gz https://github.com/wg/wrk/archive/refs/tags/4.2.0.tar.gz && tar -xvf wrk-4.2.0.tar.gz
cd wrk-4.2.0 && make && chmod +x ./wrk

压测

  1. 使用压测工具 wrk,向 Nginx 应用发起压测请求。
# node_ip填写测试机的IP地址,用于wrk向测试机发起压测;8000是Nginx暴露到测试机的端口。
taskset -c 32-45 ./wrk -t120 -c400 -d600s --latency http://${node_ip}:8000/
  1. 等待 wrk 运行结束后,获取 wrk 的压测结果,wrk 输出格式如下所示。重复多次测试,以获得相对稳定的结果。
Running 10m test @ http://192.168.0.186:8000/
  120 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.29ms    2.49ms 352.52ms   91.07%
    Req/Sec     0.96k   321.04     3.28k    62.00%
  Latency Distribution
     50%    2.60ms
     75%    3.94ms
     90%    5.55ms
     99%   12.40ms
  68800242 requests in 10.00m, 54.46GB read
Requests/sec: 114648.19
Transfer/sec:     92.93MB

总结

在 Kubernetes 集群中,不同业务负载之间可能存在 CPU、内存等资源的争抢,影响业务的性能和稳定性。面对 Noisy Neighbor 现象,用户可以使用 Koordinator 为应用配置更精细的 CPU 编排策略,使得不同应用可以协同的共享 CPU 资源。我们通过实验说明,Koordinator 的精细化 CPU 编排能力能有效抑制 CPU 资源的争抢,改善应用性能。

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 Koordinator 开源社区。你是否已经有一些希望与我们社区交流的内容呢?可以通过以下渠道参与讨论:

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 33383887 (Chinese)

相关链接:

[1] 多层次弹性 Quota 管理功能

https://koordinator.sh/docs/user-manuals/multi-hierarchy-elastic-quota-management/

[2] 安装 Koordinator

https://koordinator.sh/docs/installation/

[3] ColocationProfile

https://koordinator.sh/docs/user-manuals/colocation-profile/

点击此处,立即了解 Koordinator 项目!

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部