Alura > Cursos de DevOps > Cursos de Containers > Conteúdos de Containers > Primeiras aulas do curso Kubernetes: AIOps e self‑healing com observabilidade avançada

Kubernetes: AIOps e self‑healing com observabilidade avançada

AIOps e self-healing em Kubernetes - Introdução ao AIOps

Apresentando o tema e os objetivos

Olá! Eu sou João Brito, sua pessoa anfitriã favorita, e estou aqui em carne e osso, e um pouco de titânio, para compartilhar um pouco sobre AIOps. Vou acompanhar você em AIOps Self-Healing (auto-recuperação) em Kubernetes.

Antes de entrar no tema, vamos deixar claro o escopo. AIOps não é ChatGPT para infraestrutura. O objetivo final é reduzir e, quando possível, eliminar o trabalho operacional repetitivo. Certamente você já ouviu inúmeras falas sobre o conceito de toil (trabalho operacional repetitivo) e sobre a fadiga de alertas. Se você trabalha com operações e ainda não usa o PagerDuty, e não recebe alertas que tocam no seu celular de madrugada, parabéns: você é uma pessoa com sorte. Para o restante, isso acontece de madrugada, e é justamente o que queremos evitar.

Esclarecendo o escopo e alinhando expectativas

Também queremos esclarecer que não buscamos o sonho que já foi moda por um tempo, o chamado NoOps (ausência de operações conduzidas por pessoas). Não acreditamos em um futuro em que tudo aconteça sem envolvimento humano. Quando (e se) a Skynet chegar, aí sim será totalmente autônoma e não teremos de fazer nada além de observá-la destruir o planeta. Até lá, sigamos com responsabilidade.

Se não é NoOps — entendido como IA atuando sozinha nas operações — do que estamos falando? A definição da Gartner ajuda bastante. Caso não conheça a Gartner, pesquise rapidamente e você encontrará quem são. Eles propuseram uma mudança de “AI” (Artificial Intelligence — Inteligência Artificial) para “Algorithmic IT Operations” (Operações de TI Algorítmicas). Essa formulação é mais razoável, factível e próxima da realidade. É nesse sentido que vamos avançar.

Se você procura algo completamente diferente, quase mágico — uma ferramenta que você conecta ao Kubernetes e nunca mais precisa observar — este não é o caminho. Pedimos desculpas caso isso decepcione alguém. Ainda assim, vale ficar: nós vamos chegar a um resultado muito bom.

Definindo AIOps e contextualizando operações

O que é AIOps, afinal? Algorithmic IT Operations. Trabalhamos com:

Sobre regulação: vivemos um momento de apreensão e cuidado em torno da IA. Há diversas regulamentações, e citaremos algumas, com destaque para a da União Europeia, que define infraestrutura como crítica e não permite operação totalmente automatizada apenas por IA.

Nosso foco é operações. Nós não vamos escrever código aqui; se você busca desenvolvimento, há outros cursos na Alura que exploram isso. Nós trabalhamos com a realidade do dia a dia operacional.

Detalhando a escalada operacional e a infraestrutura imutável

Qual é nossa escalada?

Infraestrutura imutável significa evitar “marteladas” manuais quando surgir um problema. Diante de um incidente, não conectamos no servidor ou no cluster (agrupamento) para dar uma “martelada” e seguir adiante. Não operamos assim. Vamos fugir desse padrão e implementar guardrails (travas de segurança) para impedir ações desse tipo.

Se a cultura de infraestrutura imutável não for adotada por toda a companhia, você, ao praticá-la, acabará sofrendo por quem não adota: alguém aplicará uma “martelada”, não documentará, e cairemos no mesmo problema do manual da “pessoa campeã”, que guarda tudo na cabeça. É isso que vamos evitar.

Avançando para self-healing e reduzindo MTTR

No nível de AIOps, reunimos tudo isso: detectar, prever e corrigir o máximo possível. E avançamos rumo ao self-healing (auto-recuperação): o sistema “se cura” — entre aspas bem grandes. Buscamos chegar o mais perto disso que conseguirmos.

Fica uma mensagem final desta etapa: cada degrau dessa escalada tira peso das costas das pessoas. E trabalhamos, cada vez mais, para reduzir o MTTR — Mean Time To Recovery (tempo médio de recuperação).

Focando em SLOs e práticas operacionais

Sua liderança de operações vai cobrar o seu acordo de SLO (objetivo de nível de serviço): qual é o tempo para recuperarmos o sistema? Na prática, sabemos que a maior parte do tempo é gasta buscando a causa raiz, tentando identificar qual é o problema, e não resolvendo o problema em si. Às vezes, trabalhamos diariamente nisso e ainda assim demoramos para resolver, muitas vezes porque atacamos muitos lugares diferentes ao mesmo tempo, como quem usa uma metralhadora e dispara para todo lado: reiniciamos pods (unidades de execução do Kubernetes), reiniciamos nós, reiniciamos o cluster (grupo de servidores), migramos de cluster e assim por diante. Mas, se atuarmos como um franco-atirador e soubermos exatamente onde intervir, levaremos muito menos tempo. Para chegar a esse momento do “disparo único” que resolve o problema, porém, é necessário contexto e informação completa. Somos contra o uso de armas; apenas assistimos a muitos filmes, então nossos exemplos acabam sendo nessa linha.

Alguns termos importantes já mencionados: DevOps (integração entre desenvolvimento e operações), cultura, automação e SRE (Engenharia de Confiabilidade de Sites). Nós nos encaminhamos nessa direção; houve um momento de bots (robôs) e ChatOps (operações via chat), com todas as pessoas operando seus clusters e toda a operação pelo Telegram. Não vimos isso de forma ampla na prática; observamos acontecer, mas não participamos diretamente.

Abandonando prompts isolados e fornecendo contexto à IA

Sobre prompts (instruções) de IA: com o boom dos chats (conversas), muita gente pega um ou dois logs (registros), ou descreve no chat o que está acontecendo, e espera uma resposta. Uma pergunta isolada como “por que meu cluster caiu?” raramente terá resposta útil; se o chat fosse totalmente honesto, diria “eu não sei”. Ainda assim, às vezes pode fornecer alguma pista.

Entramos então em AIOps (operações de TI orientadas por IA): o algoritmo — e a IA atuando como algoritmo, como machine learning (aprendizado de máquina), isto é, uma ferramenta em nossas mãos — precisa receber contexto. Há uma lacuna grande entre o que a IA sabe a partir de documentação e do funcionamento geral de sistemas e o que ela sabe sobre o seu ambiente específico. A IA sabe como o Kubernetes funciona em cada detalhe e, por ser open source (código aberto), pode “ler” todo o código. Mas ela não tem ideia de como o nosso Kubernetes funciona na prática, instalado do nosso jeito. Em muitas empresas há 10, 15, 20 clusters; um time instalou alguns, outro time instalou outros; alguns estão atualizados para a 1.36 (em 2026); outros estão na 1.33; e há ainda clusters nos quais a empresa paga suporte estendido na nuvem, ficando em 1.29 ou 1.30. A IA não conhece esses detalhes da nossa infraestrutura, e eles são essenciais para explicar por que algo caiu.

A resposta é: a IA precisa de todos esses fatores. Quais aplicações estão em execução? Como foi feito o deploy (implantação)? Tudo passou por CI/CD (integração e entrega contínuas) ou não? Houve alguma intervenção manual? Analisamos os eventos? Checamos os logs de auditoria para ver se ninguém se autenticou no cluster? E, se não houver auditoria, podemos até levantar hipóteses mais sérias: talvez exista um pod malicioso derrubando componentes ou iniciando outros indevidamente.

Operando com agente de AIOps e estabelecendo pré-requisitos

O caminho é sair do uso episódico de prompts em chat. No chat, tudo é pontual: abrimos a janela, escrevemos um monte de coisas e esperamos uma resposta — isso toma tempo, e não é para onde vamos. Nosso direcionamento é operar em um ciclo de monitorar, analisar, planejar e executar, e então voltar a monitorar continuamente. Nesse cenário, apresentamos o agente de AIOps: um agente que observa o ambiente, recebe métricas, contexto e informações, e então raciocina, formula uma hipótese de causa raiz e toma uma ação corretiva. Esse agente ainda imita a atuação da pessoa engenheira de plantão, mas para dar um respiro ao time. Certo?

A autonomia tem um pré-requisito — e destacamos isso nesta “lamina ridiculamente amarela” para fixar a ideia: não devemos saltar para agentes autônomos sem telemetria consistente. É indispensável ter dependências mapeadas, com um grafo completo do sistema e das correlações entre componentes, além de runbooks (guias de operação) — mesmo aqueles que nunca mais revisamos são importantes. Não existe self-healing (autocorreção) sem uma base sólida. A base é essencial.

Planejando a rota rumo à autonomia e encerrando a aula

Nossa rota em direção à autonomia — este é o preâmbulo da fase atual — segue assim: escalabilidade e observabilidade; depois, remediação com IA, em que a IA passa a decidir e tomar ações; em seguida, adotamos Chaos Engineering (engenharia do caos), no qual, a partir de hipóteses, provocamos problemas controlados para gerar mais dados, ampliar o conhecimento da IA, entender melhor como ela processa, quais guardrails (barreiras de proteção) precisa e até onde pode chegar; por fim, automação inteligente com LLM (modelo de linguagem de grande porte), pilotando e controlando cada vez mais aspectos do ambiente. Na Aula 5, trataremos de guardrails (barreiras de proteção) e blast radius (raio de impacto). Iremos introduzir mais buzzwords (jargões) ao longo do curso.

Esta é a tríade que guia nossa trajetória, e à qual voltaremos constantemente: observar, decidir e agir. Até onde vamos ousar e até onde a IA atuará sozinha, ainda não sabemos; decidiremos em conjunto ao longo do caminho.

Isso é tudo. Nos vemos na próxima aula. Até mais. [♪]

AIOps e self-healing em Kubernetes - Observabilidade aplicada à escalabilidade operacional

Apresentando a aula e destacando a importância da observabilidade

Olá, eu sou João Brito, seu anfitrião favorito, e estamos aqui para mais uma aula. Continuamos na trilha do autoscaler (escalonador automático) do Kubernetes.

O autoscaler é tão bom quanto o sinal que recebe. Se a telemetria estiver com problemas, se as métricas estiverem com problemas, o pipeline (fluxo) de dados e, por conseguinte, o autoscaler serão tão ruins quanto o restante que estiver comprometido. Um lembrete: vamos parar de escalar por CPU; vamos usar métricas de qualidade. No entanto, não adianta escalar pela sinal correta se não conseguimos coletar e entregar esse sinal. A autonomia está cega sem observabilidade; isso é um passo prévio. Antes de pensar em AIOps, precisamos ter resolvido alguns problemas, e observabilidade é um deles. Como esses números e esses dados chegam até o Prometheus — esse pipeline — será algo a resolver, mas, antes, precisamos ter estruturado todo o restante.

Definindo sinais de escalonamento e padrões de comportamento

As sinais que antecipam o escalonamento — os Golden Signals (Sinais Dourados) — incluem a saturação: quando o recurso mais disputado está saturado, como threads (linhas de execução), fila e conexões. A saturação é um bom gatilho; ela sobe antes de a latência aumentar. A fila começa a encher e o sinal sobe. Degradação: o serviço responde, porém pior. Por exemplo, o P95 de 80 milissegundos passa para 400. É o sintoma que a pessoa usuária percebe. Às vezes, tudo aparece verde no monitoramento, mas, para a pessoa usuária, é um desastre, porque a degradação já começou, embora ainda não tenha disparado em outros sinais.

Comportamentos também importam. Por exemplo, um pico que sempre ocorre às 9 horas estar ocorrendo às 3 da manhã. Isso pode ser um problema, pode ser uma campanha de marketing (marketing) da qual não estamos cientes. De repente, o jogo da seleção acontece em um horário completamente aleatório para a nossa previsibilidade. O comportamento de escalonamento também pode ser preditivo. Não é que “preditivo” signifique nos guiar para algo completamente aleatório. E nem precisa ser preditivo; pode ser matemático, baseado em histórico. É nessa direção que vamos.

Introduzindo o OpenTelemetry e estruturando o pipeline de métricas

OpenTelemetry — também conhecido como “OTel” — e o termo “o11y” para observability (observabilidade). OpenTelemetry é vendor neutral (neutro em relação a fornecedores). Sem lock-in (dependência), OpenTelemetry busca ser o “esperanto do Kubernetes”. Se não soubermos o que é o Esperanto ou por que essa piada não funciona, é porque a maioria das pessoas com menos de 50 anos não conhece. OpenTelemetry propõe ser essa linguagem universal.

A aplicação envia suas métricas via OpenTelemetry, no formato do OpenTelemetry. Um “robô tradutor” entrega ao OpenTelemetry Collector. A partir daí, podemos mandar as métricas para onde quisermos: para o Mateus, para o Datadog, para qualquer outro backend (back-end) que utilizemos. Um ponto importante: se trocarmos o Prometheus por outra solução, continua sendo o OpenTelemetry Collector que enviará para essa outra solução. Se deixarmos de pagar o Datadog e passarmos a pagar o Dynatrace — ou se houver mais orçamento e decidirmos pagar o Instana, e assim por diante — conectamos ao Collector e enviamos para lá; não precisamos alterar a aplicação, nem implantar novos agentes, nem aprender comportamentos diferentes. É OpenTelemetry para frente. Assim, a equipe de desenvolvimento fica livre de reescrever integrações, escreve em um formato único e aproveita o que já foi aprendido.

Alertando sobre provedores e apresentando APIs do HPA

Fica a recomendação: cuidado com o provedor para onde enviamos métricas com OpenTelemetry; muitos, ao ingerirem métricas produzidas por OpenTelemetry — isto é, que não vêm pelo agente deles — cobram mais. Atenção a isso.

Temos três APIs de métricas do HPA:

Descrevendo o fluxo de decisão do HPA e esclarecendo o papel do KEDA

O caminho de decisão fica assim: a aplicação usa o SDK do OpenTelemetry para gerar sua métrica; o Collector recebe essa métrica e a envia para o armazenador — por exemplo, para o Prometheus; o Adapter a traduz para a API de custom metrics; o HPA lê essa métrica e o Deployment sobe ou desce réplicas, e assim sucessivamente.

