使用prometheus来避免Kubernetes CPU Limits造成的事故
使用prometheus来避免Kubernetes CPU Limits造成的事故
译自:Using Prometheus to Avoid Disasters with Kubernetes CPU Limits
本文将介绍Kubernetes的resource limits是如何工作的、使用哪些metrics来设置正确的limits值、以及使用哪些指标来定位CPU抑制的问题。
将limits中的CPU解释为时间概念,可以方便地理解容器中的多线程是如何使用CPU时间的。
理解Limits
在配置limits时,我们会告诉Linux节点在一个特定的周期内一个容器应用的运行时长。这样做是为了保护节点上的其余负载不受任意一组进程占用过多 CPU 周期的影响。
limits的核并不是主板上的物理核,而是配置了单个容器内的一组进程或线程在容器短暂暂停(避免影响到其他应用)前的运行时长。这句话有点违反直觉,特别是在 Kubernetes 调度器级别上很容易出错,Kubernetes 调度器使用了物理核的概念。
kubernetes 调度器在执行调度的时候用的是节点上物理核的概念,但容器运行的时候,应该将limits配置的CPU 转换为CPU时间的概念。
Limits其实是时间
下面使用一个虚构的例子来解释这个概念。假设有一个单线程应用,该应用需要1秒CPU运行时间来完成一个事务,此时将limits配置为1 core或1000 millicores:
Resources:
limits:
cpu: 1000m
如果该应用需要完整的1秒CPU运行时间来服务一个API调用,中间不能被停止或抑制,即在容器被抑制前需要允许该应用运行1000毫秒(ms)或1 CPU秒。
由于1000毫秒等同于1秒CPU运行时间,这就可以让该应用每秒不受限地运行一个完整的CPU秒,实际的工作方式更加微妙。我们将一个CPU秒称为一个周期(period),用来衡量时间块。
Linux Accounting system
Limits是一个记账系统(Accounting system),用于跟踪和限制一个容器在固定时间周期内使用的总vCPU数,该值作为可用运行时的全局池进行跟踪,一个容器可以在该周期内使用该池。上面陈述中有很多内容,下面对此进行分析。
回到周期或记账系统翻页频率的概念。我们需要跨多个 vCPU申请运行时间,这意味着需要将账簿的每页分为多个段,称为切片。Linux内核默认会将一个周期分为20个切片。
假设我们需要运行半个周期,此时只需要将配额配置为一半数目的切片即可,在一个周期之后,记账系统会重置切片,并重启进程。
类似于requests或shares可以转换为表示 CPU 分配百分比的比率,也可以将limits转换为一个百分比。例如,容器的配额设置为半个周期,则配置为:
resources:
limits:
cpu: 500m
开始时,使用1000 milliCPU作为一个完整的share。当配置500 milliCPU时,使用了半个周期,或500m/1000m = 50%。如果设置了200m/1000m,则表示使用的CPU比率为20%,以此类推。我们需要这些转换数字来理解一些prometheus的指标输出。
上面提到的记账系统是按容器计算的,下面看下指标container_spec_cpu_period
,与我们假设的实验不同,实际与容器相关的周期为100ms。
Linux有一个配置,称为cpu.cfs_period_us
,设置了账簿翻到下一页前的时间,该值表示下一个周期创建前的微秒时间。这些Linux指标会通过cAdvisor转换为prometheus指标。
撇开一些特殊场景不谈,在账簿翻页之前经过的时间并不像被限制的 CPU时间切片那样重要。
下面看下使用cpu.cfs_quota_us
指标设置的容器配额,这里配置为50毫秒,即100ms的一半:
多线程容器
容器通常具有多个处理线程,根据语言的不同,可能有数百个线程。
当这些线程/进程运行时,它们会调度不同的(可用)vCPU,Linux的记账系统需要全局跟踪谁在使用这些vCPU,以及需要将哪些内容添加到账簿中。
先不谈周期的概念,下面我们使用container_cpu_usage_seconds_total
来跟踪一个应用的线程在1秒内使用的vCPU数。假设线程在4个 vCPU 上均运行了整整一秒钟,则说明其使用了4个vCPU秒。
如果总的vCPU时间小于1个vCPU秒会发生什么呢?此时会在该时间帧内抑制节点上该应用的其他线程的运行。
Global accounting
上面讨论了如何将一个vCPU秒切分为多个片,然后就可以全局地在多个vCPU上申请时间片。让我们回到上述例子(4个线程运行在4个vCPU上),进一步理解它们如何运行的。
当一个CPU需要运行其队列中的一个线程或进程时,它首先会确认容器的全局配额中是否有5ms的时间片,如果全局配额中有足够的时间片,则会启动线程,否则,该线程会被抑制并等待下一个周期。
真实场景
下面假设一个实验,假如有4个线程,每个线程需要100ms的CPU时间来完成一个任务,将所有所需的vCPU时间加起来,总计需要400ms或4000m,因此可以以此为进程配置limit来避免被抑制。
不幸的是,实际的负载并不是这样的。这些函数的线程可能运行重的或轻的API调用。应用所需的CPU时间是变化的,因此不能将其认为是一个固定的值。再进一步,4个线程可能并不会同时各需要一个vCPU,有可能某些线程需要等待数据库锁或其他条件就绪。
正因为如此,负载往往会突然爆发,因此延迟并不总是能够成为设置limits的候选因素。最新的一个特性--cpu.cfs_burst_us允许将部分未使用的配额由一个周期转至下一个周期。
有趣的是,这并不是让大多数客户陷入麻烦的地方。假设我们只是猜测了应用程序和测试需求,并且1个 CPU 秒听起来差不多是正确的。该容器的应用程序线程将分布到4个 vCPU 上。这样做的结果是将每个线程的全局配额分为100ms/4或25ms 的运行时。
而实际的总配额为(100ms 的配额) * (4个线程)或400ms 的配额。在100毫秒的现实时间里,所有线程有300毫秒处于空闲状态。因此,这些线程总共被抑制了300毫秒。
Latency
下面从应用的角度看下这些影响。单线程应用需要100ms来完成一个任务,当设置的配额为100ms或1000 m/1000 m = 100%,此时设置了一个合理的limits,且没有抑制。
在第二个例子中,我们猜测错误,并将limits设置为400m或400 m/1000 m = 40%,此时的配额为100ms周期中的40ms。下图展示该配置了对该应用的延迟:
此时处理相同请求的时间翻倍(220ms)。该应用在三个统计周期中的两个周期内受到了抑制。在这两个周期中,应用被抑制了60ms。更重要的是,如果没有其他需要处理的线程,vCPU将会被浪费,这不仅仅会降低应用的处理速度,也会降低CPU的利用率。
与limits相关的最常见的指标container_cpu_cfs_throttled_periods_total
展示了被抑制的周期,container_cpu_cfs_periods_total
则给出了总的可用周期。上例中,三分之二(66%)的周期被抑制了。
那么,如何知道limits应该增加多少呢?
Throttled seconds
幸运的是,cAdvisor提供了一个指标container_cpu_cfs_throttled_seconds_total
,它会累加所有被抑制的5ms时间片,并让我们知道该进程超出配额的数量。指标的单位是秒,因此可以通过将该值除以10来获得100ms(即我们设置的周期)。
通过如下表达式可以找出CPU使用超过100ms的前三个pods。
topk(3, max by (pod, container)(rate(container_cpu_usage_seconds_total{image!="", instance="$instance"}[$__rate_interval]))) / 10
下面做一个实验:使用sysbench
启动一个现实时间100ms中需要400ms CPU时间的的4线程应用。
command:
- sysbench
- cpu
- --threads=4
- --time=0
- run
可以观测到使用了400ms的vCPU:
下面对该容器添加limits限制:
resources:
limits:
cpu: 2000m
memory: 128Mi
可以看到总的 CPU 使用在100ms 的现实时间中减少了一半,这正是我们所期望的。
PromQL 给出了每秒的抑制情况,每秒有10个周期(每个周期默认100ms)。为了得到每个周期的抑制情况,需要除以10。如果需要知道应该增加多少limits,则可以乘以10(如200ms * 10 = 2000m)。
topk(3, max by (pod, container)(rate(container_cpu_cfs_throttled_seconds_total{image!="", instance="$instance"}[$__rate_interval]))) / 10
总结
本文介绍了limits是如何工作的,以及可以使用哪些指标来设置正确的值,使用哪些指标来进行抑制类型的问题定位。本文的实验提出了一个观点,即过多地配置limits的vCPU数可能会导致vCPU处于idle状态而造成应用响应延迟,但在现实的服务中,一般会包含语言自身runtime的线程(如go和java)以及开发者自己启动的线程,一般设置较多的vCPU不会对应用的响应造成影响,但会造成资源浪费。
使用prometheus来避免Kubernetes CPU Limits造成的事故的更多相关文章
- 使用 Prometheus + Grafana 对 Kubernetes 进行性能监控的实践
1 什么是 Kubernetes? Kubernetes 是 Google 开源的容器集群管理系统,其管理操作包括部署,调度和节点集群间扩展等. 如下图所示为目前 Kubernetes 的架构图,由 ...
- [转帖]Prometheus+Grafana监控Kubernetes
原博客的位置: https://blog.csdn.net/shenhonglei1234/article/details/80503353 感谢原作者 这里记录一下自己试验过程中遇到的问题: . 自 ...
- Prometheus Operator 监控Kubernetes
Prometheus Operator 监控Kubernetes 1. Prometheus的基本架构 Prometheus是一个开源的完整监控解决方案,涵盖数据采集.查询.告警.展示整个监控流程 ...
- Prometheus+Grafana监控Kubernetes
涉及文件下载地址:链接:https://pan.baidu.com/s/18XHK7ex_J0rzTtfW-QA2eA 密码:0qn6 文件中需要下载的镜像需要自己提前下载好,eg:prom/node ...
- 使用Prometheus Operator 监控Kubernetes(15)
一.Prometheus概述: Prometheus是一个开源系统监测和警报工具箱. Prometheus Operator 是 CoreOS 开发的基于 Prometheus 的 Kubernete ...
- Prometheus 监控外部 Kubernetes 集群
转载自:https://www.qikqiak.com/post/monitor-external-k8s-on-prometheus/ 在实际环境中很多企业是将 Prometheus 单独部署在集群 ...
- 基于Prometheus,Alermanager实现Kubernetes自动伸缩
到目前为止Kubernetes对基于cpu使用率的水平pod自动伸缩支持比较良好,但根据自定义metrics的HPA支持并不完善,并且使用起来也不方便. 下面介绍一个基于Prometheus和Aler ...
- 解决 Prometheus 不能获取 Kubernetes 集群上 Windows 节点的 Metrics
背景 接上一篇 快速搭建 Windows Kubernetes , 我们发现原来在 Windows Kubernetes 会有一些与在 Linux 上使用不一样的体验,俗称坑,例如 hostAlias ...
- kubernetes cpu限制参数说明
docker CPU限制参数 Option Description --cpus=<value> Specify how much of the available CPU resourc ...
- Rancher2.x 一键式部署 Prometheus + Grafana 监控 Kubernetes 集群
目录 1.Prometheus & Grafana 介绍 2.环境.软件准备 3.Rancher 2.x 应用商店 4.一键式部署 Prometheus 5.验证 Prometheus + G ...
随机推荐
- 5 why 分析法,一种用于归纳抽象出解决方案的好方法
最近在看了<微信背后的产品观 - 张小龙手抄版>,其中有段话如下: 用户需求是零散的,解决方案是归纳抽象的过程 那如何归纳抽象呢?是否有一定的实践方法论呢?经过一轮探讨和学习,有这些答案: ...
- java安全之CC1浅学(1)
前言 由于CC链还是比较复杂的,我们可以先看命令执行的部分payload之后再加上反序列化部分组成一个完整的payload 调试一 项目导入依赖,这里使用3.1版本 <!-- https://m ...
- C#字典出错“集合已经修改,可能无法执行枚举操作”
出现这个现象的原因是由于线程安全考虑,如果你边对字典循环,又同时移除字典中的某个键值对, 那么将会出现这种错误,解决这种问题的方法是你没次remove某个键值对后需要break结束对字典的循环.
- 如何使用ModelBox快速提升AI应用性能?
摘要:在开发初期开发者往往聚焦在模型的精度上,性能关注较少,但随着业务量不断增加,AI应用的性能往往成为瓶颈,此时对于没有性能优化经验的开发者来说往往需要耗费大量精力做优化性能,本文为开发者介绍一些常 ...
- 网络yum源下载
思路一: 按照本地网罗源,然后使用reposync直接将源同步下载到本地 wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/re ...
- 研究光度立体法阶段性小结和优化(可20ms获取4个2500*2000灰度图的Normal Map)。
这个东西是我接触的第一个非2D方面的算法,到目前为止其实也没有完全搞定,不过可能短时间内也无法突破.先把能搞定的搞定吧. 这个东西也有一大堆参考资料,不过呢,搜来搜去其实也就那些同样的东西,个人觉得就 ...
- py周结04
py周结04 异常类型,理语法结构及实践案例 1.异常类型 SyntaxError 语法错误 NameError 名字错误 IndexError 指数错误 KeyError 关键字错误 Indenta ...
- 【Java SE】Day01 前言、入门程序、常量、变量
回顾一下Java之前学的内容 Day01 前言.入门程序.常量.变量 一.基础知识 莱布尼茨发明二进制,辗转相除与8421位权法互转,1B=1bit=1字节=8位=8byte dos cls清屏dir ...
- tcp/udp 协议特性和三次握手
一.TCP/UDP协议特性1)TCP特性:工作在传输层.建立连接.可靠的.错误检查 2)UDP特性:工作在传输层.不需要连接.不可靠的.有限的错误检查.传输性能高 2.控制位及确认号解释 控制位:由6 ...
- dubbo2升级到dubbo3实践
dubbo当前版本 2.7.3 期望升级到 3.0.11. 升级过程 maven依赖变更 <dependency> <groupId>org.apache.dubbo</ ...