Kubernetes:為什麼設定係統資源管理如此重要?

通常,總是需要為應用程式提供專用的資源池,以使其正確、穩定地運作。 但是,如果多個應用程式在同一電源上運行怎麼辦? 如何為他們每個人提供最少的必要資源? 如何限制資源消耗? 如何在節點之間正確分配負載? 當應用負載增加時,如何確保水平擴展機制發揮作用?

Kubernetes:為什麼設定係統資源管理如此重要?

您需要從系統中存在哪些主要資源類型開始 - 這當然是處理器時間和 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在節點之間的分發,例如 調度,它根據特定的演算法工作。 該演算法在選擇要啟動的最佳節點時經歷兩個階段:

  1. 過濾
  2. 測距

那些。 根據所描述的策略,最初選擇可以基於一組啟動 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 類別之一:

  1. 有保證的 — 當為 pod 中的每個容器指定記憶體和 cpu 的請求和限制時分配,並且這些值必須匹配
  2. 可爆裂的 — Pod 中至少有一個容器有請求和限制,且請求 < 限制
  3. 盡力而為 — 當 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 自動縮放器)。 其演算法如下:

  1. 確定觀察到的資源的當前讀數(currentMetricValue)
  2. 確定資源的期望值(desiredMetricValue),系統資源的期望值使用 request 設定
  3. 確定目前副本數量(currentReplicas)
  4. 以下公式計算所需的副本數量(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計算資源消耗的平均值,以%為單位,即有條件地執行以下操作:
    1. 從metric server接收pod指標的絕對值,即101m 和 4m
    2. 計算平均絕對值,即(101m + 4m) / 2 = 53m
    3. 取得所需資源消耗的絕對值(為此,將所有容器的請求相加) 60m + 30m = 90m
    4. 計算相對於請求 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。

結論

需要注意的是,設定容器資源限制並不是應用程式成功運行的必要條件,但最好還是這樣做,原因如下:

  1. 為了讓調度器在k8s節點之間的負載平衡方面更精準的運行
  2. 減少發生「pod 驅逐」事件的可能性
  3. 用於應用程式 Pod (HPA) 的水平自動縮放工作
  4. 用於雲端提供者的節點水平自動縮放(叢集自動縮放)

也請閱讀我們部落格上的其他文章:

來源: www.habr.com

添加評論