瞭解kubernetes中的資源限制:CPU時間

在這個由兩部分組成的有關kubernetes中資源限制的系列的第一篇文章中,我討論瞭如何使用ResourceRequirements對象在pod中設置容器的內存限制,以及容器運行時和linux控制組如何實現這些限制。 我還談到了請求和限制之間的區別,這些請求用於在調度時通知調度程序Pod的需求,而限制則用於在主機系統承受內存壓力時幫助內核實施使用約束。 在本文中,我想繼續詳細研究CPU時間要求和限制。 閱讀第一篇文章並不是從這篇文章中獲取價值的前提,但是我鼓勵您在某個時候都閱讀它們,以全面瞭解工程師和集群管理員可以使用的控件。

瞭解kubernetes中的資源限制:CPU時間

CPU限制

正如我在第一篇文章中提到的,cpu限制比內存限制更為複雜,原因如下所述。 好消息是,CPU限制由我們剛剛研究過的相同cgroups機制控制,因此所有相同的內省思想和工具都適用,並且我們可以專注於差異。 首先,將cpu限制重新添加到上次查看的示例資源對象中:

<code>resources:
  requests:
    memory: 50Mi
    cpu: 50m
  limits:
    memory: 100Mi
    cpu: 100m/<code>

後綴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 '{{.HostConfig.CpuShares}}'51

為什麼是51,而不是50? cpu控制組和docker都將一個核心分成1024個份額,而kubernetes則將其分成1000個份額。docker如何將此請求應用於容器進程? 就像設置內存限制導致docker配置進程的內存cgroup一樣,設置cpu限制導致docker配置進程cpu,cpuacct cgroup:

<code>$ 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/<code>

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 '{{.HostConfig.CpuShares}} {{.HostConfig.CpuQuota}} {{.HostConfig.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對象。 這是一個例子:

<code>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/<code>

這裡的命名可能會有些混亂,因此讓我們暫時將其刪除。 限制下的默認鍵表示每種資源的默認限制。 在這種情況下,任何沒有內存限制即可進入命名空間的吊艙都將被分配100Mi的限制。 沒有cpu限制的任何吊艙將被分配100m的限制。 defaultRequest鍵用於資源請求。 如果創建了一個沒有內存請求的Pod,則會為其分配默認請求50Mi,如果沒有cpu請求,它將獲得默認值50m。 max和min鍵有些不同:基本上,如果設置了這些鍵,則如果pod設置了違反這些限制的請求或限制,則不會將其接受到命名空間中。 我還沒有發現它們的用處,但也許您有用,如果可以,請發表評論,讓我們知道您對它們做了什麼。

LimitRange中設置的默認值由LimitRanger插件應用於Pod,後者是一個kubernetes接納控制器。 准入控制器是插件,可以在api接受對象之後但在創建pod之前修改podSpec。 在使用LimitRanger的情況下,它將查看每個Pod,並且如果未指定給定的請求或限制(在名稱空間中為其設置了默認值),它將應用該默認值。 通過檢查窗格元數據中的註釋,您可以看到LimitRanger在您的窗格上設置了默認值。 這是一個示例,其中LimitRanger應用了默認的100m cpu請求:

<code>apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/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/<code>

這樣就結束了對kubernetes中的資源限制的瞭解。 我希望這個信息對您有所幫助。 如果您有興趣閱讀有關使用資源限制和默認值,Linux cgroup或內存管理的更多信息,請在下面提供一些鏈接,以獲取有關這些主題的更多詳細信息。


(本文翻譯自Mark Betz的文章《Understanding resource limits in kubernetes: cpu time》,參考:https://medium.com/@betz.mark/understanding-resource-limits-in-kubernetes-cpu-time-9eff74d3161b)


分享到:


相關文章: