通常,總是需要為應用程式提供專用的資源池,以使其正確、穩定地運作。 但是,如果多個應用程式在同一電源上運行怎麼辦? 如何為他們每個人提供最少的必要資源? 如何限制資源消耗? 如何在節點之間正確分配負載? 當應用負載增加時,如何確保水平擴展機制發揮作用?
您需要從系統中存在哪些主要資源類型開始 - 這當然是處理器時間和 RAM。 在 k8s 清單中,這些資源類型以以下單位進行測量:
- CPU - 核心數
- RAM - 以位元組為單位
此外,對於每種資源,可以設定兩種類型的要求 - 請求 и 範圍。 請求 - 描述運行容器(以及整個 Pod)的節點可用資源的最低要求,而限制則對容器可用的資源設定硬性限制。
重要的是要理解清單不必明確定義這兩種類型,但行為如下:
- 如果僅明確指定資源的限制,則對該資源的請求將自動採用等於限制的值(您可以透過呼叫描述實體來驗證這一點)。 那些。 事實上,容器將被限制為其運作所需的相同數量的資源。
- 如果僅對資源明確指定請求,則不會對該資源設定上限 - 即。 容器僅受節點本身資源的限制。
不僅可以在特定容器層級配置資源管理,還可以使用下列實體在命名空間層級配置資源管理:
- 限制範圍 — 描述 ns 中容器/pod 等級的限制策略,為了描述容器/pod 的預設限制,以及防止建立明顯胖的容器/pod(反之亦然),限制其數量,需要使用該限制策略並確定限制和請求中的值可能存在的差異
- 資源配額 — 描述 ns 中所有容器的一般限制策略,通常用於分隔環境之間的資源(當環境在節點層級沒有嚴格劃分時有用)
以下是設定資源限制的清單範例:
-
在特定容器層級:
containers: - name: app-nginx image: nginx resources: requests: memory: 1Gi limits: cpu: 200m
那些。 在這種情況下,要運行具有 nginx 的容器,節點上至少需要 1G 的可用 RAM 和 0.2 個 CPU,而容器最多可以消耗 0.2 個 CPU 和節點上的所有可用 RAM。
-
在整數層級 ns:
apiVersion: v1 kind: ResourceQuota metadata: name: nxs-test spec: hard: requests.cpu: 300m requests.memory: 1Gi limits.cpu: 700m limits.memory: 2Gi
那些。 預設ns中所有請求容器的總和不能超過CPU 300m,OP 1G,所有限制總和為CPU 700m,OP 2G。
-
ns 中容器的預設限制:
apiVersion: v1 kind: LimitRange metadata: name: nxs-limit-per-container spec: limits: - type: Container defaultRequest: cpu: 100m memory: 1Gi default: cpu: 1 memory: 2Gi min: cpu: 50m memory: 500Mi max: cpu: 2 memory: 4Gi
那些。 在所有容器的預設命名空間中,CPU 請求設定為 100m,OP 請求設定為 1G,限制為 1 個 CPU 和 2G。 同時,也對CPU(50m < x < 2)和RAM(500M < x < 4G)的request/limit中可能的值設定了限制。
-
Pod 等級限制 ns:
apiVersion: v1 kind: LimitRange metadata: name: nxs-limit-pod spec: limits: - type: Pod max: cpu: 4 memory: 1Gi
那些。 對於預設 ns 中的每個 pod,將有 4 個 vCPU 和 1G 的限制。
現在我想告訴你設定這些限制可以為我們帶來什麼好處。
節點間負載平衡機制
大家知道,k8s組件負責pod在節點之間的分發,例如 調度,它根據特定的演算法工作。 該演算法在選擇要啟動的最佳節點時經歷兩個階段:
- 過濾
- 測距
那些。 根據所描述的策略,最初選擇可以基於一組啟動 Pod 的節點 謂詞 (包括檢查節點是否有足夠的資源來運行 pod - PodFitsResources),然後對於每個節點,根據 優先級 獎勵積分(包括,節點擁有的空閒資源越多,分配的積分就越多 - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation),並在積分最多的節點上啟動 pod(如果多個節點同時滿足此條件,則隨機選擇一個)。
同時,您需要了解調度程序在評估節點的可用資源時,是由儲存在 etcd 中的資料指導的 - 即在該節點上執行的每個 Pod 所要求/限制的資源量,而不是實際的資源消耗量。 該資訊可以從命令輸出中獲取 kubectl describe node $NODE
例如:
# kubectl describe nodes nxs-k8s-s1
..
Non-terminated Pods: (9 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE
--------- ---- ------------ ---------- --------------- ------------- ---
ingress-nginx nginx-ingress-controller-754b85bf44-qkt2t 0 (0%) 0 (0%) 0 (0%) 0 (0%) 233d
kube-system kube-flannel-26bl4 150m (0%) 300m (1%) 64M (0%) 500M (1%) 233d
kube-system kube-proxy-exporter-cb629 0 (0%) 0 (0%) 0 (0%) 0 (0%) 233d
kube-system kube-proxy-x9fsc 0 (0%) 0 (0%) 0 (0%) 0 (0%) 233d
kube-system nginx-proxy-k8s-worker-s1 25m (0%) 300m (1%) 32M (0%) 512M (1%) 233d
nxs-monitoring alertmanager-main-1 100m (0%) 100m (0%) 425Mi (1%) 25Mi (0%) 233d
nxs-logging filebeat-lmsmp 100m (0%) 0 (0%) 100Mi (0%) 200Mi (0%) 233d
nxs-monitoring node-exporter-v4gdq 112m (0%) 122m (0%) 200Mi (0%) 220Mi (0%) 233d
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 487m (3%) 822m (5%)
memory 15856217600 (2%) 749976320 (3%)
ephemeral-storage 0 (0%) 0 (0%)
在這裡,我們可以看到特定節點上運行的所有 Pod,以及每個 Pod 請求的資源。 這是啟動cronjob-cron-events-1573793820-xt6q9 pod 時調度程序日誌的樣子(當您在啟動命令參數中設定第10 個日誌記錄等級-v=10 時,此資訊將出現在調度程序日誌中) :
紀錄
I1115 07:57:21.637791 1 scheduling_queue.go:908] About to try and schedule pod nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.637804 1 scheduler.go:453] Attempting to schedule pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.638285 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s5 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638300 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s6 is allowed, Node is running only 20 out of 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s3 is allowed, Node is running only 20 out of 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s4 is allowed, Node is running only 17 out of 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638365 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s12 is allowed, Node is running only 9 out of 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s11 is allowed, Node is running only 11 out of 110 Pods.
I1115 07:57:21.638385 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s1 is allowed, Node is running only 19 out of 110 Pods.
I1115 07:57:21.638402 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s2 is allowed, Node is running only 21 out of 110 Pods.
I1115 07:57:21.638383 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638335 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, Node is running only 18 out of 110 Pods.
I1115 07:57:21.638408 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s13 is allowed, Node is running only 8 out of 110 Pods.
I1115 07:57:21.638478 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638505 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638577 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638583 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s7 is allowed, Node is running only 25 out of 110 Pods.
I1115 07:57:21.638932 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 9
I1115 07:57:21.638946 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 8
I1115 07:57:21.638961 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 9
I1115 07:57:21.638971 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7
I1115 07:57:21.638975 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 8
I1115 07:57:21.638990 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7
I1115 07:57:21.639022 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639030 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639034 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639041 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639053 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639059 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639061 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639063 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639073 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639077 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639085 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639088 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639103 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639109 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639114 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639127 1 generic_scheduler.go:781] Host nxs-k8s-s10 => Score 100037
I1115 07:57:21.639150 1 generic_scheduler.go:781] Host nxs-k8s-s8 => Score 100034
I1115 07:57:21.639154 1 generic_scheduler.go:781] Host nxs-k8s-s9 => Score 100037
I1115 07:57:21.639267 1 scheduler_binder.go:269] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10"
I1115 07:57:21.639286 1 scheduler_binder.go:279] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10": all PVCs bound and nothing to do
I1115 07:57:21.639333 1 factory.go:733] Attempting to bind cronjob-cron-events-1573793820-xt6q9 to nxs-k8s-s10
在這裡我們看到,調度程式最初會過濾並產生可以啟動它的 3 個節點的清單(nxs-k8s-s8、nxs-k8s-s9、nxs-k8s-s10)。 然後,它根據每個節點的多個參數(包括 BalancedResourceAllocation、LeastResourceAllocation)計算分數,以確定最合適的節點。 最終,Pod 被調度到具有最高點數的節點上(這裡兩個節點同時具有相同的點數 100037,因此會隨機選擇一個 - nxs-k8s-s10)。
產量:如果一個節點運行沒有設定任何限制的 pod,那麼對於 k8s(從資源消耗的角度來看)這將相當於該節點上根本沒有這樣的 pod。 因此,如果你有條件有一個pod 有貪吃進程(例如wowza),並且沒有對其設置任何限制,那麼可能會出現這樣一種情況:這個pod 實際上吃掉了該節點的所有資源,但是對於k8s 來說這個節點被視為已卸載,並且在排名時將獲得與沒有工作Pod 的節點相同數量的分數(準確地說是評估可用資源的分數),這最終可能導致節點之間的負載分佈不均勻。
Pod 的驅逐
如您所知,每個 pod 都被分配了 3 個 QoS 類別之一:
- 有保證的 — 當為 pod 中的每個容器指定記憶體和 cpu 的請求和限制時分配,並且這些值必須匹配
- 可爆裂的 — Pod 中至少有一個容器有請求和限制,且請求 < 限制
- 盡力而為 — 當 pod 中沒有一個容器資源受限時
同時,當節點遇到資源(磁碟、記憶體)不足時,kubelet 開始根據考慮 pod 優先權及其 QoS 類別的特定演算法對 pod 進行排名和驅逐。 例如,如果我們談論 RAM,則根據 QoS 等級,根據以下原則獎勵積分:
- 保證:-998
- 最大努力:1000
- 爆裂: min(max(2, 1000 - (1000 * 記憶體請求位元組) / machineMemoryCapacityBytes), 999)
那些。 具有相同的優先級,kubelet 將首先從節點中驅逐具有盡力服務 QoS 級別的 pod。
產量:如果您想減少所需 pod 在缺少資源時被從節點驅逐的可能性,那麼除了優先順序之外,您還需要注意為其設定請求/限制。
應用程式 Pod 水平自動縮放機制 (HPA)
當任務是根據資源的使用情況(系統 - CPU/RAM 或使用者 - rps)自動增加和減少 pod 數量時,例如 k8s 實體 HPA (水平 Pod 自動縮放器)。 其演算法如下:
- 確定觀察到的資源的當前讀數(currentMetricValue)
- 確定資源的期望值(desiredMetricValue),系統資源的期望值使用 request 設定
- 確定目前副本數量(currentReplicas)
- 以下公式計算所需的副本數量(desiredReplicas)
desiredReplicas = [ currentReplicas * ( currentMetricValue /desiredMetricValue )]
在這種情況下,當係數(currentMetricValue /desiredMetricValue)接近1時,不會發生縮放(這種情況下,我們可以自行設定允許誤差,預設為0.1)。
讓我們使用 app-test 應用程式(描述為部署)的範例來看看 hpa 是如何運作的,其中需要根據 CPU 消耗來更改副本數量:
-
應用清單
kind: Deployment apiVersion: apps/v1beta2 metadata: name: app-test spec: selector: matchLabels: app: app-test replicas: 2 template: metadata: labels: app: app-test spec: containers: - name: nginx image: registry.nixys.ru/generic-images/nginx imagePullPolicy: Always resources: requests: cpu: 60m ports: - name: http containerPort: 80 - name: nginx-exporter image: nginx/nginx-prometheus-exporter resources: requests: cpu: 30m ports: - name: nginx-exporter containerPort: 9113 args: - -nginx.scrape-uri - http://127.0.0.1:80/nginx-status
那些。 我們看到應用程式 pod 最初在兩個實例中啟動,每個實例包含兩個 nginx 和 nginx-exporter 容器,每個容器都有一個指定的 請求 對於CPU。
-
HPA 宣言
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: app-test-hpa spec: maxReplicas: 10 minReplicas: 2 scaleTargetRef: apiVersion: extensions/v1beta1 kind: Deployment name: app-test metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 30
那些。 我們建立了一個 hpa,它將監視 Deployment app-test 並根據 cpu 指標調整應用程式的 pod 數量(我們預計 pod 應消耗其請求的 30% 的 CPU),其中副本數量位於範圍為2-10。
現在,讓我們看看當我們對其中一個爐床施加負載時 hpa 運行的機制:
# kubectl top pod NAME CPU(cores) MEMORY(bytes) app-test-78559f8f44-pgs58 101m 243Mi app-test-78559f8f44-cj4jz 4m 240Mi
我們總共有以下內容:
- 期望值(desiredMetricValue)-根據hpa設置,我們有30%
- 目前值(currentMetricValue) - 用於計算,controller-manager計算資源消耗的平均值,以%為單位,即有條件地執行以下操作:
- 從metric server接收pod指標的絕對值,即101m 和 4m
- 計算平均絕對值,即(101m + 4m) / 2 = 53m
- 取得所需資源消耗的絕對值(為此,將所有容器的請求相加) 60m + 30m = 90m
- 計算相對於請求 pod 的 CPU 消耗的平均百分比,即53m / 90m * 100% = 59%
現在我們已經擁有了確定是否需要更改副本數量所需的一切;為此,我們計算係數:
ratio = 59% / 30% = 1.96
那些。 副本數量應增加約 2 倍,達到 [2 * 1.96] = 4。
結論: 正如您所看到的,為了使該機制發揮作用,必要條件是觀察到的 pod 中存在對所有容器的請求。
節點水平自動縮放機制(Cluster Autoscaler)
為了消除負載激增期間對系統的負面影響,僅配置 hpa 是不夠的。 例如,根據hpa控制器管理器中的設置,它決定需要將副本數量增加2倍,但節點沒有空閒資源來運行如此數量的pod(即節點無法提供向requests pod 請求資源),並且這些pod 切換到Pending 狀態。
在這種情況下,如果提供者有相應的IaaS/PaaS(例如GKE/GCE、AKS、EKS等),則可以使用類似 節點自動縮放器。 它允許您設定叢集中的最大和最小節點數,並在叢集和 Pod 資源不足時自動調整目前節點數(透過呼叫雲端提供者 API 來訂購/刪除節點)無法排程(處於 Pending 狀態)。
結論: 為了能夠自動縮放節點,需要在 pod 容器中設定請求,以便 k8s 能夠正確評估節點上的負載,並相應地報告叢集中沒有資源來啟動下一個 pod。
結論
需要注意的是,設定容器資源限制並不是應用程式成功運行的必要條件,但最好還是這樣做,原因如下:
- 為了讓調度器在k8s節點之間的負載平衡方面更精準的運行
- 減少發生「pod 驅逐」事件的可能性
- 用於應用程式 Pod (HPA) 的水平自動縮放工作
- 用於雲端提供者的節點水平自動縮放(叢集自動縮放)
也請閱讀我們部落格上的其他文章:
Tekton Pipeline - Kubernetes 原生管道 為 Nginx 建立動態模組 從無授權的ClickHouse遷移到授權的ClickHouse導致了什麼? 理解Golang中的Context包 縮小 Docker 映像的三個簡單技巧 大量異質Web項目的備份
來源: www.habr.com