了解kubernetes中的资源限制:CPU时间

  在这个由两部分组成的有关kubernetes中资源限制的系列的第一篇文章中,我讨论了如何使用ResourceRequirements对象在pod中设置容器的内存限制,以及容器运行时和linux控制组如何实现这些限制。 我还谈到了请求和限制之间的区别,这些请求用于在调度时通知调度程序Pod的需求,而限制则用于在主机系统承受内存压力时帮助内核实施使用约束。 在本文中,我想继续详细研究CPU时间要求和限制。 阅读第一篇文章并不是从这篇文章中获取价值的前提,但是我鼓励您在某个时候都阅读它们,以全面了解工程师和集群管理员可以使用的控件。

  CPU限制

  正如我在第一篇文章中提到的,cpu限制比内存限制更为复杂,原因如下所述。 好消息是,CPU限制由我们刚刚研究过的相同cgroups机制控制,因此所有相同的内省思想和工具都适用,并且我们可以专注于差异。 首先,将cpu限制重新添加到上次查看的示例资源对象中:

  resources:

  requests:

  memory: 50Mi

  cpu: 50m

  limits:

  memory: 100Mi

  cpu: 100m

  后缀m代表"千分之一内核",因此此资源对象指定容器进程需要50/1000内核(5%),并且最多允许使用100/1000内核(10%) 。 同样,2000m是两个完整的核心,也可以指定为2或2.0。 让我们创建一个仅请求cpu的Pod,看看如何在docker和cgroup级别上对其进行配置:

  $ kubectl run limit-test --image=busybox --requests "cpu=50m" --command -- /bin/sh -c "while true; do sleep 2; done"deployment.apps "limit-test" created

  我们可以看到kubernetes配置了50m cpu请求:

  $ kubectl get pods limit-test-5b4c495556-p2xkr -o=jsonpath='{.spec.containers[0].resources}'map[requests:map[cpu:50m]]

  我们还可以看到docker为容器配置了相同的限制:

  $ docker ps | grep busy | cut -d' ' -f1f2321226620e$ docker inspect f2321226620e --format '{{Config.CpuShares}}'51

  为什么是51,而不是50? cpu控制组和docker都将一个核心分成1024个份额,而kubernetes则将其分成1000个份额。docker如何将此请求应用于容器进程? 就像设置内存限制导致docker配置进程的内存cgroup一样,设置cpu限制导致docker配置进程cpu,cpuacct cgroup:

  $ ps ax | grep /bin/sh

  60554 ? Ss 0:00 /bin/sh -c while true; do sleep 2; done$ sudo cat /proc/60554/cgroup

  ...

  4:cpu,cpuacct:/kubepods/burstable/pode12b33b1-db07-11e8-b1e1-42010a800070/3be263e7a8372b12d2f8f8f9b4251f110b79c2a3bb9e6857b2f1473e640e8e75$ ls -l /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pode12b33b1-db07-11e8-b1e1-42010a800070/3be263e7a8372b12d2f8f8f9b4251f110b79c2a3bb9e6857b2f1473e640e8e75

  total 0

  drwxr-xr-x 2 root root 0 Oct 28 23:19 .

  drwxr-xr-x 4 root root 0 Oct 28 23:19 ..

  ...

  -rw-r--r-- 1 root root 0 Oct 28 23:19 cpu.shares

  Docker的HostConfig.CpuShares容器属性映射到cgroup的cpu.shares属性,因此让我们来看一下:

  $ sudo cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/podb5c03ddf-db10-11e8-b1e1-42010a800070/64b5f1b636dafe6635ddd321c5b36854a8add51931c7117025a694281fb11444/cpu.shares

  51

  您可能会惊讶地发现,设置cpu请求会将值传播到cgroup,因为在上一篇文章中我们看到设置内存请求没有。 最重要的是,关于内存软限制的内核行为对于kubernetes并不是很有用,因为设置cpu.shares很有用。 我将在下面详细讨论原因。 那么,当我们还设置CPU限制时会发生什么呢? 让我们找出:

  $ kubectl run limit-test --image=busybox --requests "cpu=50m" --limits "cpu=100m" --command -- /bin/sh -c "while true; dosleep 2; done"deployment.apps "limit-test" created

  现在我们还可以在kubernetes pod资源对象中看到限制:

  $ kubectl get pods limit-test-5b4fb64549-qpd4n -o=jsonpath='{.spec.containers[0].resources}'map[limits:map[cpu:100m] requests:map[cpu:50m]]

  并在docker容器配置中:

  $ docker ps | grep busy | cut -d' ' -f1f2321226620e$ docker inspect 472abbce32a5 --format '{{Config.CpuShares}} {{Config.CpuQuota}} {{Config.CpuPeriod}}'51 10000 100000

  如上所述,cpu请求存储在HostConfig.CpuShares属性中。 但是,CPU限制不太明显。 它由两个值表示:HostConfig.CpuPeriod和HostConfig.CpuQuota。 这些docker容器的配置属性映射到进程的cpu,cpuacct cgroup的两个附加属性:cpu.cfs_period_us和cpu.cfs_quota_us。 让我们来看看这些:

  $ sudo cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod2f1b50b6-db13-11e8-b1e1-42010a800070/f0845c65c3073e0b7b0b95ce0c1eb27f69d12b1fe2382b50096c4b59e78cdf71/cpu.cfs_period_us100000

  $ sudo cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod2f1b50b6-db13-11e8-b1e1-42010a800070/f0845c65c3073e0b7b0b95ce0c1eb27f69d12b1fe2382b50096c4b59e78cdf71/cpu.cfs_quota_us10000

  正如预期的那样,这些设置为与docker容器配置中指定的值相同。 但是,这两个属性的值如何从我们pod中的100m cpu限制设置中得出,以及它们如何实现该限制? 答案在于,使用两个独立的控制系统来实现cpu请求和cpu限制。 请求使用cpu共享系统,两者中的较早者。 Cpu份额将每个核心分成1024个切片,并确保每个进程将获得它们在这些切片中的比例份额。 如果有1024个分片,并且两个进程中的每个进程将cpu.shares设置为512,则它们各自将获得大约一半的可用时间。 但是,cpu共享系统无法强制执行上限。 如果一个进程不使用其共享,则另一个进程可以自由使用。

  在2010年左右,Google和其他公司注意到这可能会引起问题。 作为响应,添加了另一个功能更强大的系统: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代表完全公平调度程序,它是默认的Linux cpu调度程序。 还有一个实时调度程序,它具有自己的相应配额值。

  因此,我们已经看到,在kubernetes中设置cpu请求最终会设置cpu.shares cgroup属性,并且通过设置cpu.cfs_period_us和cpu.cfs_quota_us来设置cpu限制会涉及另一个系统。 与内存限制一样,该请求主要对调度程序有用,调度程序使用该请求来查找至少具有可用cpu共享数量的节点。 与设置内存请求不同,设置CPU请求还可以在cgroup上设置一个属性,该属性可以帮助内核将实际数量的份额分配给进程。 限制也不同于内存。 超出内存限制使您的容器进程成为oom-kill的候选者,而您的进程基本上不能超过设置的cpu配额,并且永远不会因为尝试使用比分配的时间更多的cpu时间而被驱逐。 系统在调度程序中强制执行配额,因此该过程只会受到限制。

  如果您没有在容器上设置这些属性,或者将它们设置为不正确的值会怎样? 与内存一样,如果您设置了限制但未设置请求,那么kubernetes会将请求默认为限制。 如果您非常了解工作量需要多少cpu时间,则可以这样做。 如何无限制地设置请求? 在这种情况下,kubernetes能够准确地调度您的pod,内核将确保它至少获得了所请求的共享数量,但是不会阻止您的进程使用超出所请求的cpu数量的资源,这将被盗用 从其他进程的cpu共享(如果有)。 既没有设置请求也没有限制是最坏的情况:调度程序不知道容器需要什么,并且进程对cpu份额的使用不受限制,这可能会对节点产生不利影响。 这是我要谈论的最后一件事的好话题:确保名称空间中的默认限制。

  默认限制

  考虑到我们刚刚讨论的所有有关忽略pod容器的资源限制的负面影响的内容,您可能会认为能够设置默认值会很好,以便每个进入集群的pod都至少设置了一些限制。 Kubernetes允许我们使用LimitRange v1 api对象在每个命名空间上做到这一点。 要建立默认限制,您可以在要应用它们的名称空间中创建LimitRange对象。 这是一个例子:

  apiVersion: v1

  kind: LimitRange

  metadata:

  name: default-limit

  spec:

  limits:

  - default:

  memory: 100Mi

  cpu: 100m

  defaultRequest:

  memory: 50Mi

  cpu: 50m

  - max:

  memory: 512Mi

  cpu: 500m

  - min:

  memory: 50Mi

  cpu: 50m

  type: Container

  这里的命名可能会有些混乱,因此让我们暂时将其删除。 限制下的默认键表示每种资源的默认限制。 在这种情况下,任何没有内存限制即可进入命名空间的吊舱都将被分配100Mi的限制。 没有cpu限制的任何吊舱将被分配100m的限制。 defaultRequest键用于资源请求。 如果创建了一个没有内存请求的Pod,则会为其分配默认请求50Mi,如果没有cpu请求,它将获得默认值50m。 max和min键有些不同:基本上,如果设置了这些键,则如果pod设置了违反这些限制的请求或限制,则不会将其接受到命名空间中。 我还没有发现它们的用处,但也许您有用,如果可以,请发表评论,让我们知道您对它们做了什么。

  LimitRange中设置的默认值由LimitRanger插件应用于Pod,后者是一个kubernetes接纳控制器。 准入控制器是插件,可以在api接受对象之后但在创建pod之前修改podSpec。 在使用LimitRanger的情况下,它将查看每个Pod,并且如果未指定给定的请求或限制(在名称空间中为其设置了默认值),它将应用该默认值。 通过检查窗格元数据中的注释,您可以看到LimitRanger在您的窗格上设置了默认值。 这是一个示例,其中LimitRanger应用了默认的100m cpu请求:

  apiVersion: v1

  kind: Pod

  metadata:

  annotations:

  kubernetes/limit-ranger: 'LimitRanger plugin set: cpu request for container

  limit-test'

  name: limit-test-859d78bc65-g6657

  namespace: default

  spec:

  containers:

  - args:

  - /bin/sh

  - -c

  - while true; do sleep 2; done

  image: busybox

  imagePullPolicy: Always

  name: limit-test

  resources:

  requests:

  cpu: 100m

  这样就结束了对kubernetes中的资源限制的了解。 我希望这个信息对您有所帮助。 如果您有兴趣阅读有关使用资源限制和默认值,Linux cgroup或内存管理的更多信息,请在下面提供一些链接,以获取有关这些主题的更多详细信息。

posted @ 2022-02-04 15:57  ebuybay  阅读(195)  评论(0编辑  收藏  举报