Kubernetes: чому так важливо настроїти управління ресурсами системи?

Як правило, завжди виникає необхідність забезпечити виділений пул ресурсів будь-якому додатку для його коректної та стабільної роботи. Але що, якщо на тих самих потужностях працюють відразу кілька додатків? Як забезпечити мінімально необхідними ресурсами кожен із них? Як можна обмежити споживання ресурсів? Як грамотно розподілити навантаження між нодами? Як забезпечити роботу механізму горизонтального масштабування у разі зростання навантаження на програми?

Kubernetes: чому так важливо настроїти управління ресурсами системи?

Почати потрібно з того, які основні типи ресурсів існують у системі — це, звичайно, процесорний час і оперативна пам'ять. У маніфестах k8s ці типи ресурсів вимірюються у наступних одиницях:

  • CPU - в ядрах
  • RAM - у байтах

Причому для кожного ресурсу є можливість задавати два типи вимог. запитів и рамки. Requests - визначає мінімальні вимоги до вільних ресурсів ноди для запуску контейнера (і пода в цілому), в той час як limits встановлює жорстке обмеження ресурсів, доступних контейнеру.

Важливо розуміти, що у маніфесті не обов'язково явно визначати обидва типи, при цьому поведінка буде такою:

  • Якщо явно заданий тільки limits ресурсу, то requests для цього ресурсу автоматично приймає значення, що дорівнює limits (у цьому можна переконатися, викликавши describe сутності). Тобто. фактично робота контейнера буде обмежена такою ж кількістю ресурсів, що він вимагає для свого запуску.
  • Якщо ресурсу явно заданий лише requests, то жодних обмежень зверху цей ресурс не задається — тобто. контейнер обмежений лише ресурсами самої ноди.

Також існує можливість настроїти керування ресурсами не тільки на рівні конкретного контейнера, але і на рівні namespace за допомогою наступних сутностей:

  • LimitRange — описує політику обмеження на рівні контейнера/пода в ns і потрібна для того, щоб описати дефолтні обмеження на контейнер/під, а також запобігати створенню свідомо жирних контейнерів/подів (або навпаки), обмежувати їх кількість та визначати можливу різницю значень у limits та requests
  • ResourceQuotas - описують політику обмеження в цілому по всіх контейнерах в ns і використовується, як правило, для розмежування ресурсів по оточенням (корисно, коли середовища жорстко не розмежовані на рівні нод)

Нижче наведено приклади маніфестів, де встановлюються обмеження на ресурси:

  • На рівні конкретного контейнера:

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

    Тобто. в даному випадку для запуску контейнера з nginx знадобиться як мінімум наявність вільних 1G ОП і 0.2 CPU на ноді, при цьому максимум контейнер може з'їсти 0.2 CPU і всю доступну ОП на ноді.

  • На рівні цілого ns:

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

    Тобто. сума всіх request контейнерів у дефолтному ns не може перевищувати 300m для CPU та 1G для ОП, а сума всіх limit — 700m для CPU та 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

    Тобто. у дефолтному namespace для всіх контейнерів за замовчуванням буде встановлений request в 100m для CPU та 1G для ОП, limit - 1 CPU та 2G. При цьому встановлено обмеження на можливі значення в request/limit для CPU (50m < x < 2) і RAM (500M < x < 4G).

  • Обмеження на рівні подів ns:

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

    Тобто. для кожного пода в дефолтному ns буде встановлено обмеження в 4 vCPU та 1G.

Тепер хотілося б розповісти, які переваги може нам дати встановлення даних обмежень.

Механізм балансування навантаження між нодами

Як відомо, за розподіл подів по нодах відповідає такий компонент k8s, як планувальникякий працює за певним алгоритмом. Цей алгоритм під час вибору оптимального вузла для запуску проходить дві стадії:

  1. фільтрація
  2. ранжування

Тобто. згідно з описаною політикою спочатку обираються ноди, на яких можливий запуск пода на основі набору предикати (у тому числі перевіряється чи достатньо у ноди ресурсів для запуску пода — PodFitsResources), а потім для кожної з цих нод, згідно пріоритети нараховуються окуляри (у тому числі, чим більше вільних ресурсів у ноди - тим більше очок їй присвоюється - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) і під запускається на ноді з найбільшою кількістю очок (якщо цій умові задовольняють відразу кілька нід, то вибирається .

У цьому слід розуміти, що scheduler в оцінці доступних ресурсів ноди орієнтується дані, які зберігаються в etcd — тобто. у сумі requested/limit ресурсу кожного пода, запущеного цієї ноді, але з фактичне споживання ресурсів. Цю інформацію можна отримати у висновку команди 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%)

