Karpenter — Estratégias para resiliência no uso de Spot Instances em produção
Update 17/11/2023 - Alguns manifestos mudam sua estrutura a partir da versão 0.32.x do Karpenter. Nessa data de hoje aproveitei para atualizar os exemplos para os schemas mais novos. Confira o blogpost do Edson sobre o tema.
Esse é o segundo artigo que eu publico sobre Karpenter. Dessa vez decidi trazer um ponto de vista bem legal que é a adoção de uso de Spots em produção.
Utilizar spots é uma estratégia muito comum pra quem deseja algum saving na conta da AWS no fim do mês, podem ser utilizada em formas de EC2 diretamente, Containers, Workloads de data e etc.
As instâncias spots nos permitem utilizar capacity ocioso do EC2 na AWS, gerando um saving de até 90% no custo das instâncias comparados aos preços On Demand. Porém a AWS não da uma garantia de que sua instância irá durar para sempre, podendo ser retirada do seu workload a qualquer momento. Por essa questão, o produto é desenhado pra aplicações que rodem no modelo mais stateless possível.
É uma boa pratica ver ambientes de desenvolvimento e teste rodando 100% em spots para diminuir custo, mas ainda existe um certo “receio” em ver ambientes produtivos utilizando toda, ou parte, da sua carga de trabalho em spots. Mas é possível, se utilizarmos de algumas estratégias para isso.
A ideia desse artigo é demonstrar possibilidades do uso do Karpenter para ganhar um pouco mais de resiliência em produção em ambientes que rodam totalmente, ou parcialmente com Spots.
Caso você não tenha visto ainda, fiz um artigo sobre uma PoC onde descrevo como criar um ambiente em Amazon EKS sem Node Groups, utilizando somente o Karpenter pra suprir o capacity computacional.
Provisionando um cluster de EKS sem Node Groups com Karpenter
Caso você não tenha visto ainda, te convido para ler um artigo que eu escrevi sobre como utilizar o Istio para sobreviver a cenários de caos. Não tem nada a ver com o tema, mas eu acho que você vai gostar. #Confia.
Sobrevivendo a cenários de caos no Kubernetes com Istio e Amazon EKS
Todos os exemplos aqui do texto estão feitos de forma resumida, porém você pode encontrá-los de forma completa neste repositório do Github
Cenário Inicial
Nesse artigo iremos abordar as estratégias:
- Multi-AZ
- Diversificação de Instâncias
- Diversificação entre capacity Spots x On Demand
Todos os cenários vão seguir a mesma formula, vamos escalar todos os pods de 2 para 100 e ver como o provisionamento vai se comportar.
Multi-AZ
Inicialmente, vamos fazer o básico em relação a ambientes produtivos, sendo ele rodando em spots ou não. Rodar em Multi AZ é o arroz com feijão quando falamos sobre cloud pública no geral. E quando falamos em ambientes 100% spots, onde vamos executar esse primeiro cenário, é praticamente impossível ganhar qualquer tipo de estabilidade sem rodarmos Multi AZ.
Vamos adicionar uma especificação sobre a label topology.kubernetes.io/zone tendo todas as AZ’s que sua aplicação deverá utilizar
Agora vamos realizar uma modificação no nosso deployment utilizando os topology spreads e skews. O controlador do Karpenter vai se basear nessa informação pra realizar o provisionamento dos nodes quando precisar suprir um capacity.
Diversificação de Máquinas
Uma das estratégias mais efetivas pra se proteger contra compras bruscas de tipos de instancias especificas de spots é a diversificação.
Isso significa subir mais de um tipo de familia e tamanho no workload. Assim, se subirmos um pool de c5.large, m5.large e r5.large, caso exista uma compra massiva de algum desses tamanhos, podemos proteger de forma segura a disponibilidade de nossas aplicações se elas forem distribuídas de forma inteligente entre os nodes.
Primeiramente vamos adicionar/alterar no NodePool a spec baseada na label node.kubernetes.io/instance-type dos nodes, e nela vamos adicionar uma lista contendo os tipos de familia que podem ser lançadas para suprir capacity.
Vamos editar o deployment e adicionar o topology spread baseado na label node.kubernetes.io/instance-type também. Dessa forma vamos direcionar uma distribuição do nosso deployment entre os tipos de instancia, assim como fizemos com as AZ’s.
Vamos fazer o scale do deployment de 2 para 100 pra ver como a distribuição irá ocorrer.
1
kubectl scale --replicas 100 deploy/chip -n chip
Dessa forma, conseguimos gerar uma distribuição bem tranquila entre os tamanhos de nodes do cluster pra suprir o novo capacity solicitado conforme o esperado.
Diversificação On Demand x Spots
Uma estratégia mais conservadora e segura de se usar spots em produção baseia-se em fazer uma diversificação entre instancias Spots e On Demand. Mantendo uma porcentagem do workload em extrema segurança. Nesse sentido, o Karpenter também nos permite selecionar mais de um tipo de Capacity Type na label karpenter.sh/capacity-type.
Agora vamos ajustar o Spread Constraint também como fizemos nos exemplos anteriores para distribuir os pods entre os tipos de nodes (já que agora temos não só o uso de spots, mas também nodes on-demand) assim como fizemos com as AZ’s e os tipos de instâncias.
Dessa forma também conseguimos instruir o Karpenter pra subir de forma diversificada a quantidade de Spots vs On Demand.
Node Termination Handler - DEPRECATED
O Node Termination Handler é uma forma interessante de fazer Drain dos nossos nodes com base em notificações de Spot Interruptions, Rebalance Recommendations do Autoscale Group ou de um desligamento padrão das EC2. Esses eventos podem ser muito comuns quando tratamos de ambientes voláteis que utilizam estratégias de Spots.
Update 17/11/2023 - Segundo a comunidade do Karpenter, não é mais recomendado o uso do Node Termination handler pois o proprio componente agora faz Interruption Handling. - Link
A Referencia continuará no post mas os detalhes de implementação serão removidos. Para consulta de referencia, os detalhes de implementação estão neste link
No proximo topico abordaremos o Interruption Handler nativo do Karpenter.
Karpenter - Interruption Handling
Update 17/11/2023 - Como mencionado no topico anterior, agora o Karpenter possui a feature de realizar handling dos nodes que mudam de estado.
Para fazer o provisionamento temos que criar a fila SQS da mesma forma como
Para hablitar o Interruption Handling nativo do Karpenter, basta informar o nome da queue na instalação do helm
1
2
3
4
5
6
7
8
resource "aws_sqs_queue" "node_termination_handler" {
name = format("%s-aws-node-termination-handler", var.cluster_name)
delay_seconds = 0
max_message_size = 2048
message_retention_seconds = 86400
receive_wait_time_seconds = 10
visibility_timeout_seconds = 60
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
resource "helm_release" "karpenter" {
namespace = "karpenter"
create_namespace = true
name = "karpenter"
repository = "https://charts.karpenter.sh"
chart = "karpenter"
set {
name = "clusterName"
value = var.cluster_name
}
set {
name = "clusterEndpoint"
value = aws_eks_cluster.eks_cluster.endpoint
}
set {
name = "settings.interruptionQueue"
value = aws_sqs_queue.node_termination_handler.name
}
}
Referências / Material de Apoio
- EKS Best Pratices https://aws.github.io/aws-eks-best-practices/karpenter/
- Pod Topology Spread Constraints https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
- Karpenter Provisioner API https://karpenter.sh/v0.5.6/provisioner/
- Karpenter Topology Spread https://karpenter.sh/v0.13.2/tasks/scheduling/#topology-spread
- EC2 Spot Best Pratices https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-best-practices.html
- EC2 Spots https://aws.amazon.com/pt/ec2/spot/
- Node Termination Handler https://github.com/aws/aws-node-termination-handler
- Interruption Handling https://karpenter.sh/docs/faq/#should-i-use-karpenter-interruption-handling-alongside-node-termination-handler
- O que muda no Karpenter a partir das versões 0.32.x? https://blog.edsoncelio.dev/o-que-muda-no-karpenter-a-partir-das-versoes-032x
Obrigado aos revisores:
- @Daniel_Requena
- Marcos Magalhães (@mmagalha)
- Rafael Gomes (@gomex)
- @indiepagodeiro
- Edson Celio (@tuxpilgrim)
Me sigam no Twitter para acompanhar as paradinhas que eu compartilho por lá!
Te ajudei de alguma forma? Me pague um café (Mentira, todos os valores doados nessa chave são dobrados por mim e destinados a ongs de apoio e resgate animal)
Chave Pix: fe60fe92-ecba-4165-be5a-3dccf8a06bfc