Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

27 de abril na conferência Greve 2019, como parte da seção “DevOps”, foi entregue o relatório “Autoscaling e gerenciamento de recursos em Kubernetes”. Ele fala sobre como você pode usar K8s para garantir alta disponibilidade de seus aplicativos e desempenho máximo.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Por tradição, temos o prazer de apresentar vídeo da reportagem (44 minutos, muito mais informativo que o artigo) e o resumo principal em forma de texto. Ir!

Vamos analisar o tema do relatório palavra por palavra e começar pelo fim.

Kubernetes

Digamos que temos contêineres Docker em nosso host. Para que? Para garantir repetibilidade e isolamento, o que por sua vez permite uma implantação simples e boa, CI/CD. Temos muitos desses veículos com contêineres.

O que o Kubernetes oferece neste caso?

  1. Paramos de pensar nessas máquinas e começamos a trabalhar com a “nuvem” aglomerado de contêineres ou pods (grupos de contêineres).
  2. Além disso, nem pensamos em grupos individuais, mas gerenciamos maisоgrupos maiores. Tal primitivos de alto nível permita-nos dizer que existe um modelo para executar uma determinada carga de trabalho e aqui está o número necessário de instâncias para executá-la. Se alterarmos posteriormente o modelo, todas as instâncias serão alteradas.
  3. Com API declarativa Em vez de executar uma sequência de comandos específicos, descrevemos a “estrutura do mundo” (em YAML), que é criada pelo Kubernetes. E novamente: quando a descrição muda, sua exibição real também muda.

Gestão de recursos

CPU

Vamos executar nginx, php-fpm e mysql no servidor. Na verdade, esses serviços terão ainda mais processos em execução, cada um dos quais requer recursos computacionais:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)
(os números no slide são “papagaios”, a necessidade abstrata de cada processo em termos de poder computacional)

Para facilitar o trabalho com isso, é lógico combinar processos em grupos (por exemplo, todos os processos nginx em um grupo “nginx”). Uma maneira simples e óbvia de fazer isso é colocar cada grupo em um contêiner:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Para continuar, você precisa lembrar o que é um contêiner (no Linux). Seu surgimento foi possível graças a três recursos principais do kernel, implementados há muito tempo: capacidades, namespaces и grupos. E o desenvolvimento posterior foi facilitado por outras tecnologias (incluindo “shells” convenientes como o Docker):

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

No contexto do relatório, estamos apenas interessados ​​em grupos, porque os grupos de controle fazem parte da funcionalidade dos contêineres (Docker, etc.) que implementam o gerenciamento de recursos. Os processos combinados em grupos, como queríamos, são grupos de controle.

Voltemos aos requisitos de CPU para esses processos e agora para grupos de processos:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)
(Repito que todos os números são uma expressão abstrata da necessidade de recursos)

Ao mesmo tempo, a própria CPU possui um certo recurso finito (no exemplo é 1000), que pode faltar a todos (a soma das necessidades de todos os grupos é 150+850+460=1460). O que acontecerá neste caso?

O kernel começa a distribuir recursos e faz isso de forma “justa”, dando a mesma quantidade de recursos para cada grupo. Mas no primeiro caso, há mais deles do que o necessário (333>150), então o excesso (333-150=183) fica na reserva, que também é distribuída igualmente entre outros dois contêineres:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Como resultado: o primeiro contentor tinha recursos suficientes, o segundo – não tinha recursos suficientes, o terceiro – não tinha recursos suficientes. Este é o resultado de ações agendador "honesto" no Linux - CFS. Sua operação pode ser ajustada usando a atribuição pesos cada um dos recipientes. Por exemplo, assim:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Vejamos o caso de falta de recursos no segundo container (php-fpm). Todos os recursos do contêiner são distribuídos igualmente entre os processos. Como resultado, o processo mestre funciona bem, mas todos os trabalhadores ficam mais lentos, recebendo menos da metade do que precisam:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

É assim que funciona o agendador CFS. Chamaremos ainda os pesos que atribuímos aos contêineres solicitações de. Por que isso acontece - veja mais.

Vejamos toda a situação do outro lado. Como você sabe, todos os caminhos levam a Roma e, no caso de um computador, à CPU. Uma CPU, muitas tarefas – você precisa de um semáforo. A maneira mais simples de gerenciar recursos é o “semáforo”: eles davam a um processo um tempo fixo de acesso à CPU, depois ao próximo, etc.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Esta abordagem é chamada de cotas rígidas (limitação rígida). Vamos lembrar disso simplesmente como limites. Porém, se você distribuir limites para todos os containers, surge um problema: o mysql estava dirigindo pela estrada e em algum momento sua necessidade de CPU acabou, mas todos os outros processos são forçados a esperar até que a CPU parado.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Voltemos ao kernel Linux e sua interação com a CPU - o quadro geral é o seguinte:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

cgroup tem duas configurações - essencialmente, são duas “reviravoltas” simples que permitem determinar:

  1. o peso do contêiner (pedidos) é partes;
  2. a porcentagem do tempo total de CPU para trabalhar em tarefas de contêiner (limites) é quota.

Como medir a CPU?

Existem diferentes maneiras:

  1. o que papagaios, ninguém sabe - você precisa negociar sempre.
  2. Juros mais claro, mas relativo: 50% de um servidor com 4 núcleos e com 20 núcleos são coisas completamente diferentes.
  3. Você pode usar os já mencionados pesos, que o Linux conhece, mas também são relativos.
  4. A opção mais adequada é medir os recursos computacionais em segundos. Aqueles. em segundos de tempo do processador em relação a segundos de tempo real: 1 segundo de tempo do processador foi fornecido por 1 segundo real - este é um núcleo inteiro da CPU.

Para facilitar ainda mais a fala, eles começaram a medir diretamente em grãos, significando por eles o mesmo tempo de CPU em relação ao real. Como o Linux entende pesos, mas não tanto tempo/núcleos de CPU, era necessário um mecanismo para traduzir de um para outro.

Vamos considerar um exemplo simples com um servidor com 3 núcleos de CPU, onde três pods receberão pesos (500, 1000 e 1500) que são facilmente convertidos nas partes correspondentes dos núcleos alocados a eles (0,5, 1 e 1,5).

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Se você pegar um segundo servidor, onde haverá o dobro de núcleos (6), e colocar os mesmos pods lá, a distribuição dos núcleos pode ser facilmente calculada simplesmente multiplicando por 2 (1, 2 e 3, respectivamente). Mas um momento importante ocorre quando um quarto pod aparece neste servidor, cujo peso, por conveniência, será 3000. Ele tira parte dos recursos da CPU (metade dos núcleos), e para os demais pods eles são recalculados (divididos pela metade):

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Kubernetes e recursos de CPU

No Kubernetes, os recursos da CPU geralmente são medidos em miliadrax, ou seja 0,001 núcleos são considerados o peso base. (A mesma coisa na terminologia Linux/cgroups é chamada de compartilhamento de CPU, embora, mais precisamente, 1000 miliccores = 1024 compartilhamentos de CPU.) O K8s garante que não coloque mais pods no servidor do que os recursos da CPU para a soma dos pesos de todos os pods.

Como isso acontece? Quando você adiciona um servidor a um cluster Kubernetes, é relatado quantos núcleos de CPU ele tem disponíveis. E ao criar um novo pod, o agendador do Kubernetes sabe de quantos núcleos esse pod precisará. Assim, o pod será atribuído a um servidor onde haja núcleos suficientes.

O que acontecerá se não a solicitação é especificada (ou seja, o pod não tem um número definido de núcleos necessários)? Vamos descobrir como o Kubernetes geralmente conta os recursos.

Para um pod você pode especificar solicitações (agendador CFS) e limites (lembra do semáforo?):

  • Se eles forem especificados iguais, o pod receberá uma classe de QoS garantido. Este número de núcleos sempre disponíveis é garantido.
  • Se a solicitação for menor que o limite - classe de QoS estourável. Aqueles. Esperamos que um pod, por exemplo, use sempre 1 núcleo, mas este valor não é uma limitação para isso: às vezes pod pode usar mais (quando o servidor tiver recursos livres para isso).
  • Há também uma classe de QoS melhor esforço — inclui os mesmos pods para os quais a solicitação não foi especificada. Os recursos são dados a eles por último.

Память

Com a memória a situação é semelhante, mas um pouco diferente – afinal, a natureza desses recursos é diferente. Em geral, a analogia é a seguinte:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Vamos ver como as solicitações são implementadas na memória. Deixe os pods viverem no servidor, alterando o consumo de memória, até que um deles fique tão grande que fique sem memória. Neste caso, o assassino OOM aparece e mata o processo maior:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Isso nem sempre nos convém, por isso é possível regular quais processos são importantes para nós e não devem ser eliminados. Para fazer isso, use o parâmetro oom_score_adj.

Vamos voltar às classes de QoS da CPU e fazer uma analogia com os valores oom_score_adj que determinam as prioridades de consumo de memória dos pods:

  • O valor oom_score_adj mais baixo para um pod - -998 - significa que tal pod deve ser eliminado por último, este garantido.
  • O mais alto - 1000 - é melhor esforço, esses frutos são eliminados primeiro.
  • Para calcular os valores restantes (estourável) existe uma fórmula cuja essência se resume ao fato de que quanto mais recursos um pod solicitar, menor será a probabilidade de ele ser eliminado.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

A segunda "reviravolta" - limite_em_bytes - para limites. Com ele tudo fica mais simples: simplesmente atribuímos a quantidade máxima de memória emitida, e aqui (ao contrário da CPU) não há dúvida de como medi-la (memória).

No total

