Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Autoscaling HPA Kubernetes con metriche EXTERNAL Stackdriver RPS del Load Balancer HTTP/S di Google

By Eran ChetzroniNov 5, 20185 min read

Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.

1 ce4zye4gpmytsbthtlntwq

Di norma i deployment Kubernetes vengono scalati in base a metriche come consumo di CPU o memoria, ma in alcuni casi è necessario scalare su metriche esterne. In questo post vi guido passo passo nella configurazione dell'autoscaling con Horizontal Pod Autoscaler (HPA) basato su una qualsiasi metrica Stackdriver: nello specifico useremo le Request Per Second di un Load Balancer HTTP/S di Google Cloud.

1 ce4zye4gpmytsbthtlntwq

Autoscaling dell'Horizontal Pod Autoscaler di Kubernetes con metriche Stackdriver

Si comincia!

Per prima cosa creiamo un nuovo cluster Google Kubernetes Engine (GKE):

gcloud beta container clusters create "hpa-with-stackdriver-metrics" --zone "us-central1-a" \
--username "admin" \
--cluster-version "1.10.7-gke.6" \
--machine-type "n1-standard-1" \
--image-type "COS" \
--disk-type "pd-standard" \
--disk-size "100" --scopes \ "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append"
--num-nodes "3" \
--enable-cloud-logging \
--enable-cloud-monitoring \
--addons HorizontalPodAutoscaling,HttpLoadBalancing \
--enable-autoupgrade --enable-autorepair

Attenzione al parametro `enable-cloud-monitoring`: è ciò che ci permetterà di leggere le metriche di Stackdriver Monitoring.

Deploy del Custom Metrics Stackdriver Adapter

Il custom metrics adapter si occupa di importare le metriche Stackdriver nelle API di Kubernetes, così l'HPA potrà consultarle e agire di conseguenza. Trovate maggiori dettagli più avanti, nella sezione di troubleshooting.

Per dare agli oggetti GKE l'accesso alle metriche memorizzate in Stackdriver, occorre installare il Custom Metrics Stackdriver Adapter nel cluster.

Per poter eseguire il Custom Metrics Adapter, dovete prima abilitare il vostro utente alla creazione dei ruoli di autorizzazione richiesti, lanciando questo comando:

kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole cluster-admin \
--user "$(gcloud config get-value account)"

Ora installiamo l'adapter vero e proprio, che ci consentirà di leggere le metriche da Stackdriver:

kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter.yaml

Creare un Deployment

Adesso facciamo il deploy di una semplice applicazione nginx, che in seguito verrà scalata in base alle RPS misurate dal Load Balancer HTTP/S.

Create questo file: deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
selector:
app: nginx

Ora applichiamolo:

kubectl apply -f deployment.yaml

Creare un Ingress LoadBalancer

Create il file di ingress: ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: basic-ingress
spec:
backend:
serviceName: nginx
servicePort: 80

E applicate l'ingress:

kubectl apply -f ingress.yaml

Creare l'oggetto HorizontalPodAutoscaler

È qui che succede la magia.

Useremo una metrica esterna*, indicando come metricName:

loadbalancing.googleapis.com|https|

Nota: l'elenco completo delle metriche Stackdriver lo trovate qui, oppure potete usare il Metrics Explorer.

Conviene anche definire un metricSelector, in modo da limitare la lettura alle sole metriche del nostro specifico load balancer.

Individuiamo la forwarding rule del nostro LB:

$ kubectl describe ingress basic-ingress
Name: basic-ingress
Namespace: default
Address: 35.190.3.165
Default backend: nginx:80 (10.48.2.11:80)
Rules:
Host Path Backends
---- ---- --------
* * nginx:80 (10.48.2.11:80)
Annotations:
backends: {"k8s-be-32432--ffd629d77b6630de":"HEALTHY"}
forwarding-rule: k8s-fw-default-basic-ingress--ffd629d77b6630de
target-proxy: k8s-tp-default-basic-ingress--ffd629d77b6630de
url-map: k8s-um-default-basic-ingress--ffd629d77b6630de

A questo punto possiamo aggiungere il match della label alla nostra configurazione (notate la label "forwarding_rule_name"):

metricSelector:
matchLabels:
resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de

