Kubelet从入门到放弃:识透CPU管理

<Kubelet从入门到放弃>系列将对Kubelet组件由基础知识到源码进行深刻梳理。在这篇文章中zouyee会介绍CPU的相关概念以及Kubelet组件CPU Manager的源码。关于《Kubernetes调度框架系列》剩余的配置及源码部分,将陆续放出。html

1、背景介绍node

1.1 需求说明nginx

默认状况下,kubelet 基于CFS 调度算法来执行 Pod 的 CPU 分配。 可是当节点上运行了 CPU 密集型 Pod 时,应用可能会因抢占等状况致使 CPU 切换,而上述的切换致使的延时与中断对于业务敏感性Pod是没法接受的。

为了解决上述问题。 Kubelet 提供了可选的 CPU 管理策略,以知足不一样的业务场景。
复制代码
  • 以API形式向用户提供配置方案
  • 同时支持多种CPU使用场景共存
  • CPU基于亲和性分配时,考虑设备拓扑

1.2 CPU架构git

a. SMTgithub

同时多线程Simultaneous multithreading,简称SMT,SMT可经过复制处理器上的结构状态,让同一个处理器上的多个线程同步执行并共享处理器的执行资源,可最大限度地实现宽发射、乱序的超标量处理,提升处理器运算部件的利用率,缓和因为数据相关或Cache未命中带来的访问内存延时。当没有多个线程可用时,SMT处理器几乎和传统的宽发射超标量处理器同样。多线程技术则能够为高速的运算核心准备更多的待处理数据,减小运算核心的闲置时间。 Intel的hyper-threading其实就是 two-thread SMT.算法

b. CMPjson

片上多处理器(Chip multiprocessors,简称CMP,其思想是将大规模并行处理器中的SMP(对称多处理器)集成到同一芯片内,各个处理器并行执行不一样的进程。因为CMP结构已经被划分红多个处理器核来设计,每一个核都比较简单,有利于优化设计。多核处理器能够在处理器内部共享缓存,提升缓存利用率,同时简化多处理器系统设计的复杂度。api

c. SMP缓存

对称多处理器(Symmetric Multi-Processors,简称SMP),其是指在一个计算机上聚集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。共享存储型多处理机有三种模型:均匀存储器存取(Uniform-Memory-Access,简称UMA)模型、非均匀存储器存取(Non-uniform Memory Access,简称NUMA)模型和只用高速缓存的存储器结构(Cache-Only Memory Architecture,简称COMA)模型,这些模型的区别在于存储器和外围资源如何共享或分布。服务器

  • S2MP全称为可扩展共享存储多处理(Scalable Shared-Memory Multiprocessing)技术。S2MP系统将大量高性能微处理器链接起来,共享一个统一的地址空间,较好地解决其余并行处理系统没法解决的问题。
  • MMP也被称为海量并行处理架构。MPP提供了另一种进行系统扩展的方式,它由多个SMP服务器经过必定的节点互联网络进行链接,协同工做,完成相同的任务,从用户的角度来看是一个服务器系统。

1.3 相关技术

在CPU管理中,涉及NUMA、HT及cpuset技术,如下为简要介绍。

NUMA

NUMA,之内存访问的不一致性为代价,减轻对总线和memory的带宽需求。这种结构对进程调度算法的要求较高,尽可能减小跨Node的内存访问次数,以提高系统性能。Core之间会共享总线、内存等资源。若是Core的数量较少,则没什么问题,但随着Core的增多,对总线以及内存带宽的需求就会显著增大,最终总线和内存会成为系统性能的瓶颈。

以下图所示,一个NUMA Node包括一个或者多个Socket,以及与之相连的local memory。一个多核的Socket有多个Core。若是CPU支持HT,OS还会把这个Core当作 2个Logical Processor。

  • Socket是一个物理上的概念,指的是主板上的cpu插槽
  • Node是一个逻辑上的概念,上图中没有说起。因为SMP体系中各个CPU访问内存只能经过单一的通道,致使内存访问成为瓶颈,cpu再多也无用。后来引入了NUMA,经过划分node,每一个node有本地RAM,这样node内访问RAM速度会很是快。但跨Node的RAM访问代价会相对高一点,咱们用Node之间的距离(Distance,抽象的概念)来定义各个Node之间互访资源的开销。
  • Core就是一个物理cpu,一个独立的硬件执行单元,好比寄存器,计算单元等
  • Thread就是超线程(HyperThreading)的概念,是一个逻辑cpu,共享core上的执行单元

HT

Hyperthreading 使操做系统认为处理器的核心数是实际核心数的2倍,超线程(hyper-threading)本质上就是CPU支持的同时多线程(simultaneous multi-threading)技术,简单理解就是对CPU的虚拟化,一颗物理CPU能够被操做系统当作多颗CPU来使用。Hyper-threading只是一种“欺骗”手段。

cpuset

cpuset做为cgroup的子系统,主要用于numa架构,用于设置cpu的亲和性,为 cgroup 中的 task 分配独立的 CPU和内存等。

cpuset使用sysfs提供用户态接口,能够经过普通文件读写,工做流程为:cpuset调用sched_setaffinity来设置进程的cpu、内存的亲和性,调用mbind和set_mempolicy来设置内存的亲和性。

1.4 数听说明

a. Numa Node

numactl是设定进程NUMA策略的命令行工具,也能够用来查看当前的Nuwa node:

[root@paasn2 ~]# numactl -H
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 16047 MB
node 0 free: 3693 MB
node distances:
node   0
  0:  10
复制代码

从上面能够看出本机有一个Numa node(操做系统配置numa=on效果同样...),若是要进一步知道一个Node包含哪几个CPU,该怎么办?

一种方法是经过查看ls /sys/devices/system/node/目录下的信息,例如:

[root@paasn2 ~]# ls /sys/devices/system/node/
has_cpu  has_normal_memory  node0  online  possible  power  uevent
[root@paasn2 ~]# ls /sys/devices/system/node/node0
compact    cpu0       cpu1       cpu2       cpu3       cpu4       cpu5       
cpu6       cpu7       cpulist    cpumap     distance   hugepages  
...
复制代码

可见, node0包含0/1/2/3/4/5/6/7八个Processor。

b、查看Socket

一个Socket对应主板上的一个插槽,在本文中是指一个CPU封装。在/proc/cpuinfo中的physical id就是Socket的ID,能够从中找到本机到底有多少个Socket,而且每一个Socket有那几个Processor。

  1. 查看Socket数量

    grep 'physical id' /proc/cpuinfo | awk -F: '{print2 | "sort -un"}' 0 1 grep 'physical id' /proc/cpuinfo | awk -F: '{print2 | "sort -un"}' | wc -l 2

2)查看每一个Socket有几个Processor

$ grep 'physical id' /proc/cpuinfo | awk -F: '{print $2}' | sort | uniq -c
      4  0
      4  1
复制代码
  1. 查看Socket对应哪几个Processor

    awk -F: '{ if (1 ~ /processor/) { gsub(/ /,"", 2 ) ; p i d = 2); p_id= 2; } else if (1 ~ /physical id/){ gsub(/ /,"",2); s_id=$2; arr[s_id]=arr[s_id] " " p_id } }

    END{ for (i in arr) print arr[i]; }' /proc/cpuinfo | cut -c2- 0 1 2 3 4 5 6 7

4)Core

/proc/cpuinfo文件中的cpu cores代表一个socket中有几个cores,例如:

cat /proc/cpuinfo | grep 'core'  | sort -u
core id		: 0
core id		: 1
core id		: 2
core id		: 3
cpu cores	: 4
复制代码
  1. Logical Processor

查看Processors的个数就比较简单了,从上面的统计结果中咱们已经能够知道有8个Logical processor,不过也能够直接从/proc/cpuinfo文件中获取:

$ grep 'processor' /proc/cpuinfo | wc -l
8
复制代码

其实,每一个socket中能有几个processor也能够从siblings字段中获取:

$ grep 'siblings' /proc/cpuinfo | sort -u
siblings	: 4
复制代码

1.5 结构体

须要注意的是,Kubelet内部启动cadvisor Manager,封装cadvisor接口为cadvisor.Interface,其对外暴露MachineInfo() (*cadvisorapi.MachineInfo, error)方法,CPU manager经过cadvisor的MachineInfo结构体信息生产CPU 拓扑信息, 具体实现为调用GetNodesInfo

github.com/google/cadv…

三个关键的结构体定义以下:

github.com/google/cadv…

type MachineInfo struct {
...
	// Machine Topology
	// Describes cpu/memory layout and hierarchy.
	Topology []Node `json:"topology"`
...
}

type Node struct {
	Id int `json:"node_id"`
	// Per-node memory
	Memory    uint64          `json:"memory"`
	HugePages []HugePagesInfo `json:"hugepages"`
	Cores     []Core          `json:"cores"`
	Caches    []Cache         `json:"caches"`
}

