Kubernetes: dlaczego tak ważna jest konfiguracja zarządzania zasobami systemowymi?

Z reguły zawsze istnieje potrzeba zapewnienia aplikacji dedykowanej puli zasobów, aby mogła ona poprawnie i stabilnie działać. Ale co, jeśli kilka aplikacji działa na tym samym zasilaniu? Jak zapewnić każdemu z nich minimum niezbędnych zasobów? Jak ograniczyć zużycie zasobów? Jak poprawnie rozłożyć obciążenie pomiędzy węzłami? Jak zapewnić działanie mechanizmu skalowania poziomego w przypadku wzrostu obciążenia aplikacji?

Kubernetes: dlaczego tak ważna jest konfiguracja zarządzania zasobami systemowymi?

Musisz zacząć od tego, jakie główne rodzaje zasobów istnieją w systemie - jest to oczywiście czas procesora i pamięć RAM. W manifestach k8 te typy zasobów są mierzone w następujących jednostkach:

  • Procesor - w rdzeniach
  • RAM - w bajtach

Ponadto dla każdego zasobu można ustawić dwa rodzaje wymagań - wywołań и Limity. Żądania - opisuje minimalne wymagania dotyczące wolnych zasobów węzła do uruchomienia kontenera (i podu jako całości), natomiast limity wyznaczają twardy limit zasobów dostępnych dla kontenera.

Ważne jest, aby zrozumieć, że manifest nie musi jawnie definiować obu typów, ale zachowanie będzie następujące:

  • Jeśli wyraźnie określone są tylko limity zasobu, to żądania dotyczące tego zasobu automatycznie przyjmują wartość równą limitom (można to sprawdzić wywołując encje opisu). Te. w rzeczywistości kontener będzie ograniczony do tej samej ilości zasobów, których potrzebuje do działania.
  • Jeśli dla zasobu jawnie określono tylko żądania, wówczas dla tego zasobu nie są ustawione żadne górne ograniczenia – tj. kontener jest ograniczony jedynie zasobami samego węzła.

Istnieje także możliwość skonfigurowania zarządzania zasobami nie tylko na poziomie konkretnego kontenera, ale także na poziomie przestrzeni nazw, wykorzystując następujące encje:

  • LimitZakresu — opisuje politykę ograniczeń na poziomie kontenera/strąka w ns i jest potrzebna w celu opisania domyślnych limitów na kontenerze/strąku, a także zapobiegania tworzeniu ewidentnie tłustych pojemników/strąków (lub odwrotnie), ograniczenia ich liczby i określ możliwą różnicę wartości w limitach i żądaniach
  • Przydziały zasobów — opisuje ogólnie politykę ograniczeń dla wszystkich kontenerów w ns i jest z reguły używana do rozgraniczenia zasobów pomiędzy środowiskami (przydatne, gdy środowiska nie są ściśle rozgraniczone na poziomie węzła)

Poniżej znajdują się przykłady manifestów ustalających limity zasobów:

  • Na poziomie konkretnego kontenera:

    containers:
    - name: app-nginx
      image: nginx
      resources:
        requests:
          memory: 1Gi
        limits:
          cpu: 200m

    Te. w tym przypadku, aby uruchomić kontener z nginxem, będziesz potrzebować co najmniej 1 GB wolnej pamięci RAM i 0.2 procesora w węźle, podczas gdy maksymalnie kontener może zużyć 0.2 procesora i całą dostępną pamięć RAM w węźle.

  • Na poziomie całkowitym ns:

    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: nxs-test
    spec:
      hard:
        requests.cpu: 300m
        requests.memory: 1Gi
        limits.cpu: 700m
        limits.memory: 2Gi

    Te. suma wszystkich kontenerów żądań w domyślnym ns nie może przekroczyć 300m dla CPU i 1G dla OP, a suma wszystkich limitów wynosi 700m dla CPU i 2G dla OP.

  • Domyślne limity dla kontenerów w 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

    Te. w domyślnej przestrzeni nazw dla wszystkich kontenerów żądanie zostanie ustawione na 100m dla CPU i 1G dla OP, limit - 1 CPU i 2G. Jednocześnie ustawiony jest również limit możliwych wartości w żądaniu/limitie dla procesora (50m < x < 2) i pamięci RAM (500M < x < 4G).

  • Ograniczenia na poziomie podów:

    apiVersion: v1
    kind: LimitRange
    metadata:
     name: nxs-limit-pod
    spec:
     limits:
     - type: Pod
       max:
         cpu: 4
         memory: 1Gi

    Te. dla każdego poda w domyślnym ns będzie limit 4 vCPU i 1G.

Teraz chciałbym powiedzieć jakie korzyści może nam dać ustawienie tych ograniczeń.

Mechanizm równoważenia obciążenia pomiędzy węzłami

Jak wiadomo komponent k8s odpowiada za dystrybucję podów pomiędzy węzłami, np planista, który działa według określonego algorytmu. Algorytm ten przechodzi przez dwa etapy wyboru optymalnego węzła do uruchomienia:

  1. filtracja
  2. Nośny

Te. zgodnie z opisaną polityką początkowo wybierane są węzły, na których możliwe jest uruchomienie poda w oparciu o zestaw predykaty (w tym sprawdzenie, czy węzeł posiada wystarczające zasoby do uruchomienia poda – PodFitsResources), a następnie dla każdego z tych węzłów wg. Priorytety przyznawane są punkty (m.in. im więcej wolnych zasobów posiada węzeł, tym więcej zostaje mu przypisanych punktów – LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation), a pod jest uruchamiany na węźle z największą liczbą punktów (jeśli kilka węzłów spełnia ten warunek jednocześnie, to wybierany jest losowo).

Jednocześnie musisz zrozumieć, że planista oceniając dostępne zasoby węzła, kieruje się danymi przechowywanymi w etcd - tj. dla ilości żądanych/ograniczonych zasobów każdego poda działającego w tym węźle, ale nie dla rzeczywistego zużycia zasobów. Informacje te można uzyskać z danych wyjściowych polecenia kubectl describe node $NODEna przykład:

# 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%)

Tutaj widzimy wszystkie pody działające w określonym węźle, a także zasoby, których żąda każdy pod. A oto jak wyglądają logi programu planującego po uruchomieniu poda cronjob-cron-events-1573793820-xt6q9 (ta informacja pojawi się w dzienniku programu planującego, gdy w argumentach polecenia uruchamiania zostanie ustawiony 10. poziom rejestrowania -v=10 ):

dziennik

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

Tutaj widzimy, że początkowo harmonogram filtruje i generuje listę 3 węzłów, na których można go uruchomić (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Następnie oblicza wyniki w oparciu o kilka parametrów (m.in. BalancedResourceAllocation, LeastResourceAllocation) dla każdego z tych węzłów, aby określić najbardziej odpowiedni węzeł. Docelowo pod jest planowany na węźle z największą liczbą punktów (tutaj dwa węzły na raz mają tę samą liczbę punktów 100037, więc wybierany jest losowy - nxs-k8s-s10).

Wniosek: jeśli węzeł uruchamia pody, dla których nie ustawiono żadnych ograniczeń, to dla k8s (z punktu widzenia zużycia zasobów) będzie to równoznaczne z tym, jakby w tym węźle w ogóle nie było takich podów. Dlatego jeśli warunkowo masz kapsułę z żarłocznym procesem (na przykład wowza) i nie ustawiono dla niej żadnych ograniczeń, może wystąpić sytuacja, gdy ta kapsuła faktycznie zjadła wszystkie zasoby węzła, ale dla k8 ten węzeł jest uznawany za nieobciążony i otrzyma w rankingu taką samą liczbę punktów (dokładnie w punktach oceniających dostępne zasoby) jak węzeł, który nie posiada działających zasobników, co w ostatecznym rozrachunku może prowadzić do nierównomiernego rozłożenia obciążenia pomiędzy węzłami.

Eksmisja Poda

Jak wiadomo, każdemu podowi przypisana jest jedna z 3 klas QoS:

  1. gwarantowane — jest przypisywany, gdy dla każdego kontenera w podu określone jest żądanie i limit dla pamięci i procesora, a wartości te muszą się zgadzać
  2. wybuchowy — co najmniej jeden kontener w podu ma żądanie i limit, z żądaniem < limit
  3. najlepszy wysiłek — gdy żaden kontener w kapsule nie ma ograniczonych zasobów

Jednocześnie, gdy w węźle zabraknie zasobów (dysku, pamięci), kubelet zaczyna oceniać i eksmitować pody według określonego algorytmu, który uwzględnia priorytet poda i jego klasę QoS. Przykładowo, jeśli mówimy o pamięci RAM, to na podstawie klasy QoS punkty przyznawane są według następującej zasady:

  • Gwarancja:-998
  • Najlepszy wysiłek: 1000
  • Pękający: min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

Te. z tym samym priorytetem kubelet najpierw eksmituje z węzła pody z najlepszą klasą QoS.

Wniosek: jeśli chcesz zmniejszyć prawdopodobieństwo wyrzucenia żądanego poda z węzła w przypadku braku na nim zasobów, to oprócz priorytetu musisz także zadbać o ustawienie dla niego żądania/limitu.

Mechanizm poziomego autoskalowania podów aplikacyjnych (HPA)

Gdy zadaniem jest automatyczne zwiększanie i zmniejszanie liczby podów w zależności od wykorzystania zasobów (system – CPU/RAM lub użytkownik – rps), taka jednostka k8s jak HPA (Autoskaler poziomego kapsuły). Algorytm którego jest następujący:

  1. Określane są aktualne odczyty obserwowanego zasobu (currentMetricValue)
  2. Określane są pożądane wartości dla zasobu (desiredMetricValue), które dla zasobów systemowych są ustawiane za pomocą request
  3. Określana jest bieżąca liczba replik (currentReplicas)
  4. Poniższa formuła oblicza żądaną liczbę replik (desiredReplicas)
    pożądaneRepliki = [bieżąceRepliki * (bieżącaWartośćMetryki / pożądanaWartośćMetryki )]

W tym przypadku skalowanie nie nastąpi, gdy współczynnik (currentMetricValue / pożądanyMetricValue) będzie bliski 1 (w tym przypadku możemy sami ustawić błąd dopuszczalny; domyślnie jest to 0.1).

Przyjrzyjmy się jak działa hpa na przykładzie aplikacji do testowania aplikacji (opisanej jako Deployment), gdzie konieczna jest zmiana liczby replik w zależności od zużycia procesora:

  • Manifest aplikacji

    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

    Te. widzimy, że moduł aplikacji jest początkowo uruchamiany w dwóch instancjach, z których każda zawiera dwa kontenery nginx i nginx-exporter, dla każdego z nich określony wywołań dla procesora.

  • Manifest 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

    Te. Stworzyliśmy hpa, które będzie monitorować test aplikacji wdrożeniowej i dostosowywać liczbę podów z aplikacją w oparciu o wskaźnik procesora (oczekujemy, że pod powinien zużywać 30% żądanego procesora), przy czym liczba replik jest w zakres 2-10.

    Przyjrzyjmy się teraz mechanizmowi działania hpa, jeśli przyłożymy obciążenie do jednego z palenisk:

     # kubectl top pod
    NAME                                                   CPU(cores)   MEMORY(bytes)
    app-test-78559f8f44-pgs58            101m         243Mi
    app-test-78559f8f44-cj4jz            4m           240Mi

W sumie mamy co następuje:

  • Pożądana wartość (desiredMetricValue) - zgodnie z ustawieniami hpa mamy 30%
  • Wartość bieżąca (currentMetricValue) – do obliczeń sterownik-menedżer wylicza średnią wartość zużycia zasobów w %, tj. warunkowo wykonuje następujące czynności:
    1. Otrzymuje wartości bezwzględne metryk poda z serwera metryki, tj. 101m i 4m
    2. Oblicza średnią wartość bezwzględną, tj. (101 m + 4 m) / 2 = 53 m
    3. Pobiera wartość bezwzględną żądanego zużycia zasobów (w tym celu sumowane są żądania wszystkich kontenerów) 60m + 30m = 90m
    4. Oblicza średni procent zużycia procesora w stosunku do zasobnika żądań, tj. 53 m / 90 m * 100% = 59%

Teraz mamy wszystko, czego potrzebujemy, aby określić, czy należy zmienić liczbę replik; w tym celu obliczamy współczynnik:

ratio = 59% / 30% = 1.96

Te. liczbę replik należy zwiększyć ~2 razy i wynieść [2 * 1.96] = 4.

Wnioski: Jak widać, aby ten mechanizm zadziałał warunkiem koniecznym jest obecność żądań dla wszystkich kontenerów w obserwowanym podzespole.

Mechanizm poziomego autoskalowania węzłów (Cluster Autoscaler)

Aby zneutralizować negatywny wpływ na system podczas skoków obciążenia, posiadanie skonfigurowanego hpa nie wystarczy. Przykładowo, zgodnie z ustawieniami w menedżerze kontrolerów hpa, decyduje, że należy zwiększyć liczbę replik 2-krotnie, ale węzły nie mają wolnych zasobów, aby uruchomić taką liczbę podów (tzn. węzeł nie może zapewnić żądane zasoby do zasobnika żądań), a zasobniki te przełączają się do stanu Oczekujące.

W takim przypadku, jeśli dostawca posiada odpowiedni IaaS/PaaS (na przykład GKE/GCE, AKS, EKS itp.), narzędzie takie jak Automatyczne skalowanie węzła. Pozwala ustawić maksymalną i minimalną liczbę węzłów w klastrze oraz automatycznie dostosować aktualną liczbę węzłów (poprzez wywołanie API dostawcy chmury w celu zamówienia/usunięcia węzła) w przypadku braku zasobów w klastrze i podach nie można zaplanować (są w stanie Oczekujące).

Wnioski: Aby móc autoskalować węzły, konieczne jest ustawienie żądań w kontenerach podów, aby k8s mógł poprawnie ocenić obciążenie węzłów i odpowiednio zgłosić, że w klastrze nie ma zasobów do uruchomienia kolejnego poda.

wniosek

Należy zauważyć, że ustawienie limitów zasobów kontenera nie jest warunkiem pomyślnego działania aplikacji, ale mimo wszystko lepiej to zrobić z następujących powodów:

  1. Dla dokładniejszej pracy harmonogramu w zakresie równoważenia obciążenia pomiędzy węzłami k8s
  2. Aby zmniejszyć prawdopodobieństwo wystąpienia zdarzenia „wyrzucenie kapsuły”.
  3. Aby poziome automatyczne skalowanie zestawów aplikacji (HPA) działało
  4. Do poziomego autoskalowania węzłów (Autoskalowanie klastra) dla dostawców usług w chmurze

Przeczytaj także inne artykuły na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz