Kubernetes: por que é tão importante configurar a gestão de recursos do sistema?

Geralmente, há sempre a necessidade de fornecer um pool dedicado de recursos a algum aplicativo para seu funcionamento correto e estável. Mas e se vários aplicativos estiverem rodando nas mesmas capacidades? Como garantir o mínimo de recursos necessários para cada um deles? De que forma é possível limitar o consumo de recursos? Como distribuir a carga entre os nós de maneira eficiente? Como garantir o funcionamento do mecanismo de escalabilidade horizontal em caso de aumento da carga sobre os aplicativos?

Kubernetes: por que é tão importante configurar a gestão de recursos do sistema?

Devemos começar pelo entendimento dos principais tipos de recursos que existem no sistema — que são, claro, o tempo de CPU e a memória RAM. Nos manifestos do k8s, esses tipos de recursos são medidos nas seguintes unidades:

  • CPU — em núcleos
  • RAM — em bytes

Além disso, para cada recurso, há a possibilidade de definir dois tipos de requisitos — requests e limits. Requests — descreve os requisitos mínimos em relação aos recursos livres do nó para executar o contêiner (e o pod como um todo), enquanto limits estabelece uma restrição rígida aos recursos disponíveis para o contêiner.

É importante entender que no manifesto não é necessário definir explicitamente ambos os tipos, e o comportamento será o seguinte:

  • Se apenas limits do recurso for definido explicitamente, então requests para esse recurso automaticamente assume o valor igual a limits (o que pode ser verificado ao invocar describe da entidade). Ou seja, na prática, o funcionamento do contêiner será limitado à mesma quantidade de recursos que ele requer para sua execução.
  • Se para o recurso apenas requests for definido explicitamente, então não há limitações superiores para esse recurso — ou seja, o contêiner é limitado apenas pelos recursos do próprio nó.

Também existe a possibilidade de configurar a gestão de recursos não apenas no nível de um contêiner específico, mas também no nível de namespace por meio das seguintes entidades:

  • LimitRange — descreve a política de limitação ao nível do contêiner/pod no namespace e é necessária para descrever as limitações padrão para contêiner/pod, bem como para prevenir a criação de contêineres/pods excessivamente grandes (ou vice-versa), limitar sua quantidade e determinar a possível diferença entre os valores de limits e requests.
  • ResourceQuotas — descrevem a política de cota em geral para todos os contêineres no ns e é utilizada, geralmente, para delimitar recursos entre os ambientes (útil quando os ambientes não são rigidamente separados ao nível dos nós)

Abaixo estão exemplos de manifests onde são estabelecidas limitações de recursos:

  • Ao nível de um contêiner específico:

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

    Ou seja, neste caso, para iniciar um contêiner com nginx, será necessário pelo menos 1G de RAM livre e 0.2 CPU no nó, enquanto o máximo que o contêiner pode consumir é 0.2 CPU e toda a RAM disponível no nó.

  • Ao nível de todo o ns:

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

    Ou seja, a soma de todos os requests dos contêineres no ns padrão não pode ultrapassar 300m para CPU e 1G para RAM, enquanto a soma de todos os limits é de 700m para CPU e 2G para RAM.

  • Limitações padrão para contêineres no 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

    Ou seja, no namespace padrão, todos os contêineres terão, por padrão, um request de 100m para CPU e 1G para RAM, e um limit de 1 CPU e 2G. Além disso, também foi estabelecido um limite nos possíveis valores para request/limit de CPU (50m < x < 2) e RAM (500M < x < 4G).

  • Limitações ao nível dos pods no ns:

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

    Ou seja, para cada pod no ns padrão, haverá uma limitação de 4 vCPU e 1G.

Agora, gostaria de falar sobre quais são as vantagens que a imposição dessas limitações pode nos trazer.

Mecanismo de balanceamento de carga entre os nós

Como é sabido, a distribuição de pods entre nós é feita por um componente do k8s chamado scheduler, que opera segundo um determinado algoritmo. Este algoritmo, ao escolher o nó ideal para executar, passa por duas etapas:

  1. Filtragem
  2. Ranqueamento

Ou seja, de acordo com a política descrita, inicialmente são selecionados os nós onde é possível executar o pod com base em um conjunto de predicates (incluindo a verificação se há recursos suficientes no nó para executar o pod — PodFitsResources), e em seguida, para cada um desses nós, de acordo com priorities são atribuídos pontos (isto é, quanto mais recursos livres houver no nó, mais pontos ele recebe — LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) e é iniciado em um nó com o maior número de pontos (se vários nós satisfizerem essa condição, um deles é escolhido aleatoriamente).