type Core struct {
	Id       int     `json:"core_id"`
	Threads  []int   `json:"thread_ids"`
	Caches   []Cache `json:"caches"`
	SocketID int     `json:"socket_id"`
}
复制代码

说明以下:

  • Node对应NUMA节点,其中ID由/sys/devices/system/node/node i 中的 i中的 i得到
  • Core对应 Core,其中ID由/sys/devices/system/node/node i / c p u i/cpu j/topology/core_id得到
  • Threads对应Logical Processor,其中ID由/sys/devices/system/node/node i / c p u i/cpu j中的$j得到
  • SocketID对应Socket,其中ID由/sys/devices/system/node/node i / c p u i/cpu j/topology/physical_package_id得到

2、功能介绍

注:其中涉及到的拓扑管理、设备管理等内容,后续有针对性文章进行介绍,此处带过。

CPU 管理器(CPU Manager)做为 alpha 特性引入 Kubernetes 1.8 版本,Kubernetes 1.12进入beta版本后,默认开启。CPU 管理策略经过 kubelet 参数 --cpu-manager-policy 来指定。支持两种策略:

  • none: 默认策略,保持现有的调度行为。
  • static: 节点上知足某些资源特征的 Pod 根据 CPU 亲和性和独占性进行分配。

CPU 管理器(即:经过goroutine方式执行reconcileState方法)按期经过 CRI(即containerRuntime) 写入资源更新,以保证内存中 CPU 分配与 cgroupfs 一致(可参考第三节)。 同步频率经过新增的 Kubelet 配置参数 --cpu-manager-reconcile-period 来设置。 若是不指定,默认与 --node-status-update-frequency 的周期相同。关于CPU管理器须要注意如下几点:

  • 像容器运行时和 kubelet 此类的系统服务能够继续在这些独占 CPU 上运行。独占性仅针对通常 Pod。
  • CPU 管理器不支持offline/online CPUs热更新。此外,若是节点上的 CPUs 发生变化, 则必须驱逐 Pod,并经过删除 kubelet 配置的 cpu_manager_state 文件以重置 CPU 管理器。
  • 当启用 static 策略时, kube-reserved 加上 system-reserved 或 reserved-cpus 设置的 CPU 值大于零。

当前支持如下两种策略:

a. none

none 策略显式地启用现有的默认 CPU 亲和方案。 经过 CFS 配额来实现 Guaranteed pods的 CPU 使用限制。

b. static

static 策略针对具备整数型 CPU请求 的 Guaranteed Pod (后续文章介绍),它容许该类 Pod 中的容器独占 CPU 资源。其基于 cpuset cgroup 控制器 实现的

从1.17版本开始,CPU保留列表能够经过 kublet 的 --reserved-cpus参数设置, 而且--reserved-cpus 指定的CPU列表优先级高于--kube-reserved 和 --system-reserved 参数指定的保留CPU,若同时指定,将进行覆盖。

- `static`策略管理一个CPU共享资源池,起初,该资源池包含节点上全部的 CPU 资源。可用且独占的CPU 数量等于节点的 CPU总量减去经过 `reserved-cpus`或`--kube-reserved` 或 `--system-reserved` 命令行保留的 CPU(其实还有eviction资源,但当前不支持CPU类型,所以省略)。
- 经过这些参数预留的 CPU 是以整数方式,按物理内核 ID 升序从初始共享池获取的。 共享池是 `BestEffort` 和 `Burstable` pod 运行的CPU 集合。`Guaranteed` Pod 中的容器,若是声明了非整数值的 CPU `requests` ,也将运行在共享池的 CPU 上。只有 指定了正整数型的 CPU `requests` 的`Guaranteed` Pod ,才能独占 CPU 资源
- 当 `Guaranteed` Pod 调度到节点上时,若是其容器符合独占要求, 相应的CPU会从共享池中移除,并放置到容器的cpuset 中。 容器cgroup目录的 cpuset文件 中的 CPU 数量与 Pod 中指定的 CPU `limit` 相等。 这种分配加强了CPU亲和性,减小了CPU上下文切换。
复制代码

由于其 requests 值与 limits相等,下述Pod属于Guaranteed QoS类型, 且容器对 CPU 资源的限制值是正整数值。符合独占要求,所以该 nginx 容器独占2个CPU。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "200Mi"
        cpu: "2"
        
复制代码

点击查看全文

后续相关内容,请查看公众号:DCOS

4、参考资料

一、cpuset

二、cpu topology

三、cpu manager policy

相关文章
相关标签/搜索