KEDA não é mágica. É um adapter — já falamos disso; KEDA pode parecer mágica, mas é simplesmente uma métrica externa que traduz o tamanho da fila para o HPA. O HPA continua funcionando exatamente como antes, porém de maneira inteligente. Não é mágica; é tecnologia.

Analisando as APIs de métricas no cluster e conferindo registros

Vamos analisar, dentro do Kubernetes, como as métricas funcionam, o que essas APIs fazem e onde estão. Ao executar um comando de consulta, como um get (obter) nos recursos APIService filtrando por metrics, observamos o comportamento dessas integrações.

Para confirmar quais APIs de métricas estão registradas no cluster, podemos listar as APIService relacionadas a metrics:

kubectl get apiservices | grep metrics

Vamos pausar e limpar o terminal. Aqui estão nossas métricas: os externos do KEDA e o metrics-server. Eles já estão em execução há algum tempo, junto com o nosso cluster (agrupamento).

Ao rodar o comando acima, a saída evidencia tanto o adapter de external metrics do KEDA quanto o Metrics Server:

kubectl get apiservices | grep metrics
v1beta1.external.metrics.k8s.io        keda-operator-metrics-apiserver   True        83m
v1beta1.metrics.k8s.io                 kube-system/metrics-server        True        128m

Consultando métricas externas do KEDA e interpretando a resposta

Podemos ver, por exemplo, diretamente a partir do HPA, uma métrica sendo coletada. Neste momento, essa métrica está em zero. Esse é o dado bruto do nosso HPA. Aqui está nossa fila do Redis; podemos consultá-la diretamente.

Para inspecionar a métrica externa exposta ao HPA via API de external metrics (no caso, a fila do Redis escalada pelo KEDA), usamos uma chamada raw da API e formatamos com jq:

kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/demo/redis-jobs?labelSelector=scaledobject.keda.sh/name=worker" | jq .

Essa consulta retorna a lista de valores da métrica externa. Note que o valor atual está em zero, confirmando o que comentamos:

{
  "kind": "ExternalMetricValueList",
  "apiVersion": "external.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "metricName": "s0-redis-jobs",
      "metricLabels": null,
      "timestamp": "2024-06-13T19:34:42Z",
      "value": "0"
    }
  ]
}

Observando usage via Metrics Server e interpretando unidades

É importante lembrar: o Kubernetes é um grande conjunto de APIs. Podemos visualizar essas informações de várias formas: YAML, JSON, DESCRIBE, em texto e na forma raw (bruto). Podemos também capturar e manipular a saída com a ferramenta jq. Neste ponto, estamos observando o campo USAGE, isto é, CPU e memória. Precisamos lembrar que os valores de CPU estão em millicores, para não estranharmos os números.

Para ilustrar a consulta do campo usage via Metrics Server (API metrics.k8s.io) com jq, podemos fazer:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes" | jq .items[0].usage

A saída traz o uso de CPU e memória do nó. Observe as unidades: a CPU frequentemente aparece em nanos (n), que podemos relacionar a millicores para interpretação prática:

{
  "cpu": "148753272n",
  "memory": "1074240Ki"
}

Contextualizando a disponibilidade de métricas e introduzindo eBPF

Tudo isso está em execução dentro do nosso cluster (agrupamento): as métricas e os dados já estão disponíveis; precisamos apenas conectá-los aos destinos adequados. Esta foi a métrica que estávamos lendo. No nosso caso, havia itens na fila e, agora, ela está vazia neste exato momento.

Sobre eBPF: o eBPF é um programa que se executa no Linux; ele possibilita coletar informações do sistema de forma eficiente. A forma tradicional de capturar latência de rede ou outros dados dentro do cluster (agrupamento), como a própria latência, é por meio de um sidecar (contêiner auxiliar).

Explicando sidecars no Kubernetes e avaliando seus custos

Retomando as premissas do Kubernetes: dentro de um Pod podemos ter mais de um contêiner, e esses contêineres compartilham rede e armazenamento. Portanto, se injetarmos um contêiner para exportar métricas dentro desse mesmo Pod, por compartilhar a rede, ele consegue medi-la, e nós não precisamos inserir código dentro da nossa aplicação; colocamos essa responsabilidade em um sidecar (contêiner auxiliar).

O problema é que um sidecar (contêiner auxiliar) tem custo: é mais um processo em execução dentro do Pod. Houve, inclusive, uma discussão interessante sobre isso. O Istio utiliza sidecar (contêiner auxiliar) para controlar a rede dos Pods, entre outras funções. Em um cluster (agrupamento) com aplicações muito pequenas, o sidecar (contêiner auxiliar) começa a representar um custo relevante de CPU. Quando comparamos com uma aplicação Java que usa, por exemplo, 2 ou 3 GB de memória, o sidecar (contêiner auxiliar) é relativamente pequeno. Porém, em uma aplicação Go compilada, de pequeno porte, que consome cerca de 30 a 50 MB, o sidecar (contêiner auxiliar) passa a pesar, pois fica muito próximo desse consumo. Assim, podemos acabar praticamente duplicando o tamanho do nosso cluster (agrupamento), ou do nosso deployment, por conta das ferramentas auxiliares.