É importante entender que o scheduler, ao avaliar os recursos disponíveis do nó, se orienta pelos dados armazenados no etcd — ou seja, pela soma dos recursos solicitados/limite de cada pod em execução nesse nó, mas não pelo consumo real de recursos. Essa informação pode ser obtida a partir da saída do comando kubectl describe node $NODE, por exemplo:

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

Aqui vemos todos os pods em execução em um nó específico, bem como os recursos solicitados por cada um deles. E aqui está como os logs do scheduler aparecem ao iniciar o pod cronjob-cron-events-1573793820-xt6q9 (essa informação nos logs do scheduler aparecerá ao configurar o nível 10 de log nos argumentos do comando de início —v=10):

log

I1115 07:57:21.637791       1 scheduling_queue.go:908] Prestes a tentar agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                           
I1115 07:57:21.637804       1 scheduler.go:453] Tentando agendar o pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                                    
I1115 07:57:21.638285       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s5 é permitido, o nó está executando apenas 16 de 110 pods.                                                                               
I1115 07:57:21.638300       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s6 é permitido, o nó está executando apenas 20 de 110 pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s3 é permitido, o nó está executando apenas 20 de 110 pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s4 é permitido, o nó está executando apenas 17 de 110 pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s10 é permitido, o nó está executando apenas 16 de 110 pods.                                                                              
I1115 07:57:21.638365       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s12 é permitido, o nó está executando apenas 9 de 110 pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s11 é permitido, o nó está executando apenas 11 de 110 pods.                                                                              
I1115 07:57:21.638385       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s1 é permitido, o nó está executando apenas 19 de 110 pods.                                                                               
I1115 07:57:21.638402       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s2 é permitido, o nó está executando apenas 21 de 110 pods.                                                                               
I1115 07:57:21.638383       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s9 é permitido, o nó está executando apenas 16 de 110 pods.                                                                               
I1115 07:57:21.638335       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s8 é permitido, o nó está executando apenas 18 de 110 pods.                                                                               
I1115 07:57:21.638408       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s13 é permitido, o nó está executando apenas 8 de 110 pods.                                                                               
I1115 07:57:21.638478       1 predicates.go:1369] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s10 é permitido, termos de anti-afinação de pods existentes satisfatórios.                                                                         
I1115 07:57:21.638505       1 predicates.go:1369] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s8 é permitido, termos de anti-afinação de pods existentes satisfatórios.                                                                          
I1115 07:57:21.638577       1 predicates.go:1369] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s9 é permitido, termos de anti-afinação de pods existentes satisfatórios.                                                                          
I1115 07:57:21.638583       1 predicates.go:829] Agendar o pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 no nó nxs-k8s-s7 é permitido, o nó está executando apenas 25 de 110 pods.                                                                               
I1115 07:57:21.638932       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacidade 39900 milicore 66620178432 bytes de memória, total de solicitação 2343 milicore 9640186880 bytes de memória, pontuação 9        
I1115 07:57:21.638946       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacidade 39900 milicore 66620178432 bytes de memória, total de solicitação 2343 milicore 9640186880 bytes de memória, pontuação 8           
I1115 07:57:21.638961       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacidade 39900 milicore 66620170240 bytes de memória, total de solicitação 4107 milicore 11307422720 bytes de memória, pontuação 9        
I1115 07:57:21.638971       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacidade 39900 milicore 66620178432 bytes de memória, total de solicitação 5847 milicore 24333637120 bytes de memória, pontuação 7        
I1115 07:57:21.638975       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacidade 39900 milicore 66620170240 bytes de memória, total de solicitação 4107 milicore 11307422720 bytes de memória, pontuação 8           
I1115 07:57:21.638990       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacidade 39900 milicore 66620178432 bytes de memória, total de solicitação 5847 milicore 24333637120 bytes de memória, pontuação 7           
I1115 07:57:21.639022       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Pontuação: (10)                                                                                                        
I1115 07:57:21.639030       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Pontuação: (10)                                                                                                         
I1115 07:57:21.639034       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Pontuação: (10)                                                                                                         
I1115 07:57:21.639041       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Pontuação: (0)                                                                                                            
I1115 07:57:21.639053       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Pontuação: (0)                                                                                                             
I1115 07:57:21.639059       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Pontuação: (0)                                                                                                             
I1115 07:57:21.639061       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Pontuação: (0)                                                                                                                   
I1115 07:57:21.639063       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Pontuação: (10)                                                                                                                   
I1115 07:57:21.639073       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Pontuação: (0)                                                                                                                    
I1115 07:57:21.639077       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Pontuação: (10)                                                                                                                    
I1115 07:57:21.639085       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Pontuação: (0)                                                                                                                    
I1115 07:57:21.639088       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Pontuação: (10)                                                                                                                    
I1115 07:57:21.639103       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Pontuação: (10)                                                                                                         
I1115 07:57:21.639109       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Pontuação: (10)                                                                                                          
I1115 07:57:21.639114       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Pontuação: (10)                                                                                                          
I1115 07:57:21.639127       1 generic_scheduler.go:781] Host nxs-k8s-s10 -> Pontuação 100037                                                                                                                                                                            
I1115 07:57:21.639150       1 generic_scheduler.go:781] Host nxs-k8s-s8 -> Pontuação 100034                                                                                                                                                                             
I1115 07:57:21.639154       1 generic_scheduler.go:781] Host nxs-k8s-s9 -> Pontuação 100037                                                                                                                                                                             
I1115 07:57:21.639267       1 scheduler_binder.go:269] AssumePodVolumes para o pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", nó "nxs-k8s-s10"                                                                                                               
I1115 07:57:21.639286       1 scheduler_binder.go:279] AssumePodVolumes para o pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", nó "nxs-k8s-s10": todos os PVCs vinculados e nada a fazer                                                                             
I1115 07:57:21.639333       1 factory.go:733] Tentando vincular cronjob-cron-events-1573793820-xt6q9 ao nxs-k8s-s10

Aqui vemos que, inicialmente, o scheduler filtra e forma uma lista com 3 nós onde a execução é possível (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Em seguida, calcula a pontuação com base em vários parâmetros (incluindo BalancedResourceAllocation e LeastResourceAllocation) para cada um desses nós, com o objetivo de determinar o nó mais adequado. No final, o plano é executado no nó com a maior quantidade de pontos (neste caso, dois nós possuem o mesmo número de pontos 100037, portanto, uma escolha aleatória é feita entre eles — nxs-k8s-s10).

Saída: se em um nó estiverem funcionando pods para os quais não foram definidos limites, para o k8s (em termos de consumo de recursos) isso será o mesmo que se esses pods não estivessem nesse nó. Portanto, se você tiver, hipoteticamente, um pod com um processo voraz (por exemplo, wowza) e não foram definidos limites para ele, pode ocorrer uma situação em que esse pod consome todos os recursos do nó, mas, para o k8s, esse nó é considerado como não sobrecarregado e receberá a mesma quantidade de pontos na classificação (exatamente nos pontos de avaliação de recursos disponíveis) que o nó onde não há pods em funcionamento, o que pode levar a uma distribuição desigual de carga entre os nós.

Esvaziamento de pod

Como é sabido, cada pod recebe uma das 3 classes QoS:

  1. garantido — é atribuído quando para cada contêiner no pod são definidos request e limit para memória e CPU, e esses valores devem coincidir
  2. burstable — pelo menos um contêiner no pod tem request e limit, sendo que request < limit
  3. melhor esforço — quando nenhum contêiner no pod é restrito em recursos

Neste caso, quando há escassez de recursos (disco, memória) no nó, o kubelet começa a classificar e esvaziar os pods com base em um algoritmo que considera a prioridade do pod e sua classe QoS. Por exemplo, quando se trata de RAM, os pontos são atribuídos com base na classe QoS da seguinte forma:

  • Garantido: -998
  • BestEffort: 1000
  • Burstable: min(max(2, 1000 — (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

Ou seja, com prioridade igual, o kubelet esvaziará primeiro os pods com classe QoS de melhor esforço.

Saída: se você deseja reduzir a probabilidade de um pod necessário ser esvaziado do nó em caso de escassez de recursos nele, deve se preocupar em definir request/limit para ele, além da prioridade.

Mecanismo de dimensionamento automático horizontal de pods da aplicação (HPA)

Quando há a necessidade de aumentar ou diminuir automaticamente a quantidade de pods com base no uso de recursos (sistemáticos — CPU/RAM ou do usuário — rps), uma entidade do k8s pode ajudar na solução: HPA (Horizontal Pod Autoscaler). O algoritmo funciona da seguinte forma:

  1. Os valores atuais do recurso observado (currentMetricValue) são determinados.
  2. Os valores desejados para o recurso (desiredMetricValue) são definidos, os quais para recursos sistêmicos são estabelecidos através de requests.
  3. A quantidade atual de réplicas (currentReplicas) é determinada.
  4. A quantidade desejada de réplicas (desiredReplicas) é calculada pela seguinte fórmula:
    desiredReplicas = [ currentReplicas * ( currentMetricValue / desiredMetricValue )]

Neste caso, não ocorrerá escalonamento quando o fator (currentMetricValue / desiredMetricValue) estiver próximo de 1 (a tolerância que podemos definir, por padrão, é 0,1).

Vamos considerar o funcionamento do HPA com o exemplo da aplicação app-test (descrito como Deployment), onde é necessário alterar a quantidade de réplicas com base no consumo de CPU:

  • Manifesto da aplicação

    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

    Ou seja, vemos que o pod da aplicação é inicialmente iniciado em duas instâncias, cada uma contendo dois contêineres nginx e nginx-exporter, para os quais é definido requests para CPU.

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

    Ou seja, criamos um HPA que irá monitorar o Deployment app-test e regular a quantidade de pods da aplicação com base no indicador de CPU (esperamos que o pod consuma 30% do CPU solicitado), onde o número de réplicas está entre 2-10.

    Agora, vamos considerar o mecanismo de funcionamento do HPA quando uma carga é aplicada a um dos pods:

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

Em resumo, temos o seguinte:

  • O valor desejado (desiredMetricValue) — de acordo com as configurações do hpa, é igual a 30%
  • O valor atual (currentMetricValue) — para o cálculo, o controller-manager calcula a média do consumo de recursos em %, ou seja, realiza o seguinte:
    1. Obtém os valores absolutos das métricas dos pods do metric-server, ou seja, 101m e 4m
    2. Calcula a média absoluta, ou seja, (101m + 4m) / 2 = 53m
    3. Obtém o valor absoluto para o consumo de recursos desejado (para isso, soma-se a request de todos os contêineres) 60m + 30m = 90m
    4. Calcula a porcentagem média do consumo de CPU em relação à request do pod, ou seja, 53m / 90m * 100% = 59%

Agora temos tudo o que precisamos para determinar se devemos alterar o número de réplicas, para isso, calculamos o coeficiente:

ratio = 59% / 30% = 1.96

Ou seja, o número de réplicas deve ser aumentado em ~2 vezes e ser igual a [2 * 1.96] = 4.

Conclusão: Como podemos notar, para que esse mecanismo funcione, uma condição necessária é que haja requests para todos os contêineres no pod observado.

O mecanismo de autoscaling horizontal de nós (Cluster Autoscaler)

Para mitigar o impacto negativo no sistema durante picos de carga, ter um hpa configurado pode ser insuficiente. Por exemplo, de acordo com as configurações, o controller manager do hpa decide que o número de réplicas deve ser dobrado, no entanto, não há recursos disponíveis nos nós para executar essa quantidade de pods (ou seja, o nó não consegue fornecer os recursos solicitados pelo pod). Esses pods entram em estado Pending.

Nesse caso, se o provedor tiver o correspondente IaaS/PaaS (por exemplo, GKE/GCE, AKS, EKS etc.), podemos contar com uma ferramenta, como Node Autoscaler. Ela permite definir o número máximo e mínimo de nós no cluster e regular automaticamente o número atual de nós (através de chamadas à API do provedor de nuvem para solicitar/remover um nó), quando há falta de recursos no cluster e os pods não podem ser agendados (estão em estado Pending).

Conclusão: Para possibilitar o autoscaling dos nós, é necessário definir requests nos contêineres dos pods, para que o k8s possa avaliar corretamente a carga dos nós e, consequentemente, informar que não há recursos suficientes no cluster para iniciar um novo pod.

Conclusão

É importante mencionar que a configuração de limitações de recursos para o contêiner não é uma condição obrigatória para o lançamento bem-sucedido do aplicativo, mas ainda assim é recomendável fazê-lo pelos seguintes motivos:

  1. Para um funcionamento mais preciso do scheduler em relação ao balanceamento de carga entre os nós do k8s
  2. Para reduzir a probabilidade de um evento de “despejo do pod”
  3. Para que o escalonamento automático horizontal dos pods do aplicativo (HPA) funcione
  4. Para que o escalonamento automático horizontal dos nós (Cluster Autoscaling) funcione em provedores de nuvem

Leia também outros artigos em nosso blog:

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster