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

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

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) у облачных провайдеров

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

Источник: habr.com

Добавить комментарий