company_banner

Kubernetes: почему так важно настроить управление ресурсами системы?

    Как правило, всегда возникает необходимость обеспечить выделенный пул ресурсов какому-либо приложению для его корректной и стабильной работы. Но что, если на одних и тех же мощностях работают сразу несколько приложений? Как обеспечить минимально необходимыми ресурсами каждое из них? Каким образом можно ограничить потребление ресурсов? Как грамотно распределить нагрузку между нодами? Как обеспечить работу механизма горизонтального масштабирования в случае роста нагрузки на приложения?



    Начать нужно с того, какие основные типы ресурсов существуют в системе — это, конечно же, процессорное время и оперативная память. В манифестах k8s эти типы ресурсов измеряются в следующих единицах:


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

    Причем для каждого ресурса есть возможность задавать два типа требований — requests и limits. 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, как scheduler, который работает по определенному алгоритму. Этот алгоритм в ходе выбора оптимального узла для запуска проходит две стадии:


    1. Фильтрация
    2. Ранжирование

    Т.е. согласно описанной политике изначально выбираются ноды, на которых возможен запуск пода на основе набора predicates (в том числе проверяется достаточно ли у ноды ресурсов для запуска пода — PodFitsResources), а затем для каждой из этих нод, согласно priorities начисляются очки (в том числе, чем больше свободных ресурсов у ноды — тем больше очков ей присваивается — 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. best effort — когда ни один контейнер в поде не ограничен по ресурсам

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


    • Guaranteed: -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, для каждого из которых задан requests для 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. Получает абсолютное значение для желаемого потребления ресурса (для этого суммируются request всех контейнеров) 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) у облачных провайдеров

    Также читайте другие статьи в нашем блоге:


    Nixys
    Эксперты в DevOps и Kubernetes

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое