Avançar para o conteúdo principal
BlogContentores (Kubernetes, Docker)Escala de Kubernetes a Zero (E Voltar)

Escala de Kubernetes a Zero (E Voltar)

Escalada imagem Kubernetes

Este posto faz parte da nossa série de escalas Kubernetes. Registe-se para assistir ao vivo ou aceder à gravação, e ver os nossos outros posts nesta série:

A redução dos custos das infra-estruturas resume-se a desligar os recursos quando estes não estão a ser utilizados. No entanto, o desafio é descobrir como ligar estes recursos automaticamente quando necessário. Vamos percorrer os passos necessários para implantar um aglomerado Kubernetes utilizando Linode Kubernetes Engine (LKE) e utilizar o Kubernetes Events-Driven Autoscaler (KEDA) para reduzir a zero e vice-versa.

Porquê Escala a Zero

Imaginemos que está a executar uma aplicação razoavelmente intensiva de recursos em Kubernetes e que só é necessária durante as horas de trabalho.

Pode querer desligá-lo quando as pessoas saem do escritório e voltar ao início do dia.

Escala de Kubernetes a zero para cargas de trabalho de desenvolvimento que só são necessárias durante as horas de trabalho, versus cargas de trabalho de produção que precisam de funcionar 24/7.
Talvez queira desligar o seu ambiente de desenvolvimento se ninguém o estiver a usar!

Embora se pudesse utilizar um CronJob para aumentar e diminuir a escala, esta solução é uma solução de paragem que só pode funcionar com um horário pré-estabelecido.

O que acontece durante o fim-de-semana? E os feriados públicos? Ou quando a equipa está doente?

Em vez de gerar uma lista sempre crescente de regras, pode aumentar a sua carga de trabalho com base no tráfego. Quando o tráfego aumenta, é possível escalar as réplicas. Se não houver tráfego, pode desligar a aplicação. Se a aplicação for desligada e houver um novo pedido de entrada, Kubernetes lançará pelo menos uma única réplica para tratar do tráfego.

Diagrama Kubernetes em escala - escalar e utilizar apenas recursos quando há tráfego activo.
Aplicações de escalonamento a zero para ajudar a poupar recursos.

A seguir, vamos falar sobre como fazê-lo:

  • intercepte todo o tráfego para as suas aplicações;
  • monitorizar o tráfego; e
  • configurar o autoscaler para ajustar o número de réplicas ou desligar as aplicações.

Se preferir ler o código para este tutorial, pode fazê-lo no GitHub do LearnK8s.

Criação de um aglomerado

Comecemos pela criação de um aglomerado Kubernetes.

Os seguintes comandos podem ser utilizados para criar o cluster e guardar o ficheiro kubeconfig.

bash
$ linode-cli lke cluster-create \
 --label cluster-manager \
 --region eu-west \
 --k8s_version 1.23
 
$ linode-cli lke kubeconfig-view "insert cluster id here" --text | tail +2 | base64 -d > kubeconfig

É possível verificar se a instalação é bem sucedida:

bash
$ kubectl get pods -A --kubeconfig=kubeconfig

Exportar o ficheiro kubeconfig com uma variável de ambiente é normalmente mais conveniente.

Pode fazê-lo com:

bash
$ export KUBECONFIG=${PWD}/kubeconfig
$ kubectl get pods

Agora vamos implantar uma aplicação.

Implantar uma Aplicação

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: podinfo
spec:
 selector:
   matchLabels:
     app: podinfo
 template:
   metadata:
     labels:
       app: podinfo
   spec:
     containers:
     - name: podinfo
       image: stefanprodan/podinfo
       ports:
       - containerPort: 9898
---
apiVersion: v1
kind: Service
metadata:
 name: podinfo
spec:
 ports:
   - port: 80
     targetPort: 9898
 selector:
   app: podinfo

Pode submeter o ficheiro YAML com:

terminal|command=1|title=bash
$ kubectl apply -f 1-deployment.yaml

E pode visitar a aplicação com:

Abra o seu navegador ao localhost:8080.

bash
$ kubectl port-forward svc/podinfo 8080:80

Nesta altura, deverá ver a aplicação.

Imagem da aplicação podinfo no browser.

A seguir, vamos instalar o KEDA - o autoscaler.

KEDA - o Kubernetes Autoscaler orientado por eventos

