编辑 | zouyee
"noisy neighbour"问题的存在时间比云要长,这个词是在互联网技术资源共享开始的时候创造的。造成这种情况的原因通常是共同租户对资源施加了太多的压力,特别是在灵活的云计算中。当一个租户的性能由于另一个租户的活动而下降时,就会出现noisy neighbour问题,当下的云原生同样支持多租户应用场景,因此在同一台服务器上运行的业务(如业务应用程序)也会相互干扰。经典的场景是在线和离线业务负载的混部。如果没有隔离,离线应用程序会经常影响在线业务。当非关键业务的离线应用干扰关键的在线业务时,就称为“noisy neighbor”。如何解决这种问题?这就是本文的key。
“问题描述”
“noisy neighbor”一词用于描述云基础设施中的一种干扰场景,即当同一云计算节点中的其他工作负载完全占用所需资源时,某个工作负载的性能会下降,表征现象如响应的时延拉长,当然,这种情况有时很难识别出来,如果该工作负载对外提供的服务对最终用户非常关键的时候,这种影响将是非常严重的。当资源在没有严格隔离的情况下进行共享时,比如CPU 共享时,问题就会出现,因为CPU 核与其他可共享资源(例如最后一级缓存和内存带宽)紧密相连。
如果在没有隔离策略的情况下将共享资源分配给工作负载,则更容易产生“noisy neighbor”问题。在超线程上运行的工作负载(HT是 Intel x86 架构的一个概念Kubelet从入门到放弃:识透CPU管理)使用同一物理 CPU 核,在不同物理 CPU 核上运行的工作负载共享同一级别的缓存、内存控制器和 I/O 总线带宽,如上图所示。即使在使用不同高速缓存和内存通道的不同物理 CPU socket上运行的工作负载也会共享 CPU 互联带宽、相同的存储设备或 I/O 总线,如下图所示。同时,“noisy neighbor”工作负载通常需要更多资源并且需要很长时间。在这种情况下,关键工作负载无法获得足够的资源而受到干扰。
虽然Kubernetes具备了CPU管理器和设备插件管理器,用于硬件资源分配,例如CPU和设备(SR-IOV、GPU)等,并且Kubernetes的最新版本实现了拓扑管理器,用于实现NUMA拓扑感知,以实现协调资源和保证关键工作负载的最佳性能,但是,它并没有提供解决“noisy neighbor”根本性方案。
“解决方案”
当关键工作负载与一个或多个嘈杂的邻居共存时,重要的是确保关键工作负载可以在任何时间获取其所需的资源,如CPU、缓存、内存、I/O带宽,以稳定运行。
在Kubernetes中没有noisy neighbor隔离,因为Kubernetes不提供这种检测,例如内存控制器、高/低频CPU和Intel® RDT等。这些资源被应用程序、虚拟机和容器共享。例如,在Intel平台上,根据硬件设计,最后一级缓存具有以下规则:
1. 每个逻辑核心(HT)拥有独占的L1缓存(L1被分为Icache和DCache)。
2. 每个物理核心(包括两个逻辑核心)共享L2缓存。
3. 每个socket的核共享L3缓存,这被称为最后一级缓存,如下图所示。
注:最后一级缓存(LLC)如上图所示,app0与app1共享LLC时,必然会导致“noisy neighbor”问题,
CRI-RM是在Intel®架构上实现的Kubernetes的CRI运行时。CRI-RM是一个具有硬件资源感知的工作负载分配策略的代理服务。CRI-RM运行内置的策略算法,决定如何将资源分配给容器。在CRI-RM中有几个可用的策略,每个策略都有不同的目标,并实现不同的硬件分配策略。Intel RDT策略提供了对最后一级缓存和内存带宽的控制,有助于控制“noisy neighbor”问题。Intel RDT控制器支持通过为三种Kubernetes QoS级别(Besteffort,Burstable和Guaranteed)配置相应的Intel RDT类来限制非关键工作负载对共享资源的访问。
“RDT使用”
在Intel RDT中(classes of service:CLOS)可以将CLOS与cgroup进行比较,在cgroup中,CLOS对应于硬件配置(例如20% L3缓存),通过将上层应用程序映射到CLOS,以控制相应硬件资源。下图是关于Intel RDT使用的概述图:
图2: Intel RDT使用概述
Linux内核以“resctrl”文件系统方式暴露用户接口。下面的步骤展示了如何使用resctrl:
STEP1:挂载resctrl
# mount -t resctrl resctrl /sys/fs/resctrl
STEP2:创建CLOS
# cd /sys/fs/resctrl
# mkdir p0 p1
每个CLOS中有三种主要类型的文件: 1. Tasks: 指示属于此CLOS的任务ID 2. CPUs: 指示此CLOS的CPU ID 3. Schemata: 指示缓存配置 有两种可供使用的模式:任务和CPU。选择下面所示的配置之一即可。
通过绑定任务和schemata实现资源隔离。当调度程序进行切换时,相应CPU的CLOS ID也会更改。
通过绑定 CPU 和 schemata 实现资源隔离。这相当于在 CPU 中固定 CLOS ID,然后将不同的线程绑在这些CPU上。如果同时配置了 tasks 和 CPUs,且存在冲突(任务的实际 CPU 与 CLOS 配置的 CPU 不一致)时,则以任务所属的 CLOS 为准。
STEP3:Schemata config
每个 L1/L2/L3 缓存都有对应的 ID。例如,Socket 0 上的 L3 缓存索引设置为 0,而 Socket 1 上的 L3 缓存索引设置为 1。
/sys/devices/system/cpu/cpu*/cache/index*/id
然后,将以下规则写入schemata:左侧是缓存 ID,右侧是位掩码
# echo "L3:0=3;1=c" > /sys/fs/resctrl/p0/schemata
# echo "L3:0=3;1=3" > /sys/fs/resctrl/p1/schemata
echo 1234 > p0/tasks
echo 5678 > p1/tasks
以上是使用Intel RDT的基础步骤,但在使用CRI-RM时,通过classes来配置Intel RDT,这意味着每个容器都被分配到一个Intel RDT类,容器的所有进程都被分配到/sys/fs/resctrl下与Intel RDT类相对应的服务类别(CLOS)中。CRI-RM在启动时或配置更改时根据其配置来配置CLOS。CRI-RM维护pod QoS和Intel RDT类之间的直接映射。如果启用了Intel RDT,则CRI-RM会尝试将容器分配到名称与其pod QoS相匹配的Intel RDT类中,默认行为可以通过pod annotation来覆盖。
CLOS分配说明如下:
默认情况下,容器获得与其pod QoS类(Guaranteed,Burstable或Besteffort)相同名称的Intel RDT类。如果缺少Intel RDT类,则将容器分配给系统根类。可以使用以下pod annotation覆盖默认行为:
1. rdtclass.cri-resource-manager.intel.com/pod:<class-name> 指定用于该pod中所有容器的pod级默认值
2.rdtclass.cri-resource-manager.intel.com/container.<container-name>:<class-name> 指定容器特定分配,优先级高于pod级注释
使用pod的annotaion,也可以指定除Guaranteed、Burstable或Besteffort之外的Intel RDT类别。默认分配也可以通过策略覆盖,但目前内置策略没有这样做。在CRI-RM中使用以下配置文件来控制资源。
# Sample configuration fragment for restricting LLC access by non-critical workloads
rdt:
options:
l3:
# Bail out with an error if cache control is not supported
optional: false
mb:
# Ignore memory bandwidth allocation settings if MBA is not supported by the system
optional: true
partitions:
shared:
# We divide up the full 100% of the available resources into classes below
l3Allocation: "100%"
mbAllocation: ["100%"]
classes:
# - critical workloads can access the full 100% of LLC and the L2 bandwidth
# - non-critical burstable workloads can access 66% of LLC and the L2 bandwidth
# - best effort workloads can access 33% of LLC and the L2 bandwidth
Guaranteed:
l3Schema: "100%"
mbSchema: [“100%”]
Burstable:
l3Schema: "66%"
mbSchema: [“66%”]
BestEffort:
l3Schema: "33%"
mbSchema: [“33%”]
Intel RDT提供了一种新的思路,以对应用程序、虚拟机(VM)和容器使用的共享资源(例如LLC和内存带宽)进行控制。Intel RDT可用于驱动工作负载的整合密度、性能一致性和服务交付。Intel RDT有助于提升应用程序的服务质量(QoS)。
缓存分配技术CAT(Cache Allocation Technology)的核心目标是基于服务级别(Class of Service, COS 或 CLOS)来实现资源分配。应用程序或者独立线程可以按照处理器提供的一系列服务级别来标记。这样就会按照应用程序和线程的服务分类来限制和分配其使用的缓存。每个CLOS可以使用能力掩码(capacity bitmasks, CBMs)来标志并在服务分类中指定覆盖(overlap)或隔离(isolation)的程度。
对于每个逻辑处理器,都有一个寄存器(被称为 IA32_PQR_ASSOC MSR或PQR)来允许操作系统(OS)或虚拟机管理器(VMM)在应用程序、线程、虚拟机(VM)调度(scheduled)的时候指定它的CLOS。
RDT分为5个功能模块:
1. Cache Monitoring Technology (CMT) 缓存检测技术
2. Cache Allocation Technology (CAT) 缓存分配技术
3. Memory Bandwidth Monitoring (MBM) 内存带宽监测
4. Memory Bandwidth Allocation (MBA) 内存带宽分配
5. Code and Data Prioritization (CDP) 代码和数据优先级
这些技术可以监控和控制应用程序、容器或在平台上同时运行的VM使用的共享资源,例如LLC和主存储器(DRAM)带宽。Intel RDT可能有助于检测“noisy neighbor”并减少性能干扰,确保关键工作负载在复杂环境中的性能,是解决云原生中“noisy neighbor”问题的关键技术。
由于笔者时间、视野、认知有限,本文难免出现错误、疏漏等问题,期待各位读者朋友、业界专家指正交流。
参考文献
1. https://www.intel.com/content/www/us/en/developer/articles/technical/noisy-neighbors-problem-in-kubernetes.html
2. https://intel.github.io/cri-resource-manager/stable/docs/index.html
3. https://builders.intel.com/docs/networkbuilders/cri-resource-manager-topology-aware-resource-assignment-in-kubernetes-technology-guide-1617431283.pdf
4. https://www.intel.com/content/www/us/en/architecture-and-technology/resource-director-technology.html
5. https://www.intel.cn/content/www/cn/zh/developer/articles/technical/introduction-to-cache-allocation-technology.html
6. https://cloud-atlas.readthedocs.io/zh_CN/latest/kernel/cpu/intel/intel_rdt/intel_rdt_arch.html