Il file finale sarà questo: hpa.yaml

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: nginx
spec:
minReplicas: 1
maxReplicas: 5
metrics:
- external:
metricName: loadbalancing.googleapis.com|https|request_count metricSelector: matchLabels: resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de targetAverageValue: "1" type: External scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx

Notate l'uso di targetAverageValue: indica quanta parte del valore totale della metrica può gestire ciascuna replica. È utile con metriche che descrivono lavoro o risorse ripartibili tra le repliche; nel nostro caso ogni replica può gestire una singola RPS (cioè 1). Il valore va naturalmente adattato alle vostre esigenze.

Mettiamo tutto alla prova

Cominciamo generando traffico verso il nostro load balancer.

Come si vede dal comando precedente:

kubectl describe ingress basic-ingress

L'indirizzo IP pubblico del nostro Ingress è 35.190.3.165.

Ora bombardiamo l'endpoint 🥊 con qualche richiesta:

while true ; do curl -Ss -k --write-out '%{http_code}\n' --output /dev/null http://35.190.3.165/ ; done

Vediamo se il nostro HorizontalPodAutoscaler reagisce:

kubectl describe hpa nginx-hpa

All'inizio potreste vedere qualche warning, perché la metrica non è ancora popolata; dopo qualche minuto, però, la sezione Metrics si riempie:

Name: nginx-hpa
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"autoscaling/v2beta1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"nginx-hpa","namespace":"default"},"spec":{"ma...
CreationTimestamp: Wed, 31 Oct 2018 18:18:28 +0200
Reference: Deployment/nginx
Metrics: ( current / target )
"loadbalancing.googleapis.com|https|request_count" (target average value): 1034m / 1
Min replicas: 1
Max replicas: 5

E nella sezione "Events" troviamo:

Events:
Type Reason Age From Message
...
Normal SuccessfulRescale 2m horizontal-pod-autoscaler New size: 2; reason: external metric loadbalancing.googleapis.com|https|request_count(&LabelSelector{MatchLabels:map[string]string{resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de,},MatchExpressions:[],}) above target

Decollo riuscito! 🚀

Troubleshooting

  1. Un modo semplice per verificare se la metrica viene effettivamente importata nelle external metrics API di Kubernetes è esplorare l'API a mano. Vi servirà anche per controllare di aver impostato correttamente il metricSelector.

Per prima cosa, avviamo il proxy di Kubernetes:

kubectl proxy --port=8080

Poi possiamo accedere da localhost:

http://localhost:8080/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_count

Ecco un estratto del risultato:

{
"kind": "ExternalMetricValueList",
"apiVersion": "external.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_count"
},
"items": [\
{\
"metricName": "loadbalancing.googleapis.com|https|request_count",\
"metricLabels": {\
"metric.labels.cache_result": "DISABLED",\
"resource.labels.backend_target_type": "BACKEND_SERVICE",\
"resource.labels.backend_name": "k8s-ig--ffd629d77b6630de",\
...\
"resource.labels.forwarding_rule_name": "k8s-fw-default-basic-ingress--ffd629d77b6630de",\
...\
},\
"timestamp": "2018-11-01T08:41:30Z",\
"value": "2433m"\
}\
]
}

Voilà!


2. Un altro modo per capire come si comporta l'adapter è leggerne i log; iniziamo elencando i pod custom-metrics:

$ kubectl get pods -n custom-metrics
NAME READY
custom-metrics-stackdriver-adapter-c4d98dc54-2n4jz 1/1

Infine, leggiamo i log:

$ kubectl logs custom-metrics-stackdriver-adapter-c4d98dc54-2n4jz -n custom-metrics
...
I1104 06:42:11.125627 1 trace.go:76] Trace[1192308782]: "List /apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com|https|request_count" (started: 2018-11-04 06:42:08.155209905 +0000 UTC m=+311951.293335726) (total time: 2.970372027s):
Trace[1192308782]: [2.97027864s] [2.970185564s] Listing from storage done
...

Da questi log si ricavano molte informazioni preziose, soprattutto in caso di messaggi di errore.

Conclusione

Sfruttare metriche esterne come quelle raccolte e archiviate da Stackdriver è semplice e immediato. Allo stesso modo potete utilizzare anche le vostre custom metrics pubblicate su Stackdriver tramite la Monitoring API.

Ho creato un repository GitHub con tutte le risorse usate per questo post: lo trovate qui.

Volete altri articoli? Date un'occhiata al nostro blog oppure seguite Eran su Twitter.