
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.

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-autorepairNotez 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.yamlCré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/v1kind: Deploymentmetadata: name: nginxspec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.8 ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: nginx labels: app: nginxspec: type: NodePort ports: - port: 80 protocol: TCP selector: app: nginxDéployons-le :
kubectl apply -f deployment.yamlCréer un Ingress LoadBalancer
Créez le fichier d'ingress : ingress.yaml
apiVersion: extensions/v1beta1kind: Ingressmetadata: name: basic-ingressspec: backend: serviceName: nginx servicePort: 80Puis appliquez-le :
kubectl apply -f ingress.yamlCré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-ingressName: basic-ingressNamespace: defaultAddress: 35.190.3.165Default 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--ffd629d77b6630deOn 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--ffd629d77b6630deLe fichier final ressemblera à ceci : hpa.yaml
apiVersion: autoscaling/v2beta1kind: HorizontalPodAutoscalermetadata: name: nginxspec: 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: nginxNotez 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-ingressL'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/ ; doneVoyons 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-hpaNamespace: defaultLabels: <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 +0200Reference: Deployment/nginxMetrics: ( current / target )"loadbalancing.googleapis.com|https|request_count" (target average value): 1034m / 1Min replicas: 1Max replicas: 5Et 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 targetDécollage réussi ! 🚀
Dépannage
- 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=8080On peut alors y accéder depuis localhost :
http://localhost:8080/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_countVoici 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-metricsNAME READYcustom-metrics-stackdriver-adapter-c4d98dc54-2n4jz 1/1Puis 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.