Тут ми бачимо всі поди, запущені на конкретній ноді, а також ресурси, які запитує кожен із подів. А ось як виглядають логи scheduler при запуску пода cronjob-cron-events-1573793820-xt6q9 (дана інформація в лозі scheduler з'явиться при установці 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

Тут ми бачимо, що спочатку scheduler здійснює фільтрацію та формує список з 3-х нід, на яких можливий запуск (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Потім здійснює підрахунок очок за декількома параметрами (у тому числі BalancedResourceAllocation, LeastResourceAllocation) для кожної з цих нід з метою визначити найбільш вдалий вузол. Зрештою під планується на ноді з найбільшою кількістю очок (тут відразу дві ноди мають однакову кількість очок 100037, тому вибирається випадкова з них – nxs-k8s-s10).

Висновок: якщо на ноді працюють поди, для яких не задані обмеження, то для k8s (з точки зору споживання ресурсів) це буде рівносильно тому, якби на цій ноді таких подів взагалі не було. Тому, якщо у вас, умовно, є pod з ненажерливим процесом (наприклад, wowza) і для нього не задані обмеження, то може виникнути така ситуація, коли фактично даний під з'їв усі ресурси ноди, але при цьому для k8s це нода вважається ненавантаженою і їй будуть нараховуватися така ж кількість очок при ранжируванні (саме в пунктах з оцінкою доступних ресурсів), як і ноді, на якій немає робочих подів, що в кінцевому підсумку може призвести до нерівномірного розподілу навантаження між нодами.

Виселення пода

Як відомо - кожному поду присвоюється один із 3 QoS-класів:

  1. guaranuted - призначається тоді, як для кожного контейнера в поді для memory і cpu заданий request і limit, причому ці значення повинні збігатися
  2. burstable - хоча б один контейнер у поді має request і limit, при цьому request < limit
  3. найкращих зусиль — коли жоден контейнер у поді не обмежений за ресурсами

При цьому, коли на ноді спостерігається нестача ресурсів (диску, пам'яті), kubelet починає ранжувати та виселяти під'ї за певним алгоритмом, який враховує пріоритет пода та його QoS-клас. Наприклад, якщо йдеться про RAM, то на основі QoS класу нараховуються окуляри за таким принципом:

  • Гарантований: -998
  • BestEffort: 1000
  • Burstable: min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

Тобто. при однаковому пріоритеті, kubelet в першу чергу виселятиме з ноди поди з QoS-класом best effort.

Висновок: якщо ви хочете зменшити ймовірність виселення потрібного пода ноди у разі браку ресурсів на ній, то нарівні з пріоритетом необхідно також подбати і про завдання request/limit для нього.

Механізм горизонтального автомасштабування подів програми (HPA)

Коли стоїть завдання автоматично збільшувати та зменшувати кількість pod залежно від використання ресурсів (системного – CPU/RAM або користувальницького – rps) у її вирішенні може допомогти така сутність k8s як HPA (Horizontal Pod Autoscaler). Алгоритм якого полягає в наступному:

  1. Визначаються поточні показання ресурсу, що спостерігається (currentMetricValue)
  2. Визначаються бажані значення для ресурсу (desiredMetricValue), які задаються для системних ресурсів за допомогою request
  3. Визначається поточна кількість реплік (currentReplicas)
  4. За наступною формулою розраховується бажана кількість реплік (desiredReplicas)
    desiredReplicas = [ currentReplicas * ( currentMetricValue / desiredMetricValue )]

При цьому масштабування не відбудеться, коли коефіцієнт (currentMetricValue/desiredMetricValue) близький до 1 (при цьому допустиму похибку ми можемо ставити самі, за умовчанням вона дорівнює 0.1).

Розглянемо роботу hpa на прикладі додатка app-test (описане як Deployment), де необхідно змінювати кількість реплік, залежно від споживання 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

    Тобто. ми бачимо, що під додатком спочатку запускається у двох примірниках, кожен з яких містить два контейнери 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 (ми очікуємо, що під повинен споживати 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-сервера, тобто. 101m та 4m
    2. Вираховує середнє абсолютне значення, тобто. (101m + 4m) / 2 = 53m
    3. Отримує абсолютне значення для бажаного споживання ресурсу (для цього сумуються потреби всіх контейнерів) 60m + 30m = 90m
    4. Розраховує середній відсоток споживання CPU щодо request пода, тобто. 53m / 90m * 100% = 59%

Тепер у нас є все необхідне для того, щоб визначити, чи потрібно змінювати кількість реплік, для цього розраховуємо коефіцієнт:

ratio = 59% / 30% = 1.96

Тобто. кількість реплік має бути збільшена в ~2 рази та становити [2 * 1.96] = 4.

Висновок: Як можна помітити, для того, щоб цей механізм працював необхідною умовою, є наявність requests для всіх контейнерів у спостережуваному поді.

Механізм горизонтального автомасштабування нод (Cluster Autoscaler)

Для того, щоб нівелювати негативний вплив на систему під час сплесків навантаження, наявність налаштованого hpa буває недостатньо. Наприклад, згідно з налаштуваннями в hpa controller manager приймає рішення про те, що кількість реплік необхідно збільшити в 2 рази, проте на нодах немає вільних ресурсів для запуску такої кількості подів (тобто нода не може надати запитувані ресурси пода requests) і ці поди переходять у стан Pending.

У цьому випадку, якщо провайдер має відповідний IaaS/PaaS (наприклад, GKE/GCE, AKS, EKS і т.д.), нам може допомогти такий інструмент, як Node Autoscaler. Він дозволяє задати максимальну та мінімальну кількість нод у кластері та автоматично регулювати поточну кількість нод (шляхом звернення до API хмарного провайдера для замовлення/видалення ноди), коли спостерігається нестача ресурсів у кластері та піди не можуть бути заплановані (перебувають у стані Pending).

Висновок: Для можливості автомасштабування нід необхідно задавати requests в контейнерах подів, щоб k8s міг правильно оцінити навантаженість нід і відповідно повідомляти, що для запуску чергового подавання ресурсів у кластері немає.

Висновок

Слід зазначити, що встановлення обмежень ресурсів контейнера не є обов'язковою умовою для успішного запуску програми, проте це все ж таки краще зробити з наступних причин:

  1. Для більш точної роботи scheduler у частині балансування навантаження між нодами k8s
  2. Для зменшення ймовірності виникнення події “виселення пода”
  3. Для роботи горизонтального автомасштабування подів програми (HPA)
  4. Для роботи горизонтального автомасштабування нід (Cluster Autoscaling) у хмарних провайдерів

Також читайте інші статті у нашому блозі:

Джерело: habr.com

Додати коментар або відгук