DEV Community

Rodrigo Fernandes
Rodrigo Fernandes

Posted on • Updated on

Implementando aplicações altamente escaláveis com Amazon EKS e Karpenter utilizando Terraform.

Introdução

A automação e escalabilidade são elementos cruciais na gestão de infraestrutura em ambientes de nuvem, especialmente quando se trata de clusters Kubernetes. Neste artigo, exploraremos o processo técnico de implantação do Karpenter, uma ferramenta de escalonamento automático para Kubernetes, usando Terraform em um cluster Amazon EKS na AWS.

O papel do Karpenter na escalabilidade

O Karpenter utiliza métricas de uso de recursos e políticas de escalonamento para provisionar automaticamente e de forma otimizada novos nós de computação quando necessário, garantindo que as aplicações tenham recursos suficientes para lidar com as demandas.

Desafios e Soluções

Implementar manualmente políticas de escalonamento automático pode ser complexo e propenso a erros. O uso do Terraform simplifica esse processo, permitindo a definição da infraestrutura como código (IaC) e garantindo a consistência e repetibilidade nas implantações.

Benefícios desta Abordagem

Ao adotar essa abordagem, você automatiza a implantação e o gerenciamento do Karpenter em um ambiente Kubernetes na AWS, garantindo uma infraestrutura escalável, confiável e consistente.

O que é Karpenter

Karpenter é um sistema de autoescalonamento ele foi projetado para automatizar o dimensionamento de recursos em ambientes baseados em Kubernetes.

O objetivo principal é otimizar o uso de recursos, garantindo que os aplicativos tenham a capacidade necessária para atender à demanda, ao mesmo tempo em que evita o desperdício de recursos quando a demanda diminui.

O Karpenter é uma ferramenta útil para equipes que gerenciam clusters em Kubernetes, pois elimina a necessidade de ajustes manuais no dimensionamento dos recursos.

Ele monitora a utilização dos recursos e toma decisões automatizadas com base nas métricas e políticas configuradas pelos usuários.

Isso ajuda a garantir um desempenho eficiente e econômico dos aplicativos implantados em ambientes Kubernetes.

Mais informações sobre o Karpenter acesse a pagina oficial clicando aqui.

Como funciona

O Karpenter funciona como um controlador personalizado para Kubernetes. Ele estende as capacidades padrão do Kubernetes, permitindo um autoescalonamento mais inteligente e eficiente.

Abaixo uma visão geral de como o Karpenter opera:

Image description

Monitoramento de métricas
Monitora constantemente métricas como uso de CPU, memória, e outras métricas relevantes dos pods e dos nós do cluster Kubernetes. Pode se integrar a sistemas de monitoramento como Prometheus para coletar essas métricas.

Análise de métricas e demanda
Com base nas métricas coletadas, o Karpenter analisa a demanda atual dos recursos pelo cluster, identificando padrões de uso e tendências.

Políticas de autoescalonamento
Os usuários podem definir políticas de autoescalonamento que especificam como o Karpenter deve ajustar a quantidade de recursos disponíveis no cluster. Isso inclui especificar limites mínimos e máximos para o número de pods e nós, bem como configurar estratégias de alocação de recursos.

Decisões de escalonamento
Com base nas métricas coletadas e nas políticas definidas, o Karpenter toma decisões automatizadas sobre a criação, destruição ou dimensionamento de pods e nós no cluster Kubernetes. Ele pode escalar para cima (aumentando o número de pods ou nós) ou para baixo (reduzindo o número de pods ou nós) conforme necessário para atender à demanda.

Alocação inteligente de recursos
Ele realiza uma alocação inteligente de recursos, distribuindo os pods de maneira eficiente nos nós disponíveis, levando em consideração as políticas de balanceamento de carga e os requisitos de recursos de cada pod.

Em resumo
O Karpenter automatiza o processo de escalonamento de recursos em clusters Kubernetes, garantindo que os aplicativos tenham a capacidade necessária para lidar com a demanda atual, ao mesmo tempo em que otimiza a utilização dos recursos disponíveis e evita o desperdício.

Isso simplifica a operação e a gestão de ambientes Kubernetes, melhorando a eficiência e o desempenho dos aplicativos implantados.

Alguns pontos importantes

  • Provisione Worker Nodes com base nos requisitos de Workload.
  • Crie diversas configurações de Worker Nodes por tipo de instância, usando opções flexíveis de NodePool. Em vez de gerenciar muitos Worker Nodes Groups personalizados específicos, o Karpenter pode permitir que você gerencie diversas capacidades de carga de trabalho com um NodePool único e flexível.
  • Obtenha um melhor agendamento de pods em escala, iniciando rapidamente Worker Nodes e agendando pods.

Alguns conceitos importantes

AWS NodeTemplate

No contexto do Karpenter se refere a uma funcionalidade específica dessa ferramenta de autoescalonamento para ambientes Kubernetes na AWS.

O AWS NodeTemplate permite definir configurações detalhadas para instâncias Worker Nodes no AWS EKS, incluindo o tipo de instância, capacidade de CPU, capacidade de memória, sistema operacional, configurações de rede, volumes anexados e outras opções. Essas configurações são essenciais para a criação de novos Worker Nodes no cluster Kubernetes da AWS de acordo com as necessidades específicas de cada aplicativo.

SubnetSelector

É uma funcionalidade que permite aos usuários selecionar sub-redes específicas dentro de uma VPC para implantar instâncias de Worker Nodes quando estão utilizando o Karpenter em conjunto com o Amazon EKS.

SecurityGroupSelector

É uma funcionalidade que permite aos usuários selecionar Security Groups específicos da AWS para Worker Nodes que estão sendo implantadas em um cluster Kubernetes gerenciado pelo Karpenter, especialmente quando utilizado em conjunto com o Amazon EKS.

Essa funcionalidade é útil por várias razões:

Segurança: Permite implementar políticas de segurança granulares, garantindo que as instâncias de Worker Nodes sejam associadas aos grupos de segurança adequados com as regras de firewall corretas.

Conformidade: Facilita a conformidade com padrões e regulamentos de segurança, pois você pode garantir que apenas as instâncias de Worker Nodes apropriadas tenham acesso aos recursos de rede necessários.

Isolamento: Ajuda a isolar o tráfego de rede entre diferentes componentes do seu ambiente Kubernetes na AWS, aumentando a segurança e reduzindo riscos.

Best practices

Exclua tipos de instância que não se adequam à sua carga de trabalho
Considere excluir tipos de instâncias específicas com a chave node.kubernetes.io/instance-type se elas não forem exigidas pelas cargas de trabalho em execução no seu cluster.

O exemplo a seguir mostra como evitar o provisionamento de grandes instâncias Graviton.

  • key: node.kubernetes.io/instance-type operator: NotIn values:
    • m6g.16xlarge
    • m6gd.16xlarge
    • r6g.16xlarge
    • r6gd.16xlarge
    • c6g.16xlarge

Habilite o tratamento de interrupções ao usar o Spot
Karpenter suporta tratamento de interrupção nativo , habilitado através do --interruption-queue-nameargumento CLI com o nome da fila SQS. O tratamento de interrupções monitora eventos futuros de interrupção involuntária que causariam interrupção em suas cargas de trabalho, como:

Avisos de interrupção pontual
Eventos de integridade de alteração agendados (eventos de manutenção)
Eventos de encerramento de instância
Eventos de interrupção de instância
Quando o Karpenter detecta que um desses eventos ocorrerá em seus Worker Nodes, ele automaticamente isola, drena e encerra os Worker Nodes antes do evento de interrupção para fornecer o tempo máximo para limpeza da carga de trabalho antes da interrupção

Criando NodePools
As práticas recomendadas a seguir abrangem tópicos relacionados à criação de NodePools.

Crie vários NodePools quando...
Quando diferentes equipes estão compartilhando um cluster e precisam executar suas cargas de trabalho em diferentes Worker Nodes de trabalho ou têm diferentes requisitos de sistema operacional ou de tipo de instância, crie vários NodePools. Por exemplo, uma equipe pode querer usar o Bottlerocket, enquanto outra pode querer usar o Amazon Linux. Da mesma forma, uma equipe pode ter acesso a hardware de GPU caro que não seria necessário para outra equipe. O uso de vários NodePools garante que os ativos mais apropriados estejam disponíveis para cada equipe.

Crie NodePools que sejam mutuamente exclusivos ou ponderados
Recomenda-se criar NodePools que sejam mutuamente exclusivos ou ponderados para fornecer um comportamento de agendamento consistente. Se não forem e vários NodePools corresponderem, o Karpenter escolherá aleatoriamente qual usar, causando resultados inesperados.

Use temporizadores (TTL) para excluir automaticamente Worker Nodes do cluster
É possível usar temporizadores em Worker Nodes provisionados para definir quando excluir Worker Nodes que estão desprovidos de pods de carga de trabalho ou que atingiram um tempo de expiração. A expiração do nó pode ser usada como meio de atualização, para que os Worker Nodes sejam retirados e substituídos por versões atualizadas. Consulte Expiração na documentação do Karpenter para obter informações sobre como spec.disruption.expireAfterconfigurar a expiração do nó.

Criando o ambiente do cluster EKS

Para criar um ambiente com um cluster EKS para nossos testes, iremos utilizar o repositório que está disponivel nesse link.

EKS Blueprint
O código fonte do repositório acima é baseado nos Blueprints que são forneceidos pela AWS.
A AWS disponibiliza diversos templates prontos, os chamados Blueprints. Eles abstraem as complexidades da infraestrutura permitindo que seja implantado cargas de trabalhado de forma simples e replicavel.
Segue o link dos vários modelos de Blueprint que pode ser acessado clicando aqui.

Clone do repositório

git clone https://github.com/rodrigofrs13/workshop-aws-eks-karpenter

Acesse o diretório /workshop-aws-eks-karpenter/enviroment

Editando as variaveis
Edite o arquivo arquivo terraform.tfvars com as variaveis que podem ser ajustadas antes de iniciar a instalação, como por exemplo:

- Região AWS
- Nome do cluster EKS
- Versão do cluster EKS
- Versão dos Addons
- Nome da Role Admin

Setup do ambiente
Com o comando abaixo vamos iniciar o Terraform, criar o Plan e efeturar o setup.

terraform init && terraform plan && terraform apply --auto-approve

Acesse o diretório /workshop-aws-eks-karpenter/eks-blue

Editando as variaveis
Edite o arquivo arquivo terraform.tfvars com as variaveis que podem ser ajustadas antes de iniciar a instalação, como por exemplo:

- Região AWS
- Nome do cluster EKS
- Versão do cluster EKS
- Versão dos Addons
- Nome da Role Admin

Setup do ambiente
Com o comando abaixo vamos iniciar o Terraform, criar o Plan e efeturar o setup.

terraform init && terraform plan && terraform apply --auto-approve

Acessando o Cluster

Vamos acessar o cluster EKS com seguinte comando para configurar o kubectl para apontar para o cluster desejado:

$ aws eks --region <region> update-kubeconfig --name <cluster-name>

Caso tenha perdido o output, utilize o comando terraform output.

Vamos fazer alguns testes iniciais para verificar se está tudo certo com nosso cluster.

Coletando algumas informações.

kubectl cluster-info

Image description

Verificando os Worker Nodes

kubectl get nodes -o wide

Image description

Analisando todos os recursos criados

kubectl get all -A

Image description

Com isso podemos concluir que nosso cluster está funcionando corretamente e estamos prontos para a próxima etapa, o Karpenter.

Image description


Validando o Karpenter

Como utilizamos um Blueprint da AWS, o Karpenter já está instalado e configurado em nosso cluster.

Vamos fazer algumas validações para confirmar se está tudo certo com o Karpenter.

Validando os pods.

kubectl get pods -n karpenter

Image description

Validando o Provisioner

kubectl get provisioner

Image description

Com isso o Karpenter está instalado e executando com sucesso.

Vamos criar um NodePool com algumas regras para começarmos a utilizar o Karpenter e escalar de forma inteligente o nosso ambiente.

Como estamos utilizando os Blueprints já temos a parte GitOps e CICD configurados. Em nosso estudo de caso vamos utilizar uma parte dessas ferramentas.

O repositório que pode ser acessado por esse link é um Fork de um blueprint que iremos utilizar.
Faça o clone do repositório com o comando git clone https://github.com/rodrigofrs13/eks-blueprints-workloads.git

No deployment da app temos o NodeSelector que indica que todos os pods daquele deployment serão deployados nos Worker Nodes do Karpenter.

Iremos utilizar o Team Riker para nossos testes, acesse arquivo do Karpenter que está nesse caminho: teams/team-riker/dev/templates/karpenter.yaml.

Vamos configurar o nosso AWSNodeTemplate e Provisioner para definir algumas configurações para o Karpenter.

Segue abaixo um exemplo do nosso NodePool

{{ if .Values.spec.karpenterInstanceProfile }}
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: karpenter-default
  labels:
    {{- toYaml .Values.labels | nindent 4 }}  
spec:
  instanceProfile: '{{ .Values.spec.karpenterInstanceProfile }}'
  subnetSelector:
    kubernetes.io/cluster/{{ .Values.spec.clusterName }}: '*'
    kubernetes.io/role/internal-elb: '1' # to select only private subnets
  securityGroupSelector:
    aws:eks:cluster-name: '{{ .Values.spec.clusterName }}' # Choose only security groups of nodes
  tags:
    karpenter.sh/cluster_name: {{.Values.spec.clusterName}}
    karpenter.sh/provisioner: default
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 2
    httpTokens: required
---
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
  labels:
    {{- toYaml .Values.labels | nindent 4 }}
spec:
  consolidation:
    enabled: true
  #ttlSecondsAfterEmpty: 60 # mutual exclusive with consolitation
  requirements:
    - key: "karpenter.k8s.aws/instance-category"
      operator: In
      values: ["c", "m"]
    - key: karpenter.k8s.aws/instance-cpu
      operator: Lt
      values:
        - '33'    
    - key: 'kubernetes.io/arch'
      operator: In
      values: ['amd64']
    - key: karpenter.sh/capacity-type
      operator: In
      values: ['on-demand']
    - key: kubernetes.io/os
      operator: In
      values:
        - linux
  providerRef:
    name: karpenter-default

  ttlSecondsUntilExpired: 2592000 # 30 Days = 60 * 60 * 24 * 30 Seconds;

  # Priority given to the provisioner when the scheduler considers which provisioner
  # to select. Higher weights indicate higher priority when comparing provisioners.
  # Specifying no weight is equivalent to specifying a weight of 0.
  weight: 1
  limits:
    resources:
      cpu: '2k'
  labels:
    billing-team: default
    team: default
    type: karpenter

  # Do we want to apply some taints on the nodes ?  
  # taints:
  #   - key: karpenter
  #     value: 'true'
  #     effect: NoSchedule

  # Karpenter provides the ability to specify a few additional Kubelet args.
  # These are all optional and provide support for additional customization and use cases.
  kubeletConfiguration:
    containerRuntime: containerd
    maxPods: 110     
    systemReserved:
      cpu: '1'
      memory: 5Gi
      ephemeral-storage: 2Gi
{{ end }}
Enter fullscreen mode Exit fullscreen mode

Alguns itens que foram defidos

Labels
Definimos labels dedicados que podem ser usados ​​por pods como nodeSelectors.

Taints
Podemos adicionar taints aos Worker Nodes para que as cargas de trabalho precisem tolerar que esses taints sejam agendados nos Worker Nodes do Karpenter.

Especificamos alguns requisitos em torno de tipos de instâncias, capacidade e arquitetura; cada provisionador é altamente personalizável.

Após os ajustes vamos fazer o commit e o push

git add teams/team-riker/dev/templates/karpenter.yaml
git commit -m "Add Karpenter provisioner"
git push

Podemos verificar com o eks-node-view(para saber mais clique aqui) abaixo e já podemos ver que alguns Worker Nodes do Karpenter já foram provisionados, cada um em uma AZ por conta das configurações que fizemos.

Image description

Image description

O próximo passo é aumentar o nosso Workload para que o Karpenter começe a dimencionar os Worker Nodes.

Vamos escalar a app skiapp-deployment do Team Riker com o comando abaixo:

> kubectl scale deployment -n team-riker skiapp-deployment --replicas 30

Podemos notar que o Karpenter começa a escalar os novos Worker Nodes conforme os requirimentos baseando sempre na menor instância.

Em um primerio momento o Karpenter adcionou 3 novos Worker Nodes (Atendendo o requisito de Multi-AZ) menores para suportar o aumento do Workload.

Image description

No segundo momento o Karpenter adcionou 3 novos Worker Nodes (Atendendo o requisito de Multi-AZ) menores para suportar o aumento do Workload.

Image description

Vamos escalar a app skiapp-deployment do Team Riker com o comando abaixo para simular um consumo alto de WorkLoad

kubectl scale deployment -n team-riker skiapp-deployment --replicas 100

Podemos notar em um primeiro momento que o Karpenter adcionou novas instancias.

Image description

Em um segundo momento o Karpenter alterou o tipo de instancia para melhorar a eficiencia do ambiente.

Image description

O Karpenter sempre tenta infinitamente economizar custos em seu cluster.

Removendo o ambiente

Vamos dismobilizar a infra reduzindo o Workload para 1

kubectl scale deployment -n team-riker skiapp-deployment --replicas 1

Removendo o cluster
Acesse o diretório /workshop-aws-eks-karpenter/enviroment

terraform destroy--auto-approve

Após a conclusão, acesse o diretório /workshop-aws-eks-karpenter/eks-blue e execute o comando abaixo

terraform destroy--auto-approve

Conclusão

Agora temos a capacidade de adicionar mais capacidade ao nosso cluster para dimensionar nosso Workload e o Karpenter garante um equilíbrio entre instâncias e custos de forma inteligente.

Referências

https://aws.github.io/aws-eks-best-practices/karpenter/

Top comments (0)