Vai al contenuto principale
BlogContenitori (Kubernetes, Docker)Scalare Kubernetes a zero (e ritorno)

Scalabilità di Kubernetes a zero (e ritorno)

Scalare l'immagine di Kubernetes

Questo post fa parte della nostra serie Scaling Kubernetes. Registratevi per assistere dal vivo o accedere alla registrazione, e date un'occhiata agli altri post di questa serie:

La riduzione dei costi dell'infrastruttura si riduce allo spegnimento delle risorse quando non vengono utilizzate. Tuttavia, la sfida consiste nel capire come attivare automaticamente queste risorse quando è necessario. Esaminiamo i passaggi necessari per implementare un cluster Kubernetes utilizzando Linode Kubernetes Engine (LKE) e utilizzare Kubernetes Events-Driven Autoscaler (KEDA) per scalare a zero e viceversa.

Perché scalare a zero

Immaginiamo di eseguire un'applicazione ad alta intensità di risorse su Kubernetes e che sia necessaria solo durante le ore di lavoro.

Si consiglia di spegnerlo quando le persone lasciano l'ufficio e di riaccenderlo quando iniziano la giornata.

Scalare Kubernetes a zero per i carichi di lavoro di sviluppo che sono necessari solo durante l'orario di lavoro, rispetto ai carichi di lavoro di produzione che devono essere eseguiti 24 ore su 24, 7 giorni su 7.
Si consiglia di spegnere l'ambiente di sviluppo se non viene utilizzato da nessuno!

Sebbene si possa usare un CronJob per scalare l'istanza, questa soluzione è un ripiego che può essere eseguito solo in base a una pianificazione predefinita.

Cosa succede durante il fine settimana? E nei giorni festivi? O quando il team è in malattia?

Invece di generare un elenco sempre crescente di regole, è possibile scalare i carichi di lavoro in base al traffico. Quando il traffico aumenta, è possibile scalare le repliche. Se non c'è traffico, si può spegnere l'applicazione. Se l'applicazione è spenta e c'è una nuova richiesta in arrivo, Kubernetes avvierà almeno una singola replica per gestire il traffico.

Diagramma Kubernetes in scala - scalare e utilizzare le risorse solo quando c'è traffico attivo.
Scalare le app a zero per risparmiare risorse.

Parliamo poi di come farlo:

  • intercettare tutto il traffico verso le vostre applicazioni;
  • monitorare il traffico; e
  • impostare l'autoscaler per regolare il numero di repliche o disattivare le applicazioni.

Se preferite leggere il codice di questa esercitazione, potete farlo su GitHub di LearnK8s.

Creazione di un cluster

Iniziamo con la creazione di un cluster Kubernetes.

I seguenti comandi possono essere usati per creare il cluster e salvare il file 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

È possibile verificare che l'installazione sia avvenuta con successo con:

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

L'esportazione del file kubeconfig con una variabile d'ambiente è solitamente più conveniente.

È possibile farlo con:

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

Ora distribuiamo un'applicazione.

Distribuzione di un'applicazione

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

È possibile inviare il file YAML con:

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

E si può visitare l'app con:

Aprite il browser su localhost:8080.

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

A questo punto, si dovrebbe vedere l'applicazione.

Schermata dell'applicazione podinfo nel browser.

Quindi, installiamo KEDA - l'autoscaler.

KEDA - l'autoscaler Kubernetes guidato dagli eventi

Kubernetes offre l'Horizontal Pod Autoscaler (HPA) come controller per aumentare e ridurre le repliche in modo dinamico.

Purtroppo, l'HPA presenta alcuni inconvenienti:

  1. Non funziona in modo immediato: è necessario installare un server di metriche per aggregare ed esporre le metriche.
  2. Non scala a zero repliche.
  3. Scala le repliche in base alle metriche e non intercetta il traffico HTTP.

Fortunatamente, non è necessario utilizzare l'autoscaler ufficiale, ma è possibile utilizzare KEDA.

KEDA è un autoscaler composto da tre componenti:

  1. Uno scalatore
  2. Un adattatore di metriche
  3. Un controllore
Diagramma dell'architettura KEDA che visualizza i componenti.
Architettura KEDA

Gli scaler sono come adattatori che possono raccogliere metriche da database, message broker, sistemi di telemetria, ecc.

Ad esempio, HTTP Scaler è un adattatore in grado di intercettare e raccogliere il traffico HTTP.

Un esempio di scaler che utilizza RabbitMQ è disponibile qui.

Il Metrics Adapter è responsabile dell'esposizione delle metriche raccolte dagli scaler in un formato che la pipeline di metriche di Kubernetes può consumare.

Infine, il controllore incolla tutti i componenti tra loro:

  • Raccoglie le metriche utilizzando l'adattatore e le espone all'API delle metriche.
  • Registra e gestisce le Custom Resource Definitions (CRD) specifiche di KEDA, ovvero ScaledObject, TriggerAuthentication, ecc.
  • Crea e gestisce l'Autoscaler Pod orizzontale per conto dell'utente.

Questa è la teoria, ma vediamo come funziona in pratica.

Un modo più rapido per installare il controller è usare Helm.

Le istruzioni per l'installazione sono disponibili sul sito ufficiale di Helm.

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

KEDA non viene fornito con uno scaler HTTP di default, quindi è necessario installarlo separatamente:

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

A questo punto, siete pronti a scalare l'applicazione.

Definizione di una strategia di autoscaling

Il componente aggiuntivo KEDA HTTP espone un CRD in cui è possibile descrivere il modo in cui l'applicazione deve essere scalata.

Vediamo un esempio:

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

Questo file istruisce gli intercettori a inoltrare le richieste di example.com al servizio podinfo.

Strategia di autoscaling KEDA per Kubernetes. Il traffico in entrata raggiunge l'intercettore HTTP di KEDA prima di raggiungere il server API di Kubernetes.
KEDA e l'intercettore HTTP.

Include anche il nome del deployment che deve essere scalato, in questo caso podinfo.

Inviamo lo YAML al cluster con:

bash
$ kubectl apply -f scaled-object.yaml

Non appena si invia la definizione, il pod viene cancellato!

Ma perché?

Dopo la creazione di un HTTPScaledObject, KEDA scala immediatamente la distribuzione a zero, poiché non c'è traffico.

È necessario inviare richieste HTTP all'applicazione per scalarla.

Verifichiamo questo aspetto collegandoci al servizio e inviando una richiesta.

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

Il comando si blocca!

Ha senso: non ci sono pod per servire la richiesta.

Ma perché Kubernetes non scala il deployment a 1?

Test dell'intercettatore KEDA

Un servizio Kubernetes chiamato keda-add-ons-http-interceptor-proxy è stato creato quando si è usato Helm per installare il componente aggiuntivo.

Affinché l'autoscaling funzioni correttamente, il traffico HTTP deve passare prima attraverso quel servizio.
È possibile utilizzare kubectl port-forward per testarlo:

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

Questa volta, non è possibile visitare l'URL nel browser.

Un singolo intercettore HTTP KEDA può gestire più distribuzioni.

Come fa a sapere dove instradare il traffico?

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

L'oggetto HTTPScaledObject ha un campo host che viene utilizzato proprio per questo.

In questo esempio, si finge che la richiesta provenga da example.com.

È possibile farlo impostando l'intestazione Host:

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

Riceverete una risposta, anche se con un leggero ritardo.

Se si ispezionano i pod, si noterà che l'installazione è stata scalata a una singola replica:

bash
$ kubectl get pods

Che cosa è successo?

Quando si instrada il traffico verso il servizio KEDA, l'intercettore tiene traccia del numero di richieste HTTP in sospeso che non hanno ancora ricevuto risposta.

Lo scaler KEDA controlla periodicamente la dimensione della coda dell'intercettore e memorizza le metriche.

Il controller KEDA monitora le metriche e aumenta o diminuisce il numero di repliche in base alle necessità. In questo caso, è in corso una singola richiesta, sufficiente al controller KEDA per scalare il deployment a una singola replica.

È possibile recuperare lo stato della coda di richieste HTTP in sospeso di un singolo intercettore con:

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}

A causa di questo design, è necessario prestare attenzione al modo in cui si instrada il traffico verso le app.

KEDA può scalare il traffico solo se può essere intercettato.

Se si dispone di un controller di ingress esistente e si desidera utilizzarlo per inoltrare il traffico alla propria applicazione, è necessario modificare il manifest di ingress per inoltrare il traffico al servizio aggiuntivo HTTP.

Vediamo un esempio.

Combinazione dell'add-on HTTP KEDA con Ingress

È possibile installare il controller nginx-ingress con Helm:

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

Scriviamo un manifest di ingress per instradare il traffico verso 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

È possibile recuperare l'IP del bilanciatore di carico con:

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

È infine possibile effettuare una richiesta all'app con:

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

Ha funzionato!

Se si attende abbastanza a lungo, si noterà che il dispiegamento finirà per azzerarsi.

Come si confronta con Serverless su Kubernetes?

Ci sono diverse differenze significative tra questa configurazione e un framework serverless su Kubernetes come OpenFaaS:

  1. Con KEDA, non è necessario riarchitettare o utilizzare un SDK per distribuire l'applicazione.
  2. I framework serverless si occupano di instradare e servire le richieste. Voi scrivete solo la logica.
  3. Con KEDA, le distribuzioni sono normali contenitori. Con un framework serverless, questo non è sempre vero.

Volete vedere questo scaling in azione? Registratevi alla serie di webinar Scaling Kubernetes.


Commenti (5)

  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!!!

  3. Author Photo

    Where does NodeBalancer show up in this configuration? Does LKE take over that job?

    • tlambert

      Hi Lee – The NodeBalancer is created during the installation of the nginx-ingress controller. For a more detailed explanation of this process you can check out our guide titled Deploying NGINX Ingress on Linode Kubernetes Engine.

      Once the NodeBalancer is provisioned it is controlled via LKE. We don’t recommend configuring the settings of your LKE NodeBalancers through the Cloud Manager.

Lascia una risposta

Il vostro indirizzo e-mail non sarà pubblicato. I campi obbligatori sono contrassegnati da *