本文介绍几种在K8S中限制资源使用的几种方法。java
在K8S中能够对两类资源进行限制:cpu和内存。linux
CPU的单位有:docker
正实数
,表明分配几颗CPU,能够是小数点,好比0.5
表明0.5颗CPU,意思是一 颗CPU的一半时间。2
表明两颗CPU。正整数m
,也表明1000m=1
,因此500m
等价于0.5
。内存的单位:api
正整数
,直接的数字表明Bytek
、K
、Ki
,Kilobytem
、M
、Mi
,Megabyteg
、G
、Gi
,Gigabytet
、T
、Ti
,Terabytep
、P
、Pi
,Petabyte在K8S中,对于资源的设定是落在Pod里的Container上的,主要有两类,limits
控制上限,requests
控制下限。其位置在:frontend
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
举例:工具
apiVersion: v1 kind: Pod metadata: name: frontend spec: containers: - name: ... image: ... resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m"
方法一虽然很好,可是其不是强制性的,所以很容易出现因忘记设定limits
/request
,致使Host资源使用过分的情形,所以咱们须要一种全局性的资源限制设定,以防止这种状况发生。K8S经过在Namespace
设定LimitRange
来达成这一目的。ui
request
/limit
:若是配置里默认的request
/limit
,那么当Pod Spec没有设定request
/limit
的时候,会使用这个配置,有效避免无限使用资源的状况。spa
配置位置在:插件
spec.limits[].default.cpu
,default limitspec.limits[].default.memory
,同上spec.limits[].defaultRequest.cpu
,default requestspec.limits[].defaultRequest.memory
,同上例子:code
apiVersion: v1 kind: LimitRange metadata: name: <name> spec: limits: - default: memory: 512Mi cpu: 1 defaultRequest: memory: 256Mi cpu: 0.5 type: Container
request
/limit
的约束咱们还能够在K8S里对request
/limit
进行如下限定:
request
必须>=某值
limit
必须<=某值
这样的话就能有效避免Pod Spec中乱设limit
致使资源耗尽的状况,或者乱设request
致使Pod没法获得足够资源的状况。
配置位置在:
spec.limits[].max.cpu
,limit
必须<=某值
spec.limits[].max.memory
,同上spec.limits[].min.cpu
,request
必须>=某值
spec.limits[].min.memory
,同上例子:
apiVersion: v1
kind: LimitRange
metadata: name: <name>
spec:
limits:
- max:
memory: 1Gi
cpu: 800m
min:
memory: 500Mi
cpu: 200m
type: Container
在关于kubernetes资源限制的这个由两部分组成的系列文章的第一篇文章中,我讨论了如何使用ResourceRequirements对象来设置容器中容器的内存限制,以及容器运行时和linux控制组如何实现这些限制。我还谈到了请求之间的区别,用于在调度时通知调度程序pod的需求,以及限制,用于在主机系统处于内存压力时帮助内核强制执行使用限制。在这篇文章中,我想继续详细查看cpu时间请求和限制。阅读完第一篇文章并非从这篇文章中获取价值的先决条件,但我建议你在某些时候阅读它们,以便全面了解工程师和集群管理员可使用的控件。
CPU限制
正如我在第一篇文章中提到的,cpu限制比内存限制更复杂,缘由将在下面说明。好消息是cpu限制是由咱们刚才看到的相同cgroups机制控制的,因此全部相同的内省思想和工具都适用,咱们能够只关注差别。让咱们首先将cpu限制添加回上次查看的示例资源对象:
单位后缀m表明“千分之一核心”,所以该资源对象指定容器进程须要50/1000的核心(5%)而且容许最多使用100/1000核心(10%)。一样,2000m将是两个完整核心,也能够指定为2或2.0。让咱们建立一个只有cpu请求的pod,看看它是如何在docker和cgroup级别配置的:
咱们能够看到kubernetes配置了50m cpu请求:
咱们还能够看到docker配置了具备相同限制的容器:
为何51,而不是50?cpu控制组和docker都将核划分为1024个共享,而kubernetes将其划分为1000. docker如何将此请求应用于容器进程?与设置内存限制致使docker配置进程的内存cgroup的方式相同,设置cpu限制会致使它配置cpu,cpuacct cgroup:
Docker的HostConfig.CpuShares容器属性映射到cgroup的cpu.shares属性,因此让咱们看一下:
你可能会惊讶地发现设置cpu请求会将值传播到cgroup,由于在上一篇文章中咱们看到设置内存请求没有。底线是关于内存软限制的内核行为对kubernetes不是颇有用,由于设置cpu.shares颇有用。我将在下面详细讨论为何。那么当咱们设置cpu限制时会发生什么?咱们来看看:
如今咱们还能够在kubernetes pod资源对象中看到限制:
并在docker容器配置中:
如上所述,cpu请求存储在HostConfig.CpuShares属性中。可是,cpu限制不太明显。它由两个值表示:HostConfig.CpuPeriod和HostConfig.CpuQuota。这些docker容器配置属性映射到进程的cpu、cpuacct cgroup的两个附加属性:cpu.cfs_period_us和cpu.cfs_quota_us。咱们来看看那些:
正如预期的那样,这些值设置为与docker容器配置中指定的值相同。可是这两个属性的值是如何从咱们的pod中的100m cpu限制设置得出的,它们如何实现该限制?答案在于cpu请求和cpu限制是使用两个独立的控制系统实现的。请求使用cpu共享系统,二者中较早的一个。Cpu共享将每一个核划分为1024个切片,并保证每一个进程将得到这些切片的比例份额。若是有1024个切片,而且两个进程中的每个都将cpu.shares设置为512,那么它们将分别得到大约一半的可用时间。可是,cpu共享系统没法强制执行上限。若是一个进程不使用其共享,则另外一个进程能够自由使用。
大约在2010年,谷歌和其余人注意到这可能会致使问题。做为回应,增长了第二个功能更强大的系统:cpu带宽控制。带宽控制系统定义一个周期,一般为1/10秒,或100000微秒,以及一个配额,表示容许进程在cpu上运行的那个周期内的最大切片数。在这个例子中,咱们要求咱们的pod上的cpu限制为100m。这是100/1000的核,或100000微秒的CPU时间中的10000。所以,咱们的限制请求转换为在进程的cpu,cpuacct cgroup上设置cpu.cfs_period_us = 100000和cpu.cfs_quota_us = 10000。顺便说一下,这些名称中的cfs表明Completely Fair Scheduler,它是默认的linux cpu调度程序。还有一个实时调度程序,它有本身相应的配额值。
因此咱们已经看到在kubernetes中设置cpu请求最终会设置cpu.shares cgroup属性,而且经过设置cpu.cfs_period_us和cpu.cfs_quota_us来设置cpu限制可使用不一样的系统。与内存限制同样,请求主要对调度程序有用,调度程序使用它来查找至少具备多个可用cpu共享的节点。与内存请求不一样,设置cpu请求还会在cgroup上设置一个属性,以帮助内核实际将该数量的共享分配给进程。限制也与内存区别对待。超出内存限制使你的容器进程成为oom-killing的候选者,而你的进程基本上不能超过设置的cpu配额,而且永远不会由于尝试使用比分配的更多的CPU时间而被驱逐。系统会在调度程序中强制执行配额,以便进程在限制时受到限制。
若是你未在容器上设置这些属性,或将它们设置为不许确的值,会发生什么?与内存同样,若是设置限制但不设置请求,kubernetes会将请求默认为限制。若是你很是了解工做负载所需的CPU时间,那么这可能会很好。设置一个没有限制的请求怎么样?在这种状况下,kubernetes可以准确地安排你的pod,而且内核将确保它至少得到所请求的共享数量,可是你的进程将不会被阻止使用超过所请求的cpu数量,这将被盗来自其余进程的cpu共享(若是可用)。既不设置请求也不设置限制是最糟糕的状况:调度程序不知道容器须要什么,而且进程对cpu共享的使用是无限的,这可能会对节点产生负面影响。这是我想要谈论的最后一件事的好消息:确保命名空间中的默认限制。
默认限制
鉴于咱们刚刚讨论过关于忽略资源限制对pod容器的负面影响的全部内容,你可能认为可以设置默认值会很好,所以容许进入群集的每一个pod都至少设置了一些限制。Kubernetes容许咱们使用LimitRange v1 api对象在每一个命名空间的基础上执行此操做。要创建默认限制,请在要将其应用于的命名空间中建立LimitRange对象。这是一个例子:
这里的命名可能有点使人困惑,因此让咱们简单地把它拆掉。default key低于限制表示每一个资源的默认限制。在这种状况下,任何容许没有内存限制的命名空间的pod都将被分配100Mi的限制。任何没有cpu限制的pod都将被分配100m的限制。defaultRequest键用于资源请求。若是在没有内存请求的状况下建立pod,则会为其分配默认请求50Mi,若是没有cpu请求,则默认值为50m。max和min键有点不一样:基本上若是设置了pod,若是设置了违反这些边界的请求或限制,则不会容许pod进入命名空间。我没有找到这些的用途,但也许你有,若是是这样发表评论,让咱们知道你对他们作了什么。
LimitRange中规定的默认值由LimitRanger插件应用于pod,这是一个kubernetes许可控制器。入场控制器是插件,在api接受对象以后但在建立pod以前有机会修改podSpecs。对于LimitRanger,它会查看每一个pod,若是它没有指定命名空间中存在默认设置的给定请求或限制,则它将应用该默认值。经过检查pod元数据中的注释,你能够看到LimitRanger已在你的pod上设置了默认值。这是LimitRanger应用100m的默认cpu请求的示例:
这包含了对kubernetes资源限制的见解。我但愿这个信息对你有所帮助。若是你有兴趣阅读有关使用资源限制和默认值,linux cgroups或内存管理的更多信息,我已经提供了一些指向下面这些主题的更详细信息的连接。