Destacando eBPF com Cilium/Hubble e complementando com OpenTelemetry

Nesse contexto, o eBPF ganha destaque — ou, mais precisamente, passa a ser reutilizado de forma mais ampla. O eBPF roda no núcleo do sistema, o kernel (núcleo), e pode observar latência e conexões abaixo da camada da aplicação, com custo muito baixo. Existem soluções que oferecem isso; uma delas é o Cilium, que pode utilizar o Hubble. O Hubble é muito eficiente. Durante muito tempo, o Calico foi o grande expoente de redes dentro do Kubernetes; o Cilium conquistou esse espaço e oferece funcionalidades notáveis, como o Hubble, utilizando eBPF para observar a rede entre as aplicações.

Ampliando o conjunto de métricas, temos o OpenTelemetry observando dentro da aplicação — logs (registros), traces (rastros) e métricas, e como os componentes se comunicam — e temos o eBPF observando entre as aplicações, a latência dessa comunicação: o que acontece quando uma chamada sai de uma aplicação e entra em outra, e como se comporta todo o caminho entre esses pontos. Isso é crucial, pois é onde surgem diversos problemas e onde tendemos a estar mais “cegues”.

Consolidando a autonomia com observabilidade e encerrando a aula

Seguindo nessa direção, avançamos em nossa autonomia. Saímos de uma única “sinalização”, expandimos para sinais externos e agora contamos com um pipeline (fluxo de processamento) de sinais: o KEDA recebendo sinais; o OpenTelemetry trazendo o comportamento da aplicação; e o eBPF trazendo o comportamento externo, isto é, como as aplicações se comunicam. Com isso, podemos evoluir para que modelos prevejam a carga e para que as métricas compreendam essa previsão e ajam sobre ela.

A observabilidade é o sentido do agente. Sistemas autônomos, self-healing (autorrecuperação) e dimensionamento preditivo são apenas modelos executando sobre dados. Sem esses dados, sem um pipeline (fluxo de processamento) de telemetria confiável, sem essa base, essa autonomia não vai funcionar — ou funcionará de forma completamente cega —, não atuará e não resolverá o problema que esperamos que resolva. A infraestrutura trafega pelos trilhos que construímos com a observabilidade, com nossos dados e nossas métricas. Isso é vital; não há outro caminho. Essa é a base para construirmos qualquer capacidade de previsão.

Nos vemos na próxima aula. Até então!

AIOps e self-healing em Kubernetes - Implementando escalabilidade orientada a eventos com KEDA

Retomando e propondo escalabilidade orientada a eventos

Olá, eu sou João Brito, sua pessoa anfitriã favorita, e estou de volta. Vamos continuar.

Já discutimos limitações do uso de CPU e memória como sinais de escalabilidade. CPU e memória nem sempre representam os recursos que realmente precisamos observar. A CPU não conta a história completa; quando ela “mente”, a taxa de execução cai. Vamos adotar escalabilidade orientada a eventos.

Contextualizando a fila e integrando KEDA ao HPA

Qual é o cenário? O sinal que importa está fora de vista. Podemos ter uma réplica executando lentamente enquanto há 5 mil jobs na fila. A solução é escalar observando esses jobs, essa fila.

O KEDA possui uma ampla lista de eventos que pode coletar ou receber: Redis, RabbitMQ, Kafka e muitas outras fontes. Esses eventos podem ser enviados para que o KEDA os interprete. E por que olhamos para o HPA? Porque o KEDA interage diretamente com o HPA. Ele cria o próprio HPA, já que é o HPA que interage com os pods.

O KEDA utiliza um CRD (Custom Resource Definition). Instalamos o KEDA, que inclui um Operator (operador). Esse operador observa os objetos em um loop, consulta a fonte de eventos, cria o HPA e interage com o HPA para que o HPA gerencie a quantidade de pods.

Quanto às métricas do API Server (servidor de API), elas podem ser internas — usando o metrics-server que já utilizamos — ou externas. Há também um Admission Webhook (webhook de admissão) para garantir que nenhum YAML malformado chegue e quebre outros componentes. Os CRDs são sensíveis a YAMLs com problemas, então precisamos ter cuidado.

Habilitando scale to zero e avaliando cold start

Existe um objetivo importante: escalar a zero (scale to zero). Em tarefas assíncronas que não ocorrem o tempo todo, quando nada está acontecendo, não faz sentido manter uma máquina — ou, neste caso, um pod — ligado, ocupando espaço no cluster que poderia ser usado para outra coisa ou até removido. O HPA sozinho não consegue deixar uma implantação com zero pods. Com o KEDA, usando CRD, conseguimos. Ele observa a fila vazia e, com zero pods, não há custo. Quando chega uma mensagem, o KEDA interage com o HPA e escala para o primeiro pod. Com outras referências, entendemos quantos pods são necessários conforme a quantidade de eventos, quanto cada worker processa, e a fila cresce de acordo com isso. Se a fila estiver vazia, perfeito. O inverso também funciona: se houver menos demanda, reduz a quantidade de pods e aguarda o próximo pico.

Há, no entanto, um alerta aqui: escalar a zero tem um preço, que é o cold start (inicialização a frio). Já falamos sobre o custo de trabalhar com o passado (backlog). Quando chega a primeira mensagem e não há nenhum pod em execução, se essa mensagem chega ao cluster e se perde — por exemplo, se não houver reintento, espera ou tempo limite —, o primeiro pod ainda vai demorar um tempo para iniciar a aplicação do worker e começar a trabalhar de fato. Portanto, isso não serve para requisições síncronas e sensíveis à latência; é inviável nesses casos. A arquitetura deve ser pensada para permitir que uma parte fique parada; precisa haver fallback (contingência) e diversas estratégias. Para filas assíncronas, como Redis, RabbitMQ e Kafka, alguns segundos de espera são aceitáveis; nesse contexto, scale to zero é excelente. Se a fila está vazia, zero workers: sucesso.

Ajustando o ScaledObject e preparando a instalação

O CRD que o KEDA injeta no cluster é o ScaledObject. No nosso exemplo, cada pod pode processar 50 itens. Definimos um mínimo de 0 réplicas, máximo de 10, um passo de escala de 5 em 5 e cerca de 30 segundos de cooldown (tempo de resfriamento), que é o tempo de espera antes de chegar a zero — o tempo que aguarda antes de desligar o último pod. Isso evita picos de “liga/desliga, liga/desliga”. Se não houver mais demanda, desliga a fila de uma vez. Em uma fila assíncrona que recebe mensagens de tempos em tempos, podemos reduzir um pouco o cooldown para esperar acumular alguns itens na fila e então ligar o pod, ou aumentar um pouco para que, quando a fila estiver terminando, dois ou três itens que chegarem atrasados ainda sejam processados sem oscilações excessivas.

Vamos preparar o terreno para criar esse novo formato com o KEDA. Primeiro, removemos o worker atual e criamos nosso ScaledObject. Antes disso, precisamos instalar o KEDA. Para instalar, vamos utilizar o Helm (gerenciador de pacotes do Kubernetes). Adicionamos o repositório do KEDA e instalamos o KEDA propriamente dito. Aguardamos um momento para que tudo fique online. Em seguida, vemos a saída com um pouco de ASCII art (arte em ASCII): KEDA — Kubernetes Event-driven Autoscaling. Podemos ver os pods do KEDA já em execução: o Webhook (webhook), o Metrics API Server (servidor de métricas da API) e o Operator (operador).

Removendo o HPA e instalando o KEDA com Helm

Para começar, removemos o HPA atual do worker, liberando o caminho para o KEDA assumir o controle do escalonamento via ScaledObject:

kubectl delete hpa worker -n demo

Esse comando remove o HPA associado ao deployment worker no namespace demo.

Em seguida, instalamos o KEDA com o Helm, adicionando o repositório oficial e aguardando a instalação completa:

helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace --version 2.19.0 --wait

Com isso, o KEDA é instalado no namespace keda e o --wait garante que os componentes fiquem prontos antes de prosseguir.

Agora, verificamos se os componentes do KEDA estão rodando (Webhook, Metrics API Server e Operator):

kubectl get pods -n keda

Ao listar os pods, confirmamos que o KEDA está operacional e pronto para criar/gerir HPAs a partir de eventos externos.

Criando e validando o ScaledObject para Redis

Com o worker removido e o KEDA ativo, criamos nosso primeiro ScaledObject para Redis com mínimo 0, máximo 10, intervalo de consulta (polling) a cada 5 segundos e cooldown de 30 segundos. Também configuramos o trigger de Redis com a lista jobs, listLength de 50 por réplica e uma ativação inicial com activationListLength de 5:

cat <<EOF | kubectl apply -f -
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: worker
  namespace: demo
spec:
  scaleTargetRef:
    name: worker
  minReplicaCount: 0
  maxReplicaCount: 10
  pollingInterval: 5
  cooldownPeriod: 30
  triggers:
  - type: redis
    metadata:
      address: redis.demo.svc.cluster.local:6379
      listName: jobs
      listLength: "50"
      activationListLength: "5"
      enableTLS: "false"
EOF

Esse manifesto cria o ScaledObject no namespace demo, instruindo o KEDA a observar o tamanho da lista jobs no Redis e a escalar o deployment worker de acordo.

Logo após criar, validamos se o ScaledObject está presente e pronto:

kubectl get scaledobject worker -n demo

Assim confirmamos o estado do recurso, visualizamos mínimo/máximo e o trigger configurado (Redis). Também poderíamos ver outros tipos de triggers disponíveis (Kafka, RabbitMQ, Prometheus, Datadog, Trace, gRPC externo etc.), inclusive a possibilidade de escrever um trigger customizado.

Em resumo, utilizamos o KEDA para escalabilidade orientada a eventos, integrando-se ao HPA por meio de CRDs, observando filas e outras fontes de eventos, habilitando inclusive scale to zero em cenários assíncronos, com atenção aos impactos de cold start e às validações via Admission Webhook para evitar problemas com YAMLs malformados.

Observando o escalonamento ao esvaziar a fila

Vamos analisar o comportamento de escalonamento partindo do final da fila. A fila estava grande; aceleramos o tempo para que ela terminasse. Em seguida, vimos 10 réplicas. Quando a fila acabou, aguardamos os 30 segundos definidos e, como esperado, voltamos a 0 réplicas. A métrica foi coletada novamente, aplicou-se a regra de 30 segundos e retornou para 0 réplicas. Era o comportamento previsto.

Para observar rapidamente o estado da fila, o número de réplicas do deployment worker e se o ScaledObject está ativo, podemos usar este trecho no terminal (e repeti-lo periodicamente, se desejado):

TS=$(date +%H:%M:%S)
LLEN=$(kubectl exec deploy/redis -n demo -- redis-cli LLEN jobs)
REPL=$(kubectl get deploy worker -n demo --no-headers | awk '{print $2}')
ACTIVE=$(kubectl get scaledobject worker -n demo \
  -o jsonpath='{.status.conditions[?(@.type=="Active")].status}')
printf "[%s] fila=%-5s | replicas=%-4s | ACTIVE=%s\n" "$TS" "$LLEN" "$REPL" "$ACTIVE"
sleep 8

Esse comando imprime timestamp, tamanho da fila, réplicas atuais e o status “Active” do ScaledObject. Você pode colocá-lo dentro de um loop while no shell para acompanhar continuamente.

Consultando o HPA do KEDA e repovoando a fila

Agora, faremos o contrário. Antes, vamos observar o HPA. Temos NULL, que é 0. A cobertura não chega a 0. O mínimo de pods está configurado como 1, mas o sistema está com 0 réplicas. Assim, notamos que o HPA não tem consciência disso. Na nossa lista, temos no mínimo 50 tarefas.

Para consultar o estado atual dos HPAs no namespace demo:

kubectl get hpa -n demo

Dessa forma, vemos o HPA que o KEDA gerencia e percebemos que o “salto” de 0 para 1 é orquestrado pelo KEDA, não pelo HPA clássico.

Vamos fazer o inverso: adicionar tarefas novamente no Redis e observar o crescimento. Limpamos a tela e executamos o comando para popular a lista de tarefas. Em seguida, verificamos a fila. Já temos 4 réplicas, avançando para 8, com a meta de atingir 10. As réplicas estão iniciando; lembramos que saímos de 0. Temos 5 mil tarefas, com 50 por réplica, para facilitar a conta, e já chegamos ao objetivo. Novamente, em cerca de 40 e poucos segundos — quase 1 minuto — alcançamos as 10 réplicas em funcionamento.

Para popular a fila do Redis com 5.000 itens rapidamente:

kubectl exec deploy/redis -n demo -- sh -c \
"for i in \$(seq 1 5000); do echo \"LPUSH jobs job-\$i\"; done | redis-cli --pipe"

Esse comando envia os LPUSH via pipe para o redis-cli, preenchendo a lista jobs de uma vez.

Inspecionando métricas e confirmando o HPA do worker

A partir desse ponto, as 10 réplicas, isto é, os trabalhadores, atuam sobre a nossa fila. Vamos verificar a métrica que o HPA está considerando agora. Vamos encerrar o loop for e exibir uma réplica. Aqui está: o KEDA, nesse momento, com 10 réplicas. Observamos o tamanho da fila e a métrica. Fizemos um describe no ScaledObject, e o nome da métrica utilizada é jobs de Redis, para facilitar a identificação. Já passamos por isso; nos slides mostramos o terminal — o slide aceita tudo — e está documentado lá.

Para checar especificamente o HPA criado pelo KEDA para o worker:

kubectl get hpa keda-hpa-worker -n demo

Assim, confirmamos as metas e o status do HPA que o KEDA gerou.

E para inspecionar as métricas externas associadas ao ScaledObject (incluindo o nome “jobs de Redis”):

kubectl describe scaledobject worker -n demo | grep -A 1 "External Metric Names"

Esse detalhe evidencia quais métricas externas o HPA está consumindo por meio do KEDA.

Reforçando o scale from zero e definindo limites