Cada pod no Kubernetes é fornecido requests и limits - ambos os parâmetros para CPU e memória:

  1. com base nas solicitações, funciona o agendador Kubernetes, que distribui pods entre servidores;
  2. com base em todos os parâmetros, a classe de QoS do pod é determinada;
  3. Os pesos relativos são calculados com base nas solicitações da CPU;
  4. o agendador CFS é configurado com base nas solicitações da CPU;
  5. O OOM killer é configurado com base em solicitações de memória;
  6. um “semáforo” é configurado com base nos limites da CPU;
  7. Com base nos limites de memória, um limite é configurado para o cgroup.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Em geral, esta imagem responde a todas as perguntas sobre como ocorre a parte principal do gerenciamento de recursos no Kubernetes.

Escalonamento automático

Autoescalador de cluster K8s

Vamos imaginar que todo o cluster já esteja ocupado e um novo pod precise ser criado. Embora o pod não possa aparecer, ele fica suspenso no status Pendente. Para que apareça, podemos conectar um novo servidor ao cluster ou... instalar o cluster-autoscaler, que fará isso por nós: solicitar uma máquina virtual do provedor de nuvem (usando uma solicitação de API) e conectá-la ao cluster , após o qual o pod será adicionado.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Este é o escalonamento automático do cluster Kubernetes, que funciona muito bem (em nossa experiência). No entanto, como em outros lugares, existem algumas nuances aqui...

Contanto que aumentássemos o tamanho do cluster, tudo ficaria bem, mas o que acontece quando o cluster começou a se libertar? O problema é que a migração de pods (para liberar hosts) é muito difícil tecnicamente e cara em termos de recursos. O Kubernetes usa uma abordagem completamente diferente.

Considere um cluster de 3 servidores que possui Deployment. Possui 6 pods: agora são 2 para cada servidor. Por algum motivo, queríamos desligar um dos servidores. Para fazer isso usaremos o comando kubectl drain, qual:

  • proibirá o envio de novos pods para este servidor;
  • excluirá os pods existentes no servidor.

Como o Kubernetes é responsável por manter o número de pods (6), ele simplesmente vai recriar em outros nós, mas não naquele que está desabilitado, pois já está marcado como indisponível para hospedagem de novos pods. Esta é uma mecânica fundamental para Kubernetes.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

No entanto, há uma nuance aqui também. Numa situação semelhante, para StatefulSet (em vez de Deployment), as ações serão diferentes. Agora já temos um aplicativo com estado - por exemplo, três pods com MongoDB, um dos quais apresenta algum tipo de problema (os dados foram corrompidos ou outro erro que impede o início correto do pod). E novamente decidimos desabilitar um servidor. O que vai acontecer?

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

MongoDB poderia morrer porque precisa de quorum: para um cluster de três instalações, pelo menos duas devem funcionar. No entanto, isso não ocorre - graças a PodDisruptionOrçamento. Este parâmetro determina o número mínimo necessário de pods em funcionamento. Saber que um dos pods do MongoDB não está mais funcionando e ver que PodDisruptionBudget está definido para MongoDB minAvailable: 2, o Kubernetes não permitirá que você exclua um pod.

Resumindo: para que a movimentação (e de fato, a recriação) dos pods funcione corretamente quando o cluster for liberado, é necessário configurar o PodDisruptionBudget.

Escala horizontal

Vamos considerar outra situação. Há um aplicativo em execução como Deployment no Kubernetes. O tráfego do usuário chega aos seus pods (por exemplo, há três deles) e medimos um determinado indicador neles (digamos, carga da CPU). Quando a carga aumenta, registramos isso em um agendamento e aumentamos o número de pods para distribuir solicitações.

Hoje no Kubernetes isso não precisa ser feito manualmente: um aumento/diminuição automático no número de pods é configurado dependendo dos valores dos indicadores de carga medidos.

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

As principais questões aqui são: o que exatamente medir и como interpretar valores obtidos (para tomar uma decisão sobre a alteração do número de pods). Você pode medir muito:

Escalonamento automático e gerenciamento de recursos no Kubernetes (visão geral e relatório de vídeo)

Como fazer isso tecnicamente – coletar métricas, etc. — Falei detalhadamente no relatório sobre Monitoramento e Kubernetes. E o principal conselho para escolher os parâmetros ideais é experimentar!

Tem Método de uso (Utilização de Saturação e Erros), cujo significado é o seguinte. Com base em que faz sentido escalar, por exemplo, php-fpm? Com base no facto de que os trabalhadores estão a esgotar-se, isto é utilização. E se acabarem os trabalhadores e não forem aceitas novas ligações, isso já é saturação. Ambos os parâmetros devem ser medidos e, dependendo dos valores, a escala deve ser realizada.

Em vez de uma conclusão

O relatório tem uma continuação: sobre a escala vertical e como selecionar os recursos certos. Falarei sobre isso em vídeos futuros no nosso YouTube - inscreva-se para não perder!

Vídeos e slides

Vídeo da performance (44 minutos):

Apresentação do relatório:

PS

Outros relatórios sobre Kubernetes em nosso blog:

Fonte: habr.com

Adicionar um comentário