Como criar o cluster Kubernetes mais eficiente em custo do mundo

Felipe Schossler
Ship It!
Published in
10 min readApr 11, 2022

Ok, eu sei, o título é chamativo e parece “clickbait”, mas não é, eu prometo. Hoje vamos aprender de várias maneiras diferentes como criar um cluster K8s se preocupando com custos do jeito certo.

Otimização de custos no Kubernetes

Spoiler: economizamos milhares de reais por mês aqui na RD aplicando essas táticas que veremos aqui neste artigo, bom demais né? Então pega um café e vem comigo ☕

Para contar pra você como chegamos nesse absurdo de diminuir milhares de reais do nosso custo de produção dividiremos nos seguintes assuntos:

Passaremos primeiro por aspectos mais relacionados a GCP como, CUD e On-demand vs Preemptible, segundo por aspectos específicos do Kubernetes como Pod Request, HPA e nossa app interna criada para dar match do número de nós com o número de Pods, o Nightly Scaler. Here we go!

CUD (o tipo de instância que eu mais vou usar é essa, consigo desconto?)

SIM. Na google é possível “adquirir” — por 1 ou 3 anos via Console o compromisso de uso em troca de preços com grandes descontos. Após a assinatura, você é cobrado mensalmente mesmo se não utilizar os recursos. É possível compartilhar a assinatura para toda a Billing Account (todos os projetos).

Beleza Felipe, mas o CUD serve pra meu caso? Se você tem workloads mais previsíveis (estamos falando aqui em período de um ano no mínimo) serve sim, você consegue economizar até 70% se comprometendo a utilizar uma quantidade “x” de recursos. Aqui vão algumas orientações sobre CUD:

Dicas:

  • Verifique quais são os tipos de instâncias mais utilizadas pela sua organização.
  • Faça um cálculo de previsão com base nos anos anteriores (não esqueça BlackFriday).
  • Crie um CUD para esses tipos de instâncias com base na sua previsão.
  • Sempre verifique a utilização dos seus CUDs para que seja reajustado caso necessário.

Restrições:

  • Só é possível adquirir compromissos por região.
  • Só é possível adquirir compromissos por tipo específico de máquina. (Ex. N1 não se aplica para N2).
  • Não se aplica para máquinas preemptivas (aí seria o desconto do desconto 🤣).
  • Após adquirir, não é possível alterar ou cancelar um compromisso.

On demand vs Preemptible Instances (se esse nó cair, tem problema?)

Uma das premissas no Kubernetes é que você consegue rodá-lo em basicamente qualquer dispositivo que aceite um Sistema Operacional e tenha hardware disponível (CPU, RAM, Storage). Habilitando a opção de Cluster Autoscale conseguimos fazer o up e down dos nós de acordo com a necessidade de utilização em nosso cluster. Para discutir melhor isso e linkar com as opções que temos na GCP (e em basicamente todos os cloud providers) irei descrever os dois possíveis tipos de utilização de instâncias que a GCP possui:

On-demand: instâncias das quais podemos adicionar ou remover sob-medida, de acordo com nossa vontade, você pede a GCP obedece.

  • Vantagens: se utilizarmos nosso custo provisionado pelo CUD conseguimos grandes descontos.
  • Desvantagens: se colocarmos na ponta do lápis, as preemptivas ainda podem ser mais vantajosas em custo.

Preemptible: instâncias que não temos o controle do seu tempo de vida, na qual a GCP pode simplesmente “desligá-la” e pegar aqueles recursos para realocá-los em outras necessidades.

  • Vantagens: seu preço é o mais atrativo da GCP, podendo chegar entre 60% à 90% de desconto.
  • Desvantagens: você não controla quando essa instância vai ser desligada.

Baseado nisso temos 3 cenários para o nosso cluster GKE:

  • Tudo Preemtiple
  • Instance group Preemtiple, instance group On-demand (CUD)
  • Tudo on-demand (com CUD)

No primeiro cenário teremos uma estabilidade menor, pois a GCP pode a qualquer momento desligar essas instâncias e utilizar seus recursos temporariamente se isso ocorrer com grande parte do seu cluster, com certeza será um grande problema. No segundo cenário definimos um grupo on-demand com o que seria o workload normal e o grupo Preemptiple seria para todo o workload adicional (isso nos dá escala mais barata e uma maior estabilidade). No terceiro cenário somente com on-demand teremos maior estabilidade porque as instâncias não serão voláteis (lembrando aqui caso possível usar CUD nessas instâncias).

Pod Request (beleza, mas quanto sua aplicação precisa de recurso?)

