一篇帶給你K8s集群穩定性提升手段

提升資源利用率
一、 資源浪費場景
1、資源預留普遍存在 50% 以上的浪費
Kubernetes 中的 Request(請求) 字段用于管理容器對 CPU 和內存資源預留的機制,保證容器至少可以達到的資源量,該部分資源不能被其他容器搶占,具體可查看(https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)。當 Request 設置過小,無法保證業務的資源量,當業務的負載變高時無力承載,因此用戶通常習慣將 Request 設置得很高,以保證服務的可靠性。
但實際上,業務在大多數時段時負載不會很高。以 CPU 為例,下圖是某個實際業務場景下容器的資源預留(Request)和實際使用量(CPU_Usage)關系圖:資源預留遠大于實際使用量,兩者之間差值所對應的資源不能被其他負載使用,因此 Request 設置過大勢必會造成較大的資源浪費。

如何解決這樣的問題?現階段需要用戶自己根據實際的負載情況設置更合理的 Request、以及限制業務對資源的無限請求,防止資源被某些業務過度占用。這里可以參考后文中的 Request Quota 和 Limit Ranges 的設置。
2、 業務資源波峰波谷現象普遍,通常波谷時間大于波峰時間,資源浪費明顯
大多數業務存在波峰波谷,例如公交系統通常在白天負載增加,夜晚負載減少;游戲業務通常在周五晚上開始出現波峰,在周日晚開始出現波谷。
如下圖所示:同一業務在不同的時間段對資源的請求量不同,如果用戶設置的是固定的 Request,業務在負載較低時利用率很低。

這時可以通過動態調整副本數,以高資源利用率承載業務的波峰波谷,可以參考k8s原生提供的HPA 。
3、不同類型的業務,導致資源利用率有較大差異
在線業務通常白天負載較高,對時延要求較高,必須優先調度和運行;而離線的計算型業務通常對運行時段和時延要求相對較低,理論上可以在在線業務波谷時運行。此外,有些業務屬于計算密集型,對 CPU 資源消耗較多,而有些業務屬于內存密集型,對內存消耗較多。

如上圖所示,通過在離線混部可以動態調度離線業務和在線業務,讓不同類型業務在不同的時間段運行以提升資源利用率。對于計算密集型業務和內存密集型業務,可以使用親和性調度,為業務分配更合適的節點,以及通過taints/tolerations 策略隔離某些node給特定的業務場景使用,有效提升資源利用率。
二、 提升資源利用率的方法
主要從兩方面著手:一是利用原生的 Kubernetes 能力手動進行資源的劃分和限制;二是結合業務特性的自動化方案。這里暫時介紹利用k8s原生能力進行資源的劃分和限制。
1、 如何資源劃分和限制
設想,你是個集群管理員,現在有4個業務部門使用同一個集群,你的責任是保證業務穩定性的前提下,讓業務真正做到資源的按需使用。為了有效提升集群整體的資源利用率,這時就需要限制各業務使用資源的上限,以及通過一些默認值防止業務過量使用。
理想情況下,業務應該根據實際情況,設置合理的 Request 和 Limit。(Request 用于對資源的占位,表示容器至少可以獲得的資源;Limit 用于對資源的限制,表示容器至多可以獲得的資源。)這樣更利于容器的健康運行、資源的充分使用。但實際上用戶經常忘記設置容器對資源的 Request 和 Limit。此外,對于共享使用一個集群的團隊/項目來說,他們通常都將自己容器的 Request 和 Limit 設置得很高以保證自己服務的穩定性。