A Kubernetes oferece o Pod Autoscaler Horizontal (HPA) como controlador para aumentar e diminuir as réplicas de forma dinâmica.

Infelizmente, a HPA tem alguns inconvenientes:

  1. Não funciona fora da caixa - é necessário instalar um Metrics Server para agregar e expor as métricas.
  2. Não escala a zero réplicas.
  3. Escala réplicas baseadas em métricas, e não intercepta tráfego HTTP.

Felizmente, não tem de usar o autoscaler oficial, mas pode usar a KEDA em vez disso.

KEDA é um autoscaler feito de três componentes:

  1. Um Escalador
  2. Um Adaptador de Métrica
  3. Um Controlador
Diagrama de arquitectura KEDA que exibe os componentes.
Arquitectura KEDA

Os escaladores são como adaptadores que podem recolher métricas de bases de dados, corretores de mensagens, sistemas de telemetria, etc.

Por exemplo, o HTTP Scaler é um adaptador que pode interceptar e recolher tráfego HTTP.

Pode encontrar aqui um exemplo de um escalador usando o RabbitMQ.

O Adaptador de Métricas é responsável por expor as métricas recolhidas pelos escaladores num formato que o gasoduto de métricas Kubernetes pode consumir.

E finalmente, o controlador cola todos os componentes juntos:

  • Recolhe as métricas utilizando o adaptador e expõe-nas às métricas API.
  • Regista e gere as Definições de Recursos Personalizados (CRD) específicas da KEDA - ou seja, ScaledObject, TriggerAuthentication, etc.
  • Ele cria e gere o Pod Autoscaler Horizontal em seu nome.

Essa é a teoria, mas vamos ver como funciona na prática.

Uma forma mais rápida de instalar o controlador é utilizar o Helm.

Pode encontrar as instruções de instalação no site oficial do Helm.

bash
$ helm repo add kedacore https://kedacore.github.io/charts
$ helm install keda kedacore/keda

A KEDA não vem com um escaler HTTP por defeito, pelo que terá de o instalar separadamente:

bash
$ helm install http-add-on kedacore/keda-add-ons-http

Neste momento, está pronto para escalar a aplicação.

Definição de uma Estratégia de Autoscaling

O suplemento KEDA HTTP expõe um CRD onde pode descrever como a sua aplicação deve ser escalonada.

Vejamos um exemplo:

yaml
kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
   name: podinfo
spec:
   host: example.com
   targetPendingRequests: 100
   scaleTargetRef:
       deployment: podinfo
       service: podinfo
       port: 80
   replicas:
       min: 0
       max: 10

Este ficheiro instrui os interceptores a encaminhar pedidos por exemplo.com para o serviço podinfo.

KEDA autoscaling strategy forKubernetes. O tráfego de entrada chega ao KEDA HTTP Interceptor antes de chegar ao servidor Kubernetes API .
KEDA e o interceptor HTTP.

Também inclui o nome do destacamento que deve ser escalonado - neste caso, podinfo.

Vamos submeter o YAML ao agrupamento com:

bash
$ kubectl apply -f scaled-object.yaml

Assim que submeter a definição, a cápsula é apagada!

Mas porquê?

Após a criação de um HTTPScaledObject, a KEDA escala imediatamente a implantação a zero, uma vez que não há tráfego.

Deve enviar pedidos HTTP para o aplicativo para o dimensionar.

Vamos testar isto, ligando-nos ao serviço e emitindo um pedido.

bash
$ kubectl port-forward svc/podinfo 8080:80

O comando está suspenso!

Faz sentido; não há cápsulas para servir o pedido.

Mas porque é que a Kubernetes não está a escalar o destacamento para 1?

Teste do Interceptor KEDA

Um Serviço Kubernetes chamado keda-add-ons-http-interceptor-proxy foi criado quando se utilizou o Helm para instalar o add-on.

Para que o autoscaling funcione adequadamente, o tráfego HTTP deve passar primeiro por esse serviço.
Pode usar kubectl port-forward para o testar:

shell
$ kubectl port-forward svc/keda-add-ons-http-interceptor-proxy 8080:8080

Desta vez, não pode visitar o URL no seu navegador.

Um único interceptor HTTP KEDA pode lidar com várias implantações.

Então como é que sabe para onde encaminhar o tráfego?

yaml
kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
   name: podinfo
spec:
   host: example.com
   targetPendingRequests: 100
   scaleTargetRef:
       deployment: podinfo
       service: podinfo
       port: 80
   replicas:
       min: 0
       max: 10

O HTTPScaledObject tem um campo hospedeiro que é utilizado precisamente para isso.

Neste exemplo, finja que o pedido vem de exemplo.com.

Pode fazê-lo definindo o cabeçalho Host:

bash
$ curl localhost:8080 -H 'Host: example.com'

Receberá uma resposta, embora com um ligeiro atraso.

Se inspeccionar as cápsulas, notará que o destacamento foi escalado para uma única réplica:

bash
$ kubectl get pods

Então o que acabou de acontecer?

Quando encaminha o tráfego para o serviço da KEDA, o interceptor mantém um registo do número de pedidos HTTP pendentes que ainda não obtiveram resposta.

O escalador da KEDA verifica periodicamente o tamanho da fila do interceptor e armazena as métricas.

O controlador KEDA monitoriza a métrica e aumenta ou diminui o número de réplicas, conforme necessário. Neste caso, está pendente um único pedido - suficiente para o controlador KEDA escalar a implantação a uma única réplica.

Pode obter o estado de fila de pedidos HTTP pendentes de um interceptor individual:

bash
$ kubectl proxy &
$ curl -L localhost:8001/api/v1/namespaces/default/services/keda-add-ons-http-interceptor-admin:9090/proxy/queue
{"example.com":0,"localhost:8080":0}

Devido a esta concepção, deve ter cuidado na forma como encaminha o tráfego para as suas aplicações.

A KEDA só pode escalar o tráfego se este puder ser interceptado.

Se tiver um controlador de entrada existente e desejar utilizá-lo para encaminhar o tráfego para a sua aplicação, terá de alterar o manifesto de entrada para encaminhar o tráfego para o serviço HTTP add-on.

Vejamos um exemplo.

Combinando o suplemento HTTP KEDA com o Ingresso

Pode instalar o nginx-ingress controller with Helm:

bash
$ helm upgrade --install ingress-nginx ingress-nginx \
 --repo https://kubernetes.github.io/ingress-nginx \
 --namespace ingress-nginx --create-namespace

Vamos escrever um manifesto de entrada para encaminhar o tráfego para o podinfo:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: podinfo
spec:
 ingressClassName: nginx
 rules:
 - host: example.com
   http:
     paths:
     - path: /
       pathType: Prefix
       backend:
         service:
           name: keda-add-ons-http-interceptor-proxy # <- this
           port:
             number: 8080

Pode recuperar o IP do equilibrador de carga com:

bash
LB_IP=$(kubectl get services -l "app.kubernetes.io/component=controller" -o jsonpath="{.items[0].status.loadBalancer.ingress
[0].ip}" -n ingress-nginx)

Pode finalmente fazer um pedido à aplicação com:

bash
curl $LB_IP -H "Host: example.com"

Funcionou!

Se esperar o tempo suficiente, notará que o desdobramento acabará por ser reduzido a zero.

Como é que isto se compara ao Serverless em Kubernetes?

Existem várias diferenças significativas entre esta configuração e uma estrutura sem servidor em Kubernetes como o OpenFaaS:

  1. Com a KEDA, não há necessidade de rearquitectura ou de utilizar um SDK para implantar a aplicação.
  2. Os quadros sem servidores tratam do encaminhamento e da prestação de serviços de pedidos. Escreve apenas a lógica.
  3. Com a KEDA, os destacamentos são contentores regulares. Com uma estrutura sem servidor, isso nem sempre é verdade.

Quer ver esta escalada em acção? Registe-se na nossa série de webinar Kubernetes de escalonamento.


Comentários (3)

  1. Author Photo

    Very nice tutorial. In the case without the nginx ingress, can you explain how to access from the outside, instead of the localhost? I tried to use a NodePort service, but the port gets closed when the Interceptor is installed. The Interceptor proxy is a ClusterIP service. How can we access it from the outside? Is there any sort of kubectl port forwarding instruction?

    • Maddie Presland

      Hi Rui! I forwarded your question to Daniele and here is his response:

      It should work with NodePort, but you have to set the right header (i.e. Host: example.com ) when you make the request. There is no way for the interceptor to decide where the traffic should go without that.

  2. Author Photo

    Muito bom o conteudo!!!

Deixe uma resposta

O seu endereço de correio electrónico não será publicado. Os campos obrigatórios estão marcados com *