Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Autoscaling HPA K8s avec métriques Stackdriver RPS du Load Balancer HTTP/S Google

By Eran ChetzroniNov 5, 20185 min read

Cette page est également disponible en English, Deutsch, Español, Italiano, 日本語 et Português.

1 ce4zye4gpmytsbthtlntwq

La plupart du temps, on scale ses déploiements Kubernetes en fonction de métriques comme la consommation CPU ou mémoire, mais il arrive qu'on doive s'appuyer sur des métriques externes. Dans cet article, je vous guide pas à pas pour mettre en place l'autoscaling avec Horizontal Pod Autoscaler (HPA) à partir de n'importe quelle métrique Stackdriver ; concrètement, on utilisera les requêtes par seconde d'un Load Balancer HTTP/S Google Cloud.

1 ce4zye4gpmytsbthtlntwq

Autoscaling de l'Horizontal Pod Autoscaler Kubernetes avec les métriques Stackdriver

C'est parti !

Commençons par créer un nouveau 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

Notez l'option `enable-cloud-monitoring`, qui nous permettra de lire les métriques de Stackdriver Monitoring.

Déployer le Custom Metrics Stackdriver Adapter

L'adaptateur de métriques personnalisées importe les métriques Stackdriver vers l'API Kubernetes, ce qui permet au HPA de les exploiter et d'agir en conséquence. Vous trouverez plus de détails dans la section Dépannage ci-dessous.

Pour donner aux objets GKE l'accès aux métriques stockées dans Stackdriver, il faut déployer le Custom Metrics Stackdriver Adapter dans votre cluster.

Pour exécuter le Custom Metrics Adapter, vous devez accorder à votre utilisateur le droit de créer les rôles d'autorisation requis, en exécutant la commande suivante :

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

Déployons à présent l'adaptateur qui nous permettra de lire les métriques depuis Stackdriver :

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

Créer un déploiement

Déployons une simple application nginx, que l'on scalera ensuite en fonction du RPS mesuré par le Load Balancer HTTP/S.

Créez ce fichier : 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

Déployons-le :

kubectl apply -f deployment.yaml

Créer un Ingress LoadBalancer

Créez le fichier d'ingress : ingress.yaml

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

Puis appliquez-le :

kubectl apply -f ingress.yaml

Créer l'objet HorizontalPodAutoscaler

C'est ici que tout se joue.

Nous utilisons une métrique externe*, avec le metricName :

loadbalancing.googleapis.com|https|

Note : la liste complète des métriques Stackdriver est disponible ici, ou via le Metrics Explorer.

Il faut également utiliser un metricSelector pour ne cibler que les métriques de notre load balancer.

Identifions la forwarding rule de notre 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

On peut maintenant ajouter la correspondance de label à notre configuration (notez le label forwarding_rule_name) :

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

Le fichier final ressemblera à ceci : 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

Notez l'usage de targetAverageValue, qui définit la part de la valeur totale de la métrique que chaque réplique peut absorber. Pratique pour des métriques décrivant un travail ou une ressource répartissable entre répliques ; ici, chaque réplique gère une seule (1) RPS. À ajuster, bien sûr, selon vos besoins.

Testons l'ensemble

Commençons par envoyer du trafic vers notre load balancer.

Comme on l'a vu avec la commande précédente :

kubectl describe ingress basic-ingress

L'adresse IP publique de notre Ingress est : 35.190.3.165

Bombardons cet endpoint 🥊 de quelques requêtes :

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

Voyons si notre HorizontalPodAutoscaler réagit :

kubectl describe hpa nginx-hpa

À ce stade, vous verrez sans doute quelques avertissements car la métrique n'est pas encore alimentée, mais après quelques minutes la section Metrics se remplit :

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

Et dans la section Events, on observe :

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

Décollage réussi ! 🚀

Dépannage

  1. Pour vérifier rapidement si la métrique est bien importée dans l'API external metrics de Kubernetes, le plus simple est de parcourir l'API à la main. Cela permet aussi de contrôler que le metricSelector est correctement configuré.

Commençons par lancer le proxy Kubernetes :

kubectl proxy --port=8080

On peut alors y accéder depuis localhost :

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

Voici un extrait du résultat :

{
"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"\
}\
]
}

Et voilà !


2. Autre approche pour observer le comportement de l'adaptateur : surveiller ses logs. Listons d'abord les pods custom-metrics :

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

Puis consultons les logs :

$ 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
...

Ces logs regorgent d'informations utiles, en particulier en cas de message d'erreur.

Conclusion

Exploiter des métriques externes comme celles collectées et stockées par Stackdriver est plutôt simple et direct. De la même manière, vous pouvez aussi publier vos propres métriques personnalisées vers Stackdriver via la Monitoring API.

J'ai créé un dépôt GitHub avec les ressources utilisées pour cet article ici.

Envie d'en lire davantage ? Consultez notre blog ou suivez Eran sur Twitter.