Quando temos 1 réplica, a fila desce até zero, e retornamos a 0 réplicas. O HPA, sozinho, não faz isso. Ao rodar em conjunto com o KEDA, é o KEDA que assume o passo de zero para um. É o KEDA que provoca o cold start (inicialização a frio). Observamos o pico de réplicas: em menos de 1 minuto, todas as réplicas estavam em funcionamento, atingindo o topo.

Registramos o horário (por volta das nove e vinte e nove). O sistema recebe o disparo, fica ativo e, de 0 réplicas, passa a ativo — scale from zero (escalar a partir de zero). Surge a primeira réplica e, então, progride: 4, 8, 10, até o limite configurado. É importante tomar cuidado para não deixar isso ilimitado. Em caso de problema em cascata, se ocorrer um flood (enxurrada) na fila por diversos motivos — da aplicação ou não —, é possível saturar o seu cluster (agrupamento) inteiro se não houver limites claros. A fila pode crescer sem limites; normalmente, a fila não terá limites além do que o Redis suportar. Como consequência, você aumentará os trabalhadores e poderá saturar o cluster de trabalhadores, levando à indisponibilidade de componentes.

O ponto principal aqui é: em menos de um minuto, o trabalhador passou de 0 para 10 e recebeu o sinal correto. Assim, ele pode permanecer em zero pelo tempo necessário, e o KEDA receberá o evento do Redis e ativará o trabalhador.

Comentando métricas, custos e casos de uso do KEDA

Uma observação adicional, que já vimos no terminal: há um detalhe — um “easter egg” — relacionado à unidade em millicores (milicores). Nosso objetivo, o list length, é 50. Tenha atenção para não interpretar um valor incorreto, como se fosse um bilhão. E temos nossa external metric (métrica externa) aqui: from zero, o nome que atribuímos à nossa métrica.

Em tom de brincadeira, comentamos que a “matriz de decisão nunca vá ao HPA clássico”. O HPA clássico baseia-se em CPU e memória. Ao movermos para o KEDA, pensamos no comportamento e no desperdício mínimo de CPU, que nunca é zero: se deixarmos apenas o HPA rodando, haverá sempre consumo residual e ocorrerá cold start (inicialização a frio). Entre os benefícios do KEDA, podemos alcançar custo zero. Como assim? O KEDA continuará rodando — os três pods do KEDA estarão ativos, incluindo o operator (operador). Se compararmos apenas um HPA e um pod versus esses três, há algum custo. Porém, em um cluster (agrupamento) com muitos pods, muitos deploys (implantações), filas, bases de dados etc., três pods tornam-se irrelevantes frente ao benefício que trazem. Se considerarmos apenas “mais um pod ocioso”, aí sim, haverá custo — mas, no cenário amplo, compensa.

Em tráfego HTTP estável, podemos utilizar o HPA e obter benefícios. Já com o KEDA, lidamos bem com tarefas em batch (lote), filas assíncronas, pipelines (fluxos) de dados, casos de machine learning (aprendizado de máquina), IA e workflows (fluxos de trabalho). Há picos específicos em cada parte desse processo. Quando temos um pipeline (fluxo) de dados, injetamos por um lado, trabalhadores consomem e encaminham ao próximo passo, e assim sucessivamente. Vamos escalonando apenas o trecho necessário. O KEDA auxilia nisso. No entanto, não é uma bala de prata. Se a sua carga de trabalho for previsível em CPU, podemos utilizar o HPA com tranquilidade — sempre ponderando.

Concluindo e apontando para escala preditiva

Em nossa transição evolutiva, saímos do reativo manual do HPA para o reativo externo com o KEDA. Continua sendo reativo, mas mais inteligente, pois pode obter métricas de outras fontes. Ao combinar HPA e KEDA, pode parecer que o KEDA é muito mais inteligente, mas não é magia: é apenas event-driven (orientado a eventos), assim como o HPA. A diferença é que o KEDA se conecta a mais lugares, trazendo mais contexto.

Para onde vamos? Para a escala preditiva. Antes de escalar, queremos antecipar o problema. Trocamos o problema do “quando o sistema deve agir?”. Saímos do dilema do sinal ruim: o KEDA já resolve essa parte. Estamos empilhando os tópicos necessários: Kubernetes, HPA, KEDA, e seguimos nesse caminho. Mudemos nosso olhar. Não esqueçamos: o KEDA não compete com o HPA; o KEDA trabalha junto. Ele funciona quase como um adaptador universal, um adaptador de sinais para o seu HPA.

Nos vemos na próxima, seguimos por aqui. Até então!

Sobre o curso Kubernetes: AIOps e self‑healing com observabilidade avançada

O curso Kubernetes: AIOps e self‑healing com observabilidade avançada possui 555 minutos de vídeos, em um total de 62 atividades. Gostou? Conheça nossos outros cursos de Containers em DevOps, ou leia nossos artigos de DevOps.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Containers acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas