控制节点上的CPU管理策略
FEATURESTATE:Kubernetesv1.12[beta]
按照设计,Kubernetes对Pod执行相关的很多方面进行了抽象,使得用户不必关心。然而,为了正常运行,有些工作负载要求在延迟和/或性能方面有更强的保证。为此,kubelet提供方法来实现更复杂的负载放置策略,同时保持抽象,避免显式的放置指令。
在开始之前
你必须拥有一个Kubernetes的集群,同时你的Kubernetes集群必须带有kubectl命令行工具。建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。如果你还没有集群,你可以通过Minikube构建一个你自己的集群,或者你可以使用下面任意一个Kubernetes工具构建:
要检查版本,请输入kubectlversion。
CPU管理策略
默认情况下,kubelet使用CFS配额来执行Pod的CPU约束。当节点上运行了很多CPU密集的Pod时,工作负载可能会迁移到不同的CPU核,这取决于调度时Pod是否被扼制,以及哪些CPU核是可用的。许多工作负载对这种迁移不敏感,因此无需任何干预即可正常工作。
然而,有些工作负载的性能明显地受到CPU缓存亲和性以及调度延迟的影响。对此,kubelet提供了可选的CPU管理策略,来确定节点上的一些分配偏好。
配置
CPU管理策略通过kubelet参数--cpu-manager-policy或KubeletConfiguration中的cpuManagerPolicy字段来指定。支持两种策略:
CPU管理器定期通过CRI写入资源更新,以保证内存中CPU分配与cgroupfs一致。同步频率通过新增的Kubelet配置参数--cpu-manager-reconcile-period来设置。如果不指定,默认与--node-status-update-frequency的周期相同。
Static策略的行为可以使用--cpu-manager-policy-options参数来微调。该参数采用一个逗号分隔的key=value策略选项列表。此特性可以通过CPUManagerPolicyOptions特性门控来完全禁用。
策略选项分为两组:alpha质量(默认隐藏)和beta质量(默认可见)。这些组分别由CPUManagerPolicyAlphaOptions和CPUManagerPolicyBetaOptions特性门控来管控。不同于Kubernetes标准,这里是由这些特性门控来管控选项组,因为为每个单独选项都添加一个特性门控过于繁琐。
更改CPU管理器策略
由于CPU管理器策略只能在kubelet生成新Pod时应用,所以简单地从“none”更改为“static”将不会对现有的Pod起作用。因此,为了正确更改节点上的CPU管理器策略,请执行以下步骤:
腾空节点。 停止kubelet。 删除旧的CPU管理器状态文件。该文件的路径默认为/var/lib/kubelet/cpu_manager_state。这将清除CPUManager维护的状态,以便新策略设置的cpu-sets不会与之冲突。 编辑kubelet配置以将CPU管理器策略更改为所需的值。 启动kubelet。
对需要更改其CPU管理器策略的每个节点重复此过程。跳过此过程将导致kubeletcrashlooping并出现以下错误:
could not restore state from checkpoint: configured policy "static" differs from state checkpoint policy "none", please drain this node and delete the CPU manager checkpoint file "/var/lib/kubelet/cpu_manager_state" before restarting Kubelet
none策略
none策略显式地启用现有的默认CPU亲和方案,不提供操作系统调度器默认行为之外的亲和性策略。通过CFS配额来实现GuaranteedPods和BurstablePods的CPU使用限制。
static策略
static策略针对具有整数型CPUrequests的GuaranteedPod,它允许该类Pod中的容器访问节点上的独占CPU资源。这种独占性是使用cpusetcgroup控制器来实现的。
Note:诸如容器运行时和kubelet本身的系统服务可以继续在这些独占CPU上运行。独占性仅针对其他Pod。
Note:CPU管理器不支持运行时下线和上线CPUs。此外,如果节点上的在线CPUs集合发生变化,则必须驱逐节点上的Pod,并通过删除kubelet根目录中的状态文件cpu_manager_state来手动重置CPU管理器。
此策略管理一个CPU共享池,该共享池最初包含节点上所有的CPU资源。可独占性CPU资源数量等于节点的CPU总量减去通过kubelet--kube-reserved或--system-reserved参数保留的CPU资源。从1.17版本开始,可以通过kubelet--reserved-cpus参数显式地指定CPU预留列表。由--reserved-cpus指定的显式CPU列表优先于由--kube-reserved和--system-reserved指定的CPU预留。通过这些参数预留的CPU是以整数方式,按物理核心ID升序从初始共享池获取的。共享池是BestEffort和BurstablePod运行的CPU集合。GuaranteedPod中的容器,如果声明了非整数值的CPUrequests,也将运行在共享池的CPU上。只有GuaranteedPod中,指定了整数型CPUrequests的容器,才会被分配独占CPU资源。
Note:当启用static策略时,要求使用--kube-reserved或--system-reserved或--reserved-cpus来保证预留的CPU值大于零。这是因为零预留CPU值可能使得共享池变空。
当GuaranteedPod调度到节点上时,如果其容器符合静态分配要求,相应的CPU会被从共享池中移除,并放置到容器的cpuset中。因为这些容器所使用的CPU受到调度域本身的限制,所以不需要使用CFS配额来进行CPU的绑定。换言之,容器cpuset中的CPU数量与Pod规约中指定的整数型CPUlimit相等。这种静态分配增强了CPU亲和性,减少了CPU密集的工作负载在节流时引起的上下文切换。
考虑以下Pod规格的容器:
spec:
containers:
- name: nginx
image: nginx
该Pod属于BestEffortQoS类型,因为其未指定requests或limits值。所以该容器运行在共享CPU池中。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
该Pod属于BurstableQoS类型,因为其资源requests不等于limits,且未指定cpu数量。所以该容器运行在共享CPU池中。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "100Mi"
cpu: "1"
该Pod属于BurstableQoS类型,因为其资源requests不等于limits。所以该容器运行在共享CPU池中。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "200Mi"
cpu: "2"
该Pod属于GuaranteedQoS类型,因为其requests值与limits相等。同时,容器对CPU资源的限制值是一个大于或等于1的整数值。所以,该nginx容器被赋予2个独占CPU。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "1.5"
requests:
memory: "200Mi"
cpu: "1.5"
该Pod属于GuaranteedQoS类型,因为其requests值与limits相等。但是容器对CPU资源的限制值是一个小数。所以该容器运行在共享CPU池中。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
该Pod属于GuaranteedQoS类型,因其指定了limits值,同时当未显式指定时,requests值被设置为与limits值相等。同时,容器对CPU资源的限制值是一个大于或等于1的整数值。所以,该nginx容器被赋予2个独占CPU。
Static策略选项
你可以使用以下特性门控根据成熟度级别打开或关闭选项组:
静态CPUManager策略存在以下策略选项:
如果使用full-pcpus-only策略选项,static策略总是会分配完整的物理核心。默认情况下,如果不使用该选项,static策略会使用拓扑感知最适合的分配方法来分配CPU。在启用了SMT的系统上,此策略所分配是与硬件线程对应的、独立的虚拟核。这会导致不同的容器共享相同的物理核心,该行为进而会导致吵闹的邻居问题。
启用该选项之后,只有当一个Pod里所有容器的CPU请求都能够分配到完整的物理核心时,kubelet才会接受该Pod。如果Pod没有被准入,它会被置于Failed状态,错误消息是SMTAlignmentError。
如果使用distribute-cpus-across-numa策略选项,在需要多个NUMA节点来满足分配的情况下,static策略会在NUMA节点上平均分配CPU。默认情况下,CPUManager会将CPU分配到一个NUMA节点上,直到它被填满,剩余的CPU会简单地溢出到下一个NUMA节点。这会导致依赖于同步屏障(以及类似的同步原语)的并行代码出现不期望的瓶颈,因为此类代码的运行速度往往取决于最慢的工作线程(由于至少一个NUMA节点存在可用CPU较少的情况,因此速度变慢)。通过在NUMA节点上平均分配CPU,应用程序开发人员可以更轻松地确保没有某个工作线程单独受到NUMA影响,从而提高这些类型应用程序的整体性能。
可以通过将full-pcups-only=true添加到CPUManager策略选项来启用full-pcpus-only选项。同样地,可以通过将distribute-cpus-across-numa=true添加到CPUManager策略选项来启用distribute-cpus-across-numa选项。当两者都设置时,它们是“累加的”,因为CPU将分布在NUMA节点的full-pcpus块中,而不是单个核心。