為了更細粒度的劃分和管理資源,可以設置命名空間級別的 Resource Quota 以及 Limit Ranges。
2、 使用 Resource Quota 劃分資源
如果你管理的某個集群有4個業務,為了實現業務間的隔離和資源的限制,你可以使用命名空間和 Resource Quota。
Resource Quota 用于設置命名空間資源的使用配額,命名空間是 Kubernetes 集群里面的一個隔離分區,一個集群里面通常包含多個命名空間,例如 Kubernetes 用戶通常會將不同的業務放在不同的命名空間里,你可以為不同的命名空間設置不同的 Resource Quota,以限制一個命名空間對集群整體資源的使用量,達到預分配和限制的效果。Resource Quota 主要作用于如下方面,具體可查看(https://kubernetes.io/docs/concepts/policy/resource-quotas/)。
- 計算資源:所有容器對 CPU 和 內存的 Request 以及 Limit 的總和。
- 存儲資源:所有 PVC 的存儲資源請求總和。
- 對象數量:PVC/Service/Configmap/Deployment等資源對象數量的總和。
Resource Quota 使用場景
- 給不同的項目/團隊/業務分配不同的命名空間,通過設置每個命名空間資源的 Resource Quota 以達到資源分配的目的。
- 設置一個命名空間的資源使用數量的上限以提高集群的穩定性,防止一個命名空間對資源的過度侵占和消耗。
這里提供一個腳本,來為集群各命名空間設置初始的Resource Quota。
「說明」:該腳本借助kubectl-view-allocations 插件獲取集群各個命名空間當前所有pod所設置的request和limit總和,然后在此基礎之上上浮30%用于設置命名空間的request和limit Resource Quota。
后續可通過集群具體情況,調整對應配置。
使用說明:
wget https://my-repo-1255440668.cos.ap-chengdu.myqcloud.com/ops/ResourceQuota-install.tar.gz
tar -xvf ResourceQuota-install.tar.gz
cd ResourceQuota-install && bash install.sh
執行完之后,會在當前目錄生成一個resourceQuota目錄和limitrange目錄,里面包含各個命名空間的ResourceQuota.yaml文件,便于后續根據集群實際情況,進行對應調整,調整之后再根據需要進行apply。

【注意】:如果命名空間request和limit總和超過了所設置的resourceQuota,那么將無法新建pod,而且設置了resourceQuota后,pod必須配置“limits.cpu,limits.memory,requests.cpu,requests.memory”,否則也無法正常部署,查看event會提示如下報錯:

同時可通過監控查看該ns的的資源使用情況是否合理,調整原有各pod的request和limit,以及根據情況調整所設置的resourceQuota。
CI配置reques和limit的方式如下:
yaml渲染配置如下(其中resources字段):
【推薦配置】:
java服務:request和limit配置同樣的值。
golang/python服務:request和limit配置為1:2。
#推薦配置:
#java服務:request和limit配置同樣的值
#golang/python服務:request和limit配置為1:2
resources:
cpu: 500/1000m
memory: 500/1000Mi
3、 使用 Limit Ranges 限制資源
用戶經常忘記設置資源的 Request 和 Limit,或者將值設置得很大怎么辦?作為管理員,如果可以為不同的業務設置不同資源使用默認值以及范圍,可以有效減少業務創建時的工作量同時,限制業務對資源的過度侵占。
與 Resource Quota 對命名空間整體的資源限制不同,Limit Ranges 適用于一個命名空間下的單個容器。可以防止用戶在命名空間內創建對資源申請過小或過大容器,防止用戶忘記設置容器的 Request 和 Limit。Limit Ranges 主要作用于如下方面,具體可查看(https://kubernetes.io/zh/docs/concepts/policy/resource-quotas/)。
- 計算資源:對所有容器設置 CPU 和內存使用量的范圍。
- 存儲資源:對所有 PVC 能申請的存儲空間的范圍。
- 比例設置:控制一種資源 Request 和 Limit 之間比例。
- 默認值:對所有容器設置默認的 Request/Limit,如果容器未指定自己的內存請求和限制,將為它指定默認的內存請求和限制。
Limit Ranges 使用場景
設置資源使用默認值,以防用戶遺忘,也可以避免 QoS 驅逐重要的 Pod。
不同的業務通常運行在不同的命名空間里,不同的業務通常資源使用情況不同,為不同的命名空間設置不同的 Request/Limit 可以提升資源利用率。
限制容器對資源使用的上下限,保證容器正常運行的情況下,限制其請求過多資源。
4、調度策略
Kubernetes 調度機制是 Kubernetes 原生提供的一種高效優雅的資源分配機制,它的核心功能是為每個 Pod 找到最適合它的節點,通過合理利用 Kubernetes 提供的調度能力,根據業務特性配置合理的調度策略,也能有效提高集群中的資源利用率。
4.1 節點親和性
說明
倘若你的某個業務是 CPU 密集型,不小心被 Kubernetes 的調度器調度到內存密集型的節點上,導致內存密集型的 CPU 被占滿,但內存幾乎沒怎么用,會造成較大的資源浪費。如果你能為節點設置一個標記,表明這是一個 CPU 密集型的節點,然后在創建業務負載時也設置一個標記,表明這個負載是一個 CPU 密集型的負載,Kubernetes 的調度器會將這個負載調度到 CPU 密集型的節點上,這種尋找最合適的節點的方式,將有效提升資源利用率。
創建 Pod 時,可以設置節點親和性,即指定 Pod 想要調度到哪些節點上(這些節點是通過 K8s Label)來指定的。
節點親和性使用場景
節點親和性非常適合在一個集群中有不同資源需求的工作負載同時運行的場景。比如說,k8s集群節點有 CPU 密集型的機器,也有內存密集型的機器。如果某些業務對 CPU 的需求遠大于內存,此時使用普通的 節點 機器,勢必會對內存造成較大浪費。此時可以在集群里添加一批 CPU 密集型的 節點,并且把這些對 CPU 有較高需求的 Pod 調度到這些 節點上,這樣可以提升 節點 資源的整體利用率。同理,還可以在集群中管理異構節點(比如 GPU 機器),在需要 GPU 資源的工作負載中指定需要GPU資源的量,調度機制則會幫助你尋找合適的節點去運行這些工作負載。
4.2 Taints和Tolerations(污點和容忍)
說明
我們說到的的NodeAffinity節點親和性,是在pod上定義的一種屬性,使得Pod能夠被調度到某些node上運行。Taint剛好相反,它讓Node拒絕Pod的運行。
Taint需要與Toleration配合使用,讓pod避開那些不合適的node。在node上設置一個或多個Taint后,除非pod明確聲明能夠容忍這些“污點”,否則無法在這些node上運行。Toleration是pod的屬性,讓pod能夠(注意,只是能夠,而非必須)運行在標注了Taint的node上。
創建 Pod 時,可以通過設置Taints和Tolerations,來讓某些node只允許某些特定的pod。
Taints和Tolerations場景
節點獨占
如果想要拿出一部分節點,專門給特定的應用使用,則可以為節點添加這樣的Taint:
kubectl taint nodes nodename dedicated=groupName:NoSchedule
然后給這些應用的pod加入相應的toleration,則帶有合適toleration的pod就會被允許同使用其他節點 一樣使用有taint的節點。然后再將這些node打上指定的標簽,再通過nodeSelector或者親和性調度的方式,要求這些pod必須運行在指定標簽的節點上。
具有特殊硬件設備的節點
在集群里,可能有一小部分節點安裝了特殊的硬件設備,比如GPU芯片。用戶自然會希望把不需要占用這類硬件的pod排除在外。以確保對這類硬件有需求的pod能夠順利調度到這些節點上??梢允褂孟旅娴拿顬楣濣c設置taint:
kubectl taint nodes nodename special=true:NoSchedule
kubectl taint nodes nodename special=true:PreferNoSchedule
然后在pod中利用對應的toleration來保障特定的pod能夠使用特定的硬件。然后同樣的,我們也可以使用標簽或者其他的一些特征來判斷這些pod,將其調度到這些特定硬件的服務器上。
應對節點故障
在節點故障時,可以通過TaintBasedEvictions功能自動將節點設置Taint,然后將pod驅逐。但是在一些場景下,比如說網絡故障造成的master與node失聯,而這個node上運行了很多本地狀態的應用即使網絡故障,也仍然希望能夠持續在該節點上運行,期望網絡能夠快速恢復,從而避免從這個node上被驅逐。Pod的Toleration可以這樣定義:
tolerations:
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
K8s集群穩定性提升手段,有很多,提升資源利用率只是某一種,后續還會繼續輸出其他手段的應用。




























