理解k8s資源限制系列(二):cpu time

本文介紹幾種在K8S中限制資源使用的幾種方法。java

資源類型

在K8S中能夠對兩類資源進行限制:cpu和內存。linux

CPU的單位有:docker

  • 正實數,表明分配幾顆CPU,能夠是小數點,好比0.5表明0.5顆CPU,意思是一 顆CPU的一半時間。2表明兩顆CPU。
  • 正整數m,也表明1000m=1,因此500m等價於0.5

內存的單位:api

  • 正整數,直接的數字表明Byte
  • kKKi,Kilobyte
  • mMMi,Megabyte
  • gGGi,Gigabyte
  • tTTi,Terabyte
  • pPPi,Petabyte

方法一:在Pod Container Spec中設定資源限制

在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"

方法二:在Namespace中限定

方法一雖然很好,可是其不是強制性的,所以很容易出現因忘記設定limits/request,致使Host資源使用過分的情形,所以咱們須要一種全局性的資源限制設定,以防止這種狀況發生。K8S經過在Namespace設定LimitRange來達成這一目的。ui

配置默認request/limit

若是配置裏默認的request/limit,那麼當Pod Spec沒有設定request/limit的時候,會使用這個配置,有效避免無限使用資源的狀況。spa

配置位置在:插件

  • spec.limits[].default.cpu,default limit
  • spec.limits[].default.memory,同上
  • spec.limits[].defaultRequest.cpu,default request
  • spec.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.cpulimit必須<=某值
  • spec.limits[].max.memory,同上
  • spec.limits[].min.cpurequest必須>=某值
  • 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或內存管理的更多信息,我已經提供了一些指向下面這些主題的更詳細信息的連接。

相關文章
相關標籤/搜索