1 靈魂拷問
我們在使用 Kubernetes 時是否遇到以下情況:
- 你會不會部署負載的時候將 CPU requests/limits 設置得過低或過高?
- 你會不會部署負載的時候將 內存 requests/limits 設置得過低或過高?
- 又或者你根本不設置 requests/limits?
- request表示什么意思?limits 又是什么意思?
- CPU 設置0.5表示啥意思?為啥又有人寫 500m,這又是什么情況?
- 最佳實踐又是什么?
2 什么是 requests 和 limits?
我們都知道 Kubernetes 中最小的原子調度單位是Pod,那么就意味著資源管理和資源調度相關的屬性都應該Pod對象的字段,其中我們最常見的就是 Pod 的 CPU 和內存配置,而為了實現 Kubernetes 集群中資源的有效調度和充分利用,Kubernetes采用 requests 和 limits 兩種限制類型來對CPU和內存資源進行容器粒度的分配。
resources: limits: cpu: "1"memory: "500Mi"requests: cpu: "100m"memory: "1000Mi"
下面我們首先來了解一下上面這段 yaml 文件中字段的含義:requests 和 limits:
- requests 定義了對應的容器所需要的最小資源量。
- limits 定義了對應容器最大可以消耗的資源上限。
- cpu 等于1一般等同于1CPU 核心,1個VCPU或者一個超線程,具體要看服務器的CPU。而 limits 這里設置的 100m 則叫做100毫核,100m就表示0.1個核,所以這里也可以用0.1代替。
- memory 等于500Mi,(備注:1Mi=10241024;1M=10001000)
接下來我們來初步理解 requests 和 limits 這兩個資源限制類型,在 Kubernetes 對 CPU 和內存資源限額的設計,通常是指用戶在提交 Pod 時,可以聲明一個相對較小的 requests 值供調度器使用,而 Kubernetes 真正設置給容器 Cgroups 的,則是相對較大的 limits 值。所以一般來說,在調度的時候 requests 比較重要,在運行時 limits 比較重要。
而對應實際的業務場景來說,以 java 應用為例,requests 對應的就是JVM虛擬機所需資源的最小值,而 limits 對應的就是 JVM 虛擬機所能夠使用的資源最大值。以內存資源為例一般就是指:Xms 和 Xmx,如果 requests 值設置的小于JVM虛擬機 Xms 的值,那么就會導致 Pod 內存溢出,從而導致 Pod 被殺掉,而后重新創建一個Pod。
那么如果 CPU 資源使用超過了 limits,Pod會不會被殺掉呢?答案是不會,但是被限制。如果沒有設置 limits ,Pod 可以使用全部空閑的資源。另外如果設置了 limits而沒有設置 requests 時,Kubernetes 默認會將 requests 等于 limits。
這里通常還會將 requests 和 limits 描述的資源分為兩類:可壓縮資源(compressible resources) 和不可壓縮資源(incompressible resources)。這里不難看出CPU這類型資源為可壓縮資源,而內存這類型資源為不可壓縮資源。所以合理設置不可壓縮資源的limits值就相當重要了。
3 理解 Kubernetes 中 Pod 的 Qos
當我們理解 requests 和 limits了之后,我們來想一個問題:當某個 Node 上的內存還剩下 90Mi,這個時候就觸發了 Kubernetes 的 Eviction,這個時候 kubelet 就會挑選 Pod 進行刪除操作,那么這個時候 kubelet 挑選的依據是什么呢?
當 Kubernetes 所管理的宿主機上不可壓縮資源短缺時,就有可能觸發 Eviction。
Eviction 的默認閾值如下:
memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%
答案就是依據requests和limits值的設置方式來決定,Kubernetes會將Pod劃分成3種不同的Qos級別里面去,根據Pod不同的Qos級別來挑選。?
- 首當,其沖的就是刪除BestEffort級別的Pod,這個級別的Pod完全沒有做任何資源限制,即完全沒有設置CPU/內存的requests和limits。
- 其次,是Burstable級別的Pod,這個級別的Pod至少設置了1個CPU或者內存的requests,但又不滿足最高級別的Qos條件。
- 最后,才是 Guaranteed 級別的Pod,即Pod同時設置了CPU、內存的requests和limits,并且requests值等于limits的值。并且,Kubernetes 會保證只有當 Guaranteed 級別的 Pod 的資源使用量超過了其 limits 的限制,或者宿主機本身正處于 Memory Pressure 狀態(當宿主機的 Eviction 閾值達到后,就會進入該狀態)時,Guaranteed 級別的 Pod 才可能被選中進行 Eviction 操作。
?可以看下面的表格,以更好的理解:
?CPU requests/limits | 內存 requests/limits | Qos級別 |
---|---|---|
未設置 | 未設置 | BestEffort |
未設置 | requests < limits | Burstable |
未設置 | requests = limits | Burstable |
requests < limits | 未設置 | Burstable |
requests < limits | requests < limits | Burstable |
requests < limits | requests = limits | Burstable |
requests = limits | requests = limits | Guaranteed |
4 最佳實踐
為 namespace 設置資源配額
- ResourceQuotas 限制主要是指該 namespace 下面的所有 Pod 指定一個 requests和limits的總和要小于設置的 requests 和 limits。
- 該 namespace 下每一個容器必須指定 requests 或 limits,否則將不允許創建。
這樣子做的好處就是實現了一條隱含的規則,每個人都要遵守。
設置默認的 requests 和 limits
在生產環境中,很多負載 CPU 和內存的所需的資源基本相同,那么我們可以設置好默認的 requests 和 limits,當用戶沒有指定 requests 和 limits 值,直接使用默認值。
配置啟用CPUSET
我們知道,在使用容器的時候,你可以通過設置 cpuset 把容器綁定到某個 CPU 的核上,而不是像 cpushare 那樣共享 CPU 的計算能力。這種情況下,由于操作系統在 CPU 之間進行上下文切換的次數大大減少,容器里應用的性能會得到大幅提升。事實上,cpuset 方式,是生產環境里部署在線應用類型的 Pod 時,非常常用的一種方式。
那么如何啟用 cpuset 呢?只需要遵循以下2條規則來設置 requests 和 limits 即可:
- 設置 CPU 的 requests 和 limits 的值相等且為整數值。
- 設置 Pod 的 CPU 和內存的 requests 和 limits 值相等,也就是該 Pod 是一個Guaranteed 級別的 Pod。
高負載Pod requests 和 limits 的設置
而對于負載,流量比較高的 Pod,requests 和 limits 的設置需要根據具體的情況分析,需要分析業務的多個維度。例如
- 該服務的容器是 CPU 密集型,還是吃內存型,亦或者是 IO 密集型。
- 該服務是個單點,還是高可用的。
- 這個服務的上下游都是誰?
- 這個服務的歷史監控數據是怎么樣的?
說了這么多,貌似還是不知道怎么設置。這就給大家一個“標準答案”:
- 根據歷史的 CPU,內存,網絡,存儲等監控數據,一般 requests 值可以設定為歷史數據均值。
- limits 則設置為歷史數據均值再增加 30%-50%,當然實際設置還是要根據情況做些微調。
總結
本文主要為大家介紹了 Kubernetes 中 requests 和 limits 兩種資源限制類型來對資源進行容器粒度的分配,從而實現 Kubernetes 集群中資源的有效調度和充分利用,還提供了一些我的一些實踐。歡迎大家留言交流。