Aqui está um dos principais vilões quando se trata de custos em um cluster GKE com autoprovisioning caso não seja utilizado de modo correto.

Os requests dos Pods no Kubernetes são utilizados para dizer ao kube-scheduler quanto o seu pod vai precisar de recurso para funcionar, e o kube-scheduler vai verificar se existe o espaço necessário no cluster. Caso não exista, ele vai solicitar ao autoprovisioning que faça a adição de 1 novo nó para colocar esse pod.

Requests e Limits no Kubernetes

Basicamente, se você definir um request alto que sua aplicação não utilize, aquela fatia entre “Utilização” e “Request” será entendida como utilizada pelo kube-scheduler. Exemplo:

Utilização de RAM do Pod vs Request de RAM do Pod

Esses inofensivos 128Mi de diferença parecem pouco, mas dependendo da quantidade de pods que esse deployment pode ter — como por exemplo 200 — um simples cálculo (128Mi * 200 pods) chegamos ao resultado de 25600Mi em RAM que o kube-scheduler irá entender que estão sendo utilizados, que na verdade estão ociosos (ou seja, a instância não vai utilizar toda a sua capacidade computacional 😥).

Dicas:

  • O primeiro passo de tudo é monitorar. Entender o que cada aplicação precisa no seu workload normal é vital para vários pontos dentro do desenvolvimento de software e aqui na definição de requests não é diferente 😉 .
  • O segundo passo é definir nossos requests no Kubernetes com base no que verificamos anteriormente (se baseie inicialmente dentro dos últimos 30 dias, 7 dias, ou o timerange que você tiver).
  • E por fim o terceiro e último passo será já iniciar com os requests corretamente. Falar com os times responsáveis para testar e verificar quais são os requests em um workload normal é com certeza o melhor dos cenários.

HPA (mas não é só habilitar e ser feliz?)

Quem dera. HPA é um assunto complexo (principalmente em larga escala). Entender sua utilização é essencial para que tenhamos qualidade na resiliência das nossas aplicações.

Cada workload tem uma peculiaridade única e é indispensável entender quais métricas são importantes para fazer o scale dos seus deployments. Baseado nisso seguem algumas dicas que utilizamos aqui na RD para definir nossos HPAs e deixá-los mais eficientes possível:

  • Entenda sobre seu workload: seu deploy é um serviço? Tem picos de funcionamento no seu uso normal? Funciona em batches de processamento ou recebe requisições a todo momento?
  • Defina seu target do HPA com base na velocidade de scale desejada: quanto menor o número ou porcentagem utilizada no target mais rápido seus pods farão scale. Então mais uma vez aqui fica a dica, entenda do seu workload.
  • Defina uma métrica que reflita a necessidade de scale: temos infinitas métricas que podemos escolher utilizando o CustomMetrics do HPA. Escolher uma dessas métricas é arte. Entenda o que mais impacta o scale dos seus pods a tempo de servirem as requisições a seus clientes. Pode ser uma fila no pub/sub, requests HTTP entre outros, o importante aqui é testar e ajustar de acordo com a necessidade.
  • Não esqueça do flapping: flapping é quando seu workload fica continuamente subindo e descendo e por consequência seus pods também (o que pode se tornar um problema devido ao tempo de startup do pod). Você consegue definir uma janela de estabilização para que a quantidade de pods se mantenha por mais tempo ou faça o scale em grupos de pods visando contornar o problema de flapping.
  • Monitore, monitore e monitore: tenha métricas de quantidade de pods e requests, verifique a utilização dos mesmos de 30 dias atrás até o momento, seja data-driven, isso vai te dar propriedade para a tomada de decisão de qual threshold definir e da quantidade de pods necessária para aquele determinado deployment. Alguns exemplos de dashboards do Grafana que utilizamos aqui na RD para auxiliar na tomada de decisão:
Dashboard para visualizar infos sobre o HPA de um determinado deployment.
Dashboard que mostra em vermelho o Limit do Pod e em amarelo o Request. As linhas coloridas são a utilização atual de cada pod.

E por fim, uma vez que você definiu seu HPA você pode descansar? Infelizmente não 😔.

Como normalmente as aplicações estão em constante mudança e o workload das mesmas está sempre variando, ficar atento a cada aspecto do HPA e de tempos em tempos atualizá-los é uma tarefa necessária. Recomendamos criar mecanismos de validação de eficiência e alertas para que os deployments menos eficientes sejam visíveis a todos e corrigidos.

Nightly Scaler (selo RD de qualidade)

Ao longo do tempo utilizando o GKE, percebemos que às vezes o autoprovisioning não era eficiente em entender quantos nós eram realmente necessários, principalmete quando era necessário o o downscale. Visto que nosso workload diminui radicalmente no período da noite, o número de nós deveria seguir o mesmo fluxo. Mas não seguia. Até que surgiu o “Nightly Scaler”.

O Nightly Scaler é uma aplicação desenvolvida internamente aqui na RD (kudos @Suzano) que faz o match ideal entre o número de Pods atual e o número de nós. Basicamente ela calcula o seguinte (visando que o máximo de pods por nó é de 110):

Número de pods rodando no cluster / 110

Mais algumas implementações foram feitas, mas a ideia geral do código está aqui:

Bônus

Pra você leitor(a), que leu o artigo todo e chegou até o fim, você é merecedor de mais algumas dicas bônus né 😜? Seguem então mais algumas dicas e considerações que valem uma menção honrosa:

Autoscaling profile

A opção Autoscaling Profile (utilizada somente com a opção de Cluster Autoscaler habilitada), essa configuração é basicamente o GKE te perguntando “como eu posso te ajudar a escalar os nós”, se é de maneira mais “ativa” ou mais “passiva”.

  • Balanced: é a padrão, mais conservadora ela tenta manter o workload dos seus nós mais estável.
  • Optimize Utilization: prioriza performance ao invés de manter a mesma quantidade de nós. Basicamente remove os nós de maneira mais agressiva (olhando pra quantidade de requests dentro do cluster). A propósito é a que estamos utilizando com sucesso até este momento.

Lembre dos seus PVs e PVCs

Lembrou deles? É, as vezes a gente esquece. Na maioria dos casos são discos persistentes que são utilizados pelo GKE por workloads com algum tipo de persistência (Stack de Observabilidade, Operators, Databases e etc) que acabamos esquecendo pois normalmente essas aplicações abstém a complexidade de utilização principalmente por parte de Storage. Mas vale notar que no final são PDs(Persist Disks) na GCP que estão sendo criados e custos sendo adicionados.

Lembrarmos da utilização deles e tomarmos ações pró-ativas de utilizar somente o necessário garantirá que manteremos nossos custos sob controle não só do ponto de vista de CPU e RAM (que é o que basicamente quem está trabalhando com Kubernetes se preocupa) mas também do ponto de vista de storage.

Monitore tudo!

Monitoramento não é um princípio de SRE à toa. A facilidade e assertividade que teremos devido a observabilidade valem os custos de implementação. Algumas vantagens são:

  • Comparar o “antes e depois” de cada mudança, entendendo quais foram os ganhos reais
  • Ter um maior entendimento do funcionamento do workload da sua aplicação
  • Criação de alertas com base em discrepâncias e problemas

Diria que sem todo o sistema de observabilidade que possuímos aqui na RD (métricas, logs, tracing) eu não seria capaz de reduzir o custo de maneira efetiva como foi o que felizmente aconteceu.

Conclusão

Economizamos milhares de reais com tudo isso, na prática reduzimos mais de 50% dos custos de produção. Eu mesmo que participei da tarefa ativamente fico chocado com quanto conseguimos economizar utilizando as melhores práticas, ferramentas e processos. Alguns números interessantes de compartilhar com vocês são:

  • Nosso workload em número de nós bateu o pico de 128 nós em média antes de todo o processo de otimização, hoje após esse processo o cluster roda em torno de 50 nós 😲.
  • O número de pods de um dia típico, chegava à quase 6k, hoje roda em torno dos 5k (mas à noite o ganho chegou em torno de 1,5k de pods de diferença entre antes e depois das melhorias 🚀).

Se você pessoa ou time leu esse artigo e finalmente se sentiu inspirado a discutir e tomar essas ações na sua organização, o que gostaria de deixar de dicas para vocês é:

  • Deixe todos cientes do processo (muitas vezes eu mesmo não avisei a determinado time que iria começar as melhorias, o que causou bastante ruído).
  • Sempre tome suas decisões baseadas em data-driven (não esqueça o monitoramento!).
  • De primeira acertar os requests e o HPA pode não ser fácil, não se frustre, reajuste e entenda em quais pontos você estava errado.
  • Desenvolva a cultura de out-teach em sua organização, passando aos times todos os mecanismos e conhecimento necessário para se tornarem auto-suficientes nas tomadas de decisões (requests, HPA e etc).

Para finalizar gostaria de encerrar com uma frase emblemática do livro de SRE que diz muito sobre iniciar certo, arquitetando corretamente:

“Uma grama de prevenção vale por um quilo de cura”.

É isso meus amigues. Vejo vocês na próxima, espero que tenham gostado. Adiós!

“Isso é tudo pessoal.” by Looney Tunes

--

--