Auto-scaling delle applicazioni Kubernetes con Prometheus e KEDA

Auto-scaling delle applicazioni Kubernetes con Prometheus e KEDABalloon Man di Cimuanos

La scalabilità è un requisito fondamentale per le applicazioni cloud. Con Kubernetes, scalare un'applicazione è semplice quanto aumentare il numero di repliche per il deployment corrispondente o ReplicaSet — ma è un processo manuale.

Kubernetes consente di scalare automaticamente le applicazioni (cioè i Pod in un deployment o ReplicaSet) in modo dichiarativo utilizzando la specifica Horizontal Pod Autoscaler. Di default, il criterio per lo scaling automatico sono le metriche di utilizzo della CPU (metriche delle risorse), ma è possibile integrare metriche personalizzate e metriche fornite dall'esterno.

Team Kubernetes aaS di Mail.ru ha tradotto un articolo su come utilizzare metriche esterne per il auto-scaling dell'applicazione Kubernetes. Per illustrare il funzionamento, l'autore utilizza metriche di richieste HTTP, raccolte tramite Prometheus.

Invece della scalabilità automatica orizzontale dei pod, viene utilizzato Kubernetes Event Driven Autoscaling (KEDA) — un operatore Kubernetes open source. Si integra inizialmente con il Horizontal Pod Autoscaler per garantire una scalabilità fluida (incluso fino/a zero) per carichi di lavoro attivati da eventi. Il codice è disponibile su GitHub.

Panoramica del sistema

Auto-scaling delle applicazioni Kubernetes con Prometheus e KEDA

Nello schema, una breve descrizione di come funziona tutto:

  1. L'applicazione fornisce metriche sul numero di richieste HTTP nel formato Prometheus.
  2. Prometheus è configurato per raccogliere queste metriche.
  3. Il scalatore Prometheus in KEDA è impostato per scalare automaticamente l'applicazione in base al numero di richieste HTTP.

Ora spiegherò in dettaglio ogni elemento.

KEDA e Prometheus

Prometheus è un set di strumenti open source per il monitoraggio e la notifica dei sistemi, parte Cloud Native Computing Foundation. Raccoglie metriche da diverse fonti e le memorizza come dati temporali. Per la visualizzazione dei dati è possibile utilizzare Grafana o altri strumenti di visualizzazione che lavorano con l'API di Kubernetes.

KEDA supporta il concetto di scaler: funge da ponte tra KEDA e un sistema esterno. L'implementazione dello scaler è specifica per ogni sistema di destinazione e ne estrae i dati. KEDA utilizza poi queste informazioni per gestire la scalabilità automatica.

Gli scaler supportano diverse fonti di dati, come Kafka, Redis, Prometheus. Questo significa che KEDA può essere utilizzato per la scalabilità automatica dei deployment Kubernetes, utilizzando come criteri le metriche di Prometheus.

Applicazione di test

L'applicazione di test in Golang fornisce accesso HTTP e svolge due funzioni importanti:

  1. Utilizza la libreria client Prometheus Go per strumentare l'app e fornire la metrica http_requests, che contiene un contatore degli accessi. Il punto finale dal quale le metriche di Prometheus sono accessibili si trova all'URI /metrics.
    var httpRequestsCounter = promauto.NewCounter(prometheus.CounterOpts{
           Name: "http_requests",
           Help: "numero di richieste http",
       })
    
  2. In risposta a una richiesta GET l'applicazione incrementa il valore della chiave (access_count) in Redis. Questo è un modo semplice per eseguire l'operazione come parte del gestore HTTP e controllare anche le metriche di Prometheus. Il valore della metrica dovrebbe essere lo stesso del valore access_count in Redis.
    func main() {
           http.Handle("/metrics", promhttp.Handler())
           http.HandleFunc("/test", func(w http.ResponseWriter, r 
    *http.Request) {
               defer httpRequestsCounter.Inc()
               count, err := client.Incr(redisCounterName).Result()
               if err != nil {
                   fmt.Println("Impossibile incrementare il contatore Redis", err)
                   os.Exit(1)
               }
               resp := "Accesso il " + time.Now().String() + "
    Conteggio accessi " + strconv.Itoa(int(count))
               w.Write([]byte(resp))
           })
           http.ListenAndServe(":8080", nil)
       }
    

L'applicazione viene distribuita in Kubernetes tramite Deployment. Viene anche creato un servizio ClusterIP, che consente al server Prometheus di ricevere le metriche dell'applicazione.

Ecco manifesto di distribuzione per l'applicazione.

Server Prometheus

Il manifesto di distribuzione di Prometheus è composto da:

  • ConfigMap — per la trasmissione della configurazione di Prometheus;
  • Deployment — per la distribuzione di Prometheus nel cluster Kubernetes;
  • ClusterIP — servizio per l'accesso all'interfaccia utente di Prometheus;
  • ClusterRole, ClusterRoleBinding e ServiceAccount — per il funzionamento dell'auto-scoperta dei servizi in Kubernetes (Auto-discovery).

Ecco manifesto per l'avvio di Prometheus.

KEDA Prometheus ScaledObject

Lo scaler funge da ponte tra KEDA e un sistema esterno da cui è necessario raccogliere metriche. ScaledObject — risorsa configurabile, deve essere distribuita per sincronizzare la distribuzione con la sorgente di eventi, in questo caso con Prometheus.

ScaledObject contiene informazioni sullo scaling del deployment, metadati sull'origine dell'evento (ad esempio, segreti per la connessione, nome della coda), intervallo di polling, periodo di raffreddamento e altri dati. Porta alla risorsa di auto-scaling corrispondente (definizione HPA) per scalare il deployment.

Quando l'oggetto ScaledObject viene rimosso, la relativa definizione HPA viene cancellata.

Ecco la definizione ScaledObject per il nostro esempio, in cui viene utilizzato lo scaler Prometheus:

apiVersion: keda.k8s.io/v1alpha1
kind: ScaledObject
metadata:
 name: prometheus-scaledobject
 namespace: default
 labels:
   deploymentName: go-prom-app
spec:
 scaleTargetRef:
   deploymentName: go-prom-app
 pollingInterval: 15
 cooldownPeriod:  30
 minReplicaCount: 1
 maxReplicaCount: 10
 triggers:
 - type: prometheus
   metadata:
     serverAddress: 
http://prometheus-service.default.svc.cluster.local:9090
     metricName: access_frequency
     threshold: '3'
     query: sum(rate(http_requests[2m]))

Tenere a mente i seguenti punti:

  1. Indica Deployment chiamato go-prom-app.
  2. Il tipo di trigger è Prometheus. L'indirizzo del server Prometheus è menzionato insieme al nome della metrica, al valore soglia e alla query PromQL, che verrà utilizzata. La query PromQL è sum(rate(http_requests[2m])).
  3. Secondo pollingInterval, KEDA interroga l'obiettivo da Prometheus ogni quindici secondi. È supportato un minimo di un pod (minReplicaCount), mentre il numero massimo di pod non supera maxReplicaCount (in questo esempio - dieci).

Puoi impostare minReplicaCount a zero. In questo caso, KEDA attiverà il deployment da zero a uno e poi fornirà HPA per ulteriore scalabilità automatica. È possibile anche l'ordine inverso, quindi scalare da uno a zero. Nell'esempio non abbiamo scelto zero, poiché si tratta di un servizio HTTP, non di un sistema on-demand.

La magia dell'autoscaling

Il valore di soglia viene utilizzato come attivatore per scalare il deployment. Nel nostro esempio, la richiesta PromQL sum(rate(http_requests[2m])) restituisce il valore aggregato della velocità delle richieste HTTP (numero di richieste al secondo), misurato nelle ultime due minuti.

Poiché il valore di soglia è tre, ci sarà un pod finché il valore è sum(rate(http_requests[2m])) inferiore a tre. Se il valore aumenta, viene aggiunto un pod ogni volta che sum(rate(http_requests[2m])) aumenta di tre. Ad esempio, se il valore va da 12 a 14, il numero di pod sarà quattro.

Adesso proviamo a configurarlo!

Configurazione preliminare

Tutto ciò di cui hai bisogno è un cluster Kubernetes e un'utilità configurata kubectl. In questo esempio si utilizza il cluster minikube, ma puoi prendere qualsiasi altro. Per installare il cluster, c'è la guida.

Installa l'ultima versione su Mac:

curl -Lo minikube 
https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64 
&& chmod +x minikube
sudo mkdir -p /usr/local/bin/
sudo install minikube /usr/local/bin/

Installa kubectl, per accedere al cluster Kubernetes.

Installare l'ultima versione su Mac:

curl -LO 
"https://storage.googleapis.com/kubernetes-release/release/$(curl -s
https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
kubectl version

Installazione di KEDA

Puoi distribuire KEDA in diversi modi, elencati in documentazione. Io utilizzo un YAML monolitico:

kubectl apply -f
https://raw.githubusercontent.com/kedacore/keda/master/deploy/KedaScaleController.yaml

KEDA e i suoi componenti vengono installati nello spazio dei nomi keda. Comando per il controllo:

kubectl get pods -n keda

Aspetta che il pod del KEDA Operator avvii e passi a Running State. E poi continua.

Installazione di Redis con Helm

Se Helm non è installato, utilizza questa guida. Comando per l'installazione su Mac:

brew install kubernetes-helm
helm init --history-max 200

helm init inizializza l'interfaccia della riga di comando locale e installa Tiller nel cluster Kubernetes.

kubectl get pods -n kube-system | grep tiller

Aspetta che il pod di Tiller passi allo stato Running.

Nota del traduttore: L'autore utilizza Helm@2, che richiede l'installazione del componente server Tiller. Attualmente è in uso Helm@3, per il quale il server non è necessario.

Dopo aver installato Helm per avviare Redis, è sufficiente un comando:

helm install --name redis-server --set cluster.enabled=false --set 
usePassword=false stable/redis

Assicurati che Redis sia stato avviato correttamente:

kubectl get pods/redis-server-master-0

Attendi che il pod Redis passi allo stato Esecuzione.

Distribuzione dell'applicazione

Comando per la distribuzione:

kubectl apply -f go-app.yaml

//output
department.apps/go-prom-app created
service/go-prom-app-service created

Verifica che tutto sia avviato:

kubectl get pods -l=app=go-prom-app

Attendi il cambiamento dello stato di Redis in Esecuzione.

Distribuzione del server Prometheus

Il manifesto di Prometheus utilizza Kubernetes Service Discovery per Prometheus. Questo consente di scoprire dinamicamente i pod dell'applicazione in base all'etichetta del servizio.

kubernetes_sd_configs:
   - role: service
   relabel_configs:
   - source_labels: [__meta_kubernetes_service_label_run]
     regex: go-prom-app-service
     action: keep

Per la distribuzione:

kubectl apply -f prometheus.yaml

//output
clusterrole.rbac.authorization.k8s.io/prometheus created
serviceaccount/default configured
clusterrolebinding.rbac.authorization.k8s.io/prometheus created
configmap/prom-conf created
department.extensions/prometheus-deployment created
service/prometheus-service created

Verifica che tutto sia avviato:

kubectl get pods -l=app=prometheus-server

Attendere il passaggio di Prometheus allo stato Esecuzione.

Usa kubectl port-forward per accedere all'interfaccia utente di Prometheus (o al server API) all'indirizzo http://localhost:9090.

kubectl port-forward service/prometheus-service 9090

Distribuzione della configurazione di auto-scaling KEDA

Comando per creare ScaledObject:

kubectl apply -f keda-prometheus-scaledobject.yaml

Controlla i log dell'operatore KEDA:

KEDA_POD_NAME=$(kubectl get pods -n keda 
-o=jsonpath='{.items[0].metadata.name}')
kubectl logs $KEDA_POD_NAME -n keda

Il risultato dovrebbe apparire simile a:

time="2019-10-15T09:38:28Z" level=info msg="Watching ScaledObject:
default/prometheus-scaledobject"
time="2019-10-15T09:38:28Z" level=info msg="Created HPA with 
namespace default and name keda-hpa-go-prom-app"

Controlla il pod delle applicazioni. Dovrebbe esserci un'istanza in esecuzione, poiché minReplicaCount è uguale a 1:

kubectl get pods -l=app=go-prom-app

Verifica che la risorsa HPA sia stata creata con successo:

kubectl get hpa

Dovresti vedere qualcosa di simile:

NAME                   REFERENCE                TARGETS     MINPODS   MAXPODS   REPLICAS   AGE
keda-hpa-go-prom-app   Deployment/go-prom-app   0/3 (avg)   1         10        1          45s

Verifica della funzionalità: accesso all'applicazione

Per accedere all'endpoint REST della nostra applicazione, esegui:

kubectl port-forward service/go-prom-app-service 8080

Ora puoi accedere all'applicazione Go utilizzando l'indirizzo http://localhost:8080. Per fare ciò, esegui il comando:

curl http://localhost:8080/test

Il risultato dovrebbe apparire simile a:

Accessed on 2019-10-21 11:29:10.560385986 +0000 UTC 
m=+406004.817901246
Access count 1

In questo passaggio, controlla anche Redis. Vedrai che la chiave access_count è aumentata a 1:

kubectl exec -it redis-server-master-0 -- redis-cli get access_count
//output
"1"

Assicurati che il valore della metrica http_requests sia lo stesso:

curl http://localhost:8080/metrics | grep http_requests
//output
# HELP http_requests numero di richieste http
# TYPE http_requests counter
http_requests 1

Creazione del carico

Utilizzeremo hey — un'utility per generare carico:

curl -o hey https://storage.googleapis.com/hey-release/hey_darwin_amd64 
&& chmod a+x hey

Puoi anche scaricare l'utility per Linux o Windows.

Eseguila:

./hey http://localhost:8080/test

Per impostazione predefinita, l'utility invia 200 richieste. Puoi verificarlo utilizzando le metriche Prometheus e anche Redis.

curl http://localhost:8080/metrics | grep http_requests
//output
# HELP http_requests numero di richieste http
# TYPE http_requests counter
http_requests 201
kubectl exec -it redis-server-master-0 -- redis-cli get access_count
//output
201

Verifica il valore della metrica effettiva (restituita dalla query PromQL):

curl -g 
'http://localhost:9090/api/v1/query?query=sum(rate(http_requests[2m]))'
//output
{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1571734214.228,"1.686057971014493"]}]}}

In questo caso, il risultato effettivo è 1,686057971014493 e viene visualizzato nel campo value. Questo non è sufficiente per scalare, poiché la soglia che abbiamo impostato è 3.

Maggiore carico!

Nel nuovo terminale, segui il numero di pod dell'app:

kubectl get pods -l=app=go-prom-app -w

Aumentiamo il carico con il comando:

./hey -n 2000 http://localhost:8080/test

Dopo un po' vedrai che HPA scala il deployment e avvia nuovi pod. Controlla l'HPA per verificarlo:

kubectl get hpa
NAME                   REFERENCE                TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
keda-hpa-go-prom-app   Deployment/go-prom-app   1830m/3 (avg)   1         10        6          4m22s

Se il carico è irregolare, il deployment si ridurrà al punto in cui funziona solo un pod. Se vuoi controllare la metrica reale (restituita dalla query PromQL), usa il comando:

curl -g 
'http://localhost:9090/api/v1/query?query=sum(rate(http_requests[2m]))'

Pulizia

//Delete KEDA
kubectl delete namespace keda
//Delete the app, Prometheus server and KEDA scaled object
kubectl delete -f .
//Delete Redis
helm del --purge redis-server

Conclusione

KEDA permette di scalare automaticamente i tuoi deployment Kubernetes (fino/da zero) in base ai dati provenienti da metriche esterne. Ad esempio, basato su metriche Prometheus, lunghezza della coda in Redis, latenza del consumatore nel topic Kafka.

KEDA effettua l'integrazione con una fonte esterna e fornisce anche le sue metriche attraverso il Metrics Server per il Horizontal Pod Autoscaler.

Buona fortuna!

Cosa altro leggere:

  1. Le migliori pratiche e consigli per l'implementazione di container e Kubernetes in ambienti produttivi.
  2. 90+ strumenti utili per Kubernetes: deployment, gestione, monitoraggio, sicurezza e altro ancora.
  3. Il nostro canale Attorno a Kubernetes su Telegram.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster