深入理解Kubernetes資源限制:CPU

2021-02-16 雲原生實驗室

由於微信公眾號不能插入外鏈,請點擊 閱讀原文 查看原文。

原文地址:https://medium.com/@betz.mark/understanding-resource-limits-in-kubernetes-cpu-time-9eff74d3161b 
作者:Mark Betz 
譯者:楊傳勝

在關於 Kubernetes 資源限制的系列文章的第一篇文章中,我討論了如何使用 ResourceRequirements 對象來設置 Pod 中容器的內存資源限制,以及如何通過容器運行時和 linux control group(cgroup)來實現這些限制。我還談到了 Requests 和 Limits 之間的區別,其中 Requests 用於在調度時通知調度器 Pod 需要多少資源才能調度,而 Limits 用來告訴 Linux 內核什麼時候你的進程可以為了清理空間而被殺死。在這篇文章中,我會繼續仔細分析 CPU 資源限制。想要理解這篇文章所說的內容,不一定要先閱讀上一篇文章,但我建議那些工程師和集群管理員最好還是先閱讀完第一篇,以便全面掌控你的集群。

1. CPU 限制

正如我在上一篇文章中提到的,CPU 資源限制比內存資源限制更複雜,原因將在下文詳述。幸運的是 CPU 資源限制和內存資源限制一樣都是由 cgroup 控制的,上文中提到的思路和工具在這裡同樣適用,我們只需要關注他們的不同點就行了。首先,讓我們將 CPU 資源限制添加到之前示例中的 yaml:

單位後綴 m 表示千分之一核,也就是說 1 Core = 1000m。因此該資源對象指定容器進程需要 50/1000 核(5%)才能被調度,並且允許最多使用 100/1000 核(10%)。同樣,2000m 表示兩個完整的 CPU 核心,你也可以寫成 2或者 2.0。為了了解 Docker 和 cgroup 如何使用這些值來控制容器,我們首先創建一個只配置了 CPU requests 的 Pod:

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

deployment.apps "limit-test" created

通過 kubectl 命令我們可以驗證這個 Pod 配置了 50m 的 CPU requests:

$ 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' ' -f1
f2321226620e

$ docker inspect f2321226620e --format '{{.HostConfig.CpuShares}}'
51

這裡顯示的為什麼是 51,而不是 50?這是因為 Linux cgroup 和 Docker 都將 CPU 核心數分成了 1024 個時間片(shares),而 Kubernetes 將它分成了 1000 個 shares。

與配置內存資源限制時 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 requests 竟然會把值傳播到 cgroup,而在上一篇文章中我們設置內存 requests 時並沒有將值傳播到 cgroup。這是因為內存的 soft limit 內核特性對 Kubernetes 不起作用,而設置了 cpu.shares 卻對 Kubernetes 很有用。後面我會詳細討論為什麼會這樣。現在讓我們先看看設置 CPU limits 時會發生什麼:

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

deployment.apps "limit-test" created

再一次使用 kubectl 驗證我們的資源配置:

$ 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' ' -f1
f2321226620e
$ docker inspect 472abbce32a5 --format '{{.HostConfig.CpuShares}} {{.HostConfig.CpuQuota}} {{.HostConfig.CpuPeriod}}'
51 10000 100000

可以明顯看出,CPU requests 對應於 Docker 容器的 HostConfig.CpuShares 屬性。而 CPU limits 就不太明顯了,它由兩個屬性控制:HostConfig.CpuPeriod 和 HostConfig.CpuQuota。Docker 容器中的這兩個屬性又會映射到進程的 cpu,couacct 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_us
100000

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

如我所說,這些值與容器配置中指定的值相同。但是這兩個屬性的值是如何從我們在 Pod 中設置的 100m cpu limits 得出的呢,他們是如何實現該 limits 的呢?這是因為 cpu requests 和 cpu limits 是使用兩個獨立的控制系統來實現的。Requests 使用的是 cpu shares 系統,cpu shares 將每個 CPU 核心劃分為 1024 個時間片,並保證每個進程將獲得固定比例份額的時間片。如果總共有 1024 個時間片,並且兩個進程中的每一個都將 cpu.shares 設置為 512,那麼它們將分別獲得大約一半的 CPU 可用時間。但 cpu shares 系統無法精確控制 CPU 使用率的上限,如果一個進程沒有設置 shares,則另一個進程可用自由使用 CPU 資源。

大約在 2010 年左右,谷歌團隊和其他一部分人注意到了這個問題。為了解決這個問題,後來在 linux 內核中增加了第二個功能更強大的控制系統:CPU 帶寬控制組。帶寬控制組定義了一個 周期,通常為 1/10 秒(即 100000 微秒)。還定義了一個 配額,表示允許進程在 cpu 上運行的那個周期內的最大時間片數。在本例中我們將 Pod 的 cpu limits 設置為 100m,這表示 100/1000 個 CPU 核心,即 100000 微秒的 CPU 時間周期中的 10000。所以該 limits 翻譯到 cpu,cpuacct cgroup 中被設置為 cpu.cfs_period_us=100000 和 cpu.cfs_quota_us=10000。順便說一下,其中的 cfs 代表 Completely Fair Scheduler(絕對公平調度),這是 Linux 系統中默認的 CPU 調度算法。還有一個實時調度算法,它也有自己相應的配額值。

現在讓我們來總結一下:

在 Kubernetes 中設置的 cpu requests 最終會被 cgroup 設置為 cpu.shares 屬性的值, cpu limits 會被帶寬控制組設置為 cpu.cfs_period_us 和 cpu.cfs_quota_us 屬性的值。與內存一樣,cpu requests 主要用於在調度時通知調度器節點上至少需要多少個 cpu shares 才可以被調度。

與 內存 requests 不同,設置了 cpu requests 會在 cgroup 中設置一個屬性,以確保內核會將該數量的 shares 分配給進程。

cpu limits 與 內存 limits 也有所不同。如果容器進程使用的內存資源超過了內存使用限制,那麼該進程將會成為 oom-killing 的候選者。但是容器進程基本上永遠不能超過設置的 CPU 配額,所以容器永遠不會因為嘗試使用比分配的更多的 CPU 時間而被驅逐。系統會在調度程序中強制進行 CPU 資源限制,以確保進程不會超過這個限制。

如果你沒有在容器中設置這些屬性,或將他們設置為不準確的值,會發生什麼呢?與內存一樣,如果只設置了 limits 而沒有設置 requests,Kubernetes 會將 CPU 的 requests 設置為 與 limits 的值一樣。如果你對你的工作負載所需要的 CPU 時間了如指掌,那再好不過了。如果只設置了 CPU requests 卻沒有設置 CPU limits 會怎麼樣呢?這種情況下,Kubernetes 會確保該 Pod 被調度到合適的節點,並且該節點的內核會確保節點上的可用 cpu shares 大於 Pod 請求的 cpu shares,但是你的進程不會被阻止使用超過所請求的 CPU 數量。既不設置 requests 也不設置 limits 是最糟糕的情況:調度程序不知道容器需要什麼,並且進程對 cpu shares 的使用是無限制的,這可能會對 node 產生一些負面影響。

最後我還想告訴你們的是:為每個 pod 都手動配置這些參數是挺麻煩的事情,kubernetes 提供了 LimitRange 資源,可以讓我們配置某個 namespace 默認的 request 和 limit 值。

2. 默認限制

通過上文的討論大家已經知道了忽略資源限制會對 Pod 產生負面影響,因此你可能會想,如果能夠配置某個 namespace 默認的 request 和 limit 值就好了,這樣每次創建新 Pod 都會默認加上這些限制。Kubernetes 允許我們通過 LimitRange 資源對每個命名空間設置資源限制。要創建默認的資源限制,需要在對應的命名空間中創建一個 LimitRange 資源。下面是一個例子:

這裡的幾個欄位可能會讓你們有些困惑,我拆開來給你們分析一下。

limits 欄位下面的 default 欄位表示每個 Pod 的默認的 limits 配置,所以任何沒有分配資源的 limits 的 Pod 都會被自動分配 100Mi limits 的內存和 100m limits 的 CPU。

defaultRequest 欄位表示每個 Pod 的默認 requests 配置,所以任何沒有分配資源的 requests 的 Pod 都會被自動分配 50Mi requests 的內存和 50m requests 的 CPU。

max 和 min 欄位比較特殊,如果設置了這兩個欄位,那麼只要這個命名空間中的 Pod 設置的 limits 和 requests 超過了這個上限和下限,就不會允許這個 Pod 被創建。我暫時還沒有發現這兩個欄位的用途,如果你知道,歡迎在留言告訴我。

LimitRange 中設定的默認值最後由 Kubernetes 中的準入控制器 LimitRanger 插件來實現。準入控制器由一系列插件組成,它會在 API 接收對象之後創建 Pod 之前對 Pod 的 Spec 欄位進行修改。對於 LimitRanger 插件來說,它會檢查每個 Pod 是否設置了 limits 和 requests,如果沒有設置,就給它配置 LimitRange 中設定的默認值。通過檢查 Pod 中的 annotations 注釋,你可以看到 LimitRanger 插件已經在你的 Pod 中設置了默認值。例如:

以上就是我對 Kubernetes 資源限制的全部見解,希望能對你有所幫助。如果你想了解更多關於 Kubernetes 中資源的 limits 和 requests、以及 linux cgroup 和內存管理的更多詳細信息,可以查看我在文末提供的參考連結。

3. 參考資料

Understanding Linux Container Scheduling

Managing Compute Resources for Containers

Red Hat Customer Portal

Chapter 1. Introduction to Control Groups (Cgroups)

Configure Default Memory Requests and Limits for a Namespace

相關焦點

  • 基於 Kubernetes 的 GPU 類型調度實現
    在實際生產集群環境中,我們不僅需要滿足不同應用對資源的使用,更是要做到不同應用對資源使用的限制,以及對不同的 namespace 分配不同的資源。而在 Kubernetes 中,我們一般會通過 ResourceQuota 資源對象來限制不同 namespace 的資源,例如:kind: ResourceQuotametadata: name: example-quota namespace: systemspec: hard: cpu: "10" memory
  • Kubernetes ELK 日誌收集
    第二種缺點就是浪費資源,因為每次啟動除了業務日誌,還有sidecar日誌要跟隨業務啟動。第一種方式使用sidecar將應用程式日誌重定向就可以在接入前面agent採集自動獲取日誌信息,不需要在配置其他。
  • 使用 Kubernetes 最易犯的 10 個錯誤
    Kubernetes 作為大規模企業級應用容器編排的首推工具,其為容器化的應用提供部署運行、資源調度、服務發現和動態伸縮等一系列完整功能,本文作者 Marek Bartik 深入分享了 K8s 的避坑寶典,相信會對開發者們大有裨益。
  • 深入解析Kubernetes service 概念
    深入解析Kubernetes service 概念Kubernetes在Kubernetes平臺上,Pod是有生命周期,為了可以給客戶端一個固定的訪問端點,因此需要在客戶端和Pod之間添加一個中間層,這個中間層稱之為ServiceService是什麼?
  • 深入理解 kubernetes iptables proxy 模式
    ://en.wikipedia.org/wiki/Netfilterhttps://zh.wikipedia.org/wiki/Iptableshttps://phoenixnap.com/kb/iptables-tutorial-linux-firewallhttps://www .cnblogs.com/charlieroro/p/9588019.htmlhttps://github.com/kubernetes
  • 使用 nice、cpulimit 和 cgroups 限制 cpu 佔用率
    Linux內核是一名了不起的馬戲表演者,它在進程和系統資源間小心地玩著雜耍,並保持系統的正常運轉。接下來,通過命令行運行:/usr/local/bin/matho-primes 0 9999999999 > /dev/null &程序運行後,將輸出從0到9999999999之間的質數。因為我們並不需要這些輸出結果,直接將輸出重定向到/dev/null就好。
  • 深入理解計算機系統系列
    本系列主要參考大學計算機系的課程安排,從數字晶片的基礎門電路說起、再到cpu的主要架構、簡單的彙編、高級語言及編譯器、作業系統的相關知識等。以上基礎文章寫完後會結合當前特別流行的虛擬概念講一下諸如jvm、kvm、docker的幾種虛擬化。目的是讓大家對計算機如何列印出「hello world」有一個貫穿軟硬體的簡單認識。
  • Kubernetes scheduler學習筆記
    name: nginx ports: - containerPort: 80 name: nginx protocol: TCP resources: requests: cpu
  • Kubernetes集群的監控報警策略最佳實踐
    如果想深入監控相關服務,則需要採取不一樣的方法。新的基礎架構層:現在服務和主機之間有一個新的層:容器和容器編排服務。這些是你需要監控的新內部服務,你的監控系統需要了解這些服務。運行容器數量可能發生變化的原因有多種:因為節點失敗或資源不足將容器重新調度到不同主機或者進行新版本的滾動部署等。如果在延長的時間段內運行的副本數或實例的數量低於我們所需的副本數,則表示某些組件工作不正常(缺少足夠的節點或資源、Kubernetes或Docker Engine故障、Docker鏡像損壞等等)。
  • Kubernetes 服務質量和 OOM 詳解
    Kubernetes 是目前最主流的容器集群管理平臺,需要整體統籌資源使用情況,將資源合理分配給 Pod 使用。
  • Docker數據卷管理和資源限制
    本文簡要介紹了Docker容器對數據卷管理以及CPU、內存和IO等系統資源限制方式1、Docker
  • 機器學習平臺在Kubernetes上的實踐
    : ${cpu}           memory: ${memory}Gi           <#if (gpu > 0)>           nvidia.com/gpu: ${gpu}                    limits:           cpu: ${cpu}           memory: ${memory}Gi           <#
  • Kubernetes RBAC角色權限控制
    資源如下PodsConfigMapsDeploymentsNodesSecretsNamespaces資源對象可能存在的操作有如下Namespace是kubernetes項目中的一個邏輯管理單位。那麼kubernetes的授權系統就能夠從這個文件裡找到對象的用戶Rolebinding對象通過roleRef欄位可以直接通過名字,來引用前面定義的Role對象(example-role),從而定義了」被作用者(Subject)」和」角色(Role)」之間的綁定關係Role和RoleBinding 他們的權限限制規則僅僅在他們自己的
  • kubernetes-issue-1:ephemeral-storage引發的pod驅逐問題
    受限圖片大小限制,有些圖片不是很清晰,可以到微信公眾號查看;目錄(1).問題現象(2).問題查證過程說明當前所在work-node資源不容許此pod部署,發生了pod驅逐。B.The node was low on resource: ephemeral-storage.說明了發生pod驅逐的原因。
  • 雲計算核心技術Docker教程:Docker CPU管理
    來源:TechWeb.com.cnDocker在默認情況下,容器使用的資源是不受限制的。也就是可以使用主機內核調度器所允許的最大資源。如果不對容器可以使用的 CPU 資源進行限制,一旦發生容器內程序異常使用 CPU 的情況,很可能把整個主機的 CPU 資源耗盡,從而導致更大的災難。本文將介紹如何設置容器可以使用的 CPU 資源。Docker 的資源限制和隔離完全基於 Linux cgroups。對 CPU 資源的限制方式也和 cgroups 相同。
  • Kubernetes持續部署指南
    這個流水線開始構建並且push鏡像到Docker Hub,它僅僅有1個block和1個job:這次,我們需要使用更好的性能,因為Docker往往更加耗費資源。它們可以同步啟動和停止,並且由於它們在同一臺機器上運行,因此它們可以共享資源。pod的問題在於它們可以隨時啟動和停止,我們沒辦法確定它們會被分配到的pod IP。要把用戶的http流量轉發,還需要提供一個公共IP和一個負載均衡器,它負責跟蹤pod和轉發客戶端的流量。打開位於deploymente.yml的文件。
  • kubernetes面試題匯總
    kubernetes是什麼?Kubernetes是什麼?kubernetes,簡稱K8s,是用8代替8個字符「ubernete」而成的縮寫。通過使用Kubernetes的Service資源,可以有多種方案實現對一個跑在Kubernetes裡的帶有多個實例的Docker應用的訪問。可以使用一個公網IP來創建一個Service,一個負載均衡Service,或者說,如果是HTTP的情況下,用一個Kubernetes的Ingress資源。
  • Docker 容器資源管理,你真的學會了嗎?
    作者 | 張晉濤責編 | 胡巍巍Docker 上手很容易,但如果將其應用於生產環境,則需要對它有更深入的理解。只有這樣,才能確保應用符合我們的預期,或在遇到問題時可及時解決。所以,要想真正掌握 Docker 的核心知識,只靠網絡上零散的信息往往是不夠的,必須系統性地學習。
  • SkyForm ECP基於Kubernetes構建AIaaS平臺
    社區中也出現了很多tensorflow on kubernetes的方案,比如最早使用jinja模板的方案(https://github.com/tensorflow/ecosystem/tree/master/kubernetes),以及後來的kubeflow(https://github.com/google/kubeflow)和Tensorflow/k8s(https://github.com