De la haute disponibilité avec Azure Kubernetes Services : le focus Apps !
Published: 2020-10-20 09:10:01

Après s’être intéressé à la haute disponibilité de notre [infrastructure](https://dev.to/thomasrannou/de-la-haute-disponibilite-avec-azure-kubernetes-services-le-focus-infra-2jdc), au menu aujourd'hui : la configuration de la montée en charge automatique sur nos pods et nos nodes.

#Rappel sur le scaling

Il y a deux possibilités de scale.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9yu9zwnmsbyah024d24y.png)

## Scaling Vertical
Si un traitement dans un pod necessite plus de ressources mémoire ou vCPU que ce que vous avez prévu mais que ce traitement ne peux être réparti sur plusieurs instances, vous devrez allouer plus de ressources à votre pod. On parle alors de scaling vertical.

## Scaling horizontal
Cette méthode permet de multiplier le nombre d'instance d'une application si l'instance est trop sollicitée afin de repartir la charges.

Aujourd'hui nous allons nous concentrer sur le scaling horizontal et la façon de l'automatiser.

#L’autoscaling sur les pods

Le scaling consiste à augmenter ou diminuer le nombre d’instances d’une application. Cela permet par exemple de résister à un pic de charge si votre service est fortement sollicité par moments et très peu le reste du temps. On peut configurer grâce à Kubernetes l’upscale et le downscale pour s’adapter en temps réel aux besoins de nos utilisateurs.

##Montée en charge manuelle
Avant de rentrer dans le vif du sujet, petite précision sur le scaling : la commande kubectl scale vous permet de modifier instantanément le nombre d’instances dont vous souhaitez disposer pour exécuter votre application.

```powershell
kubectl scale --replicas=5 deployment/myApp
```

##Montée en charge automatique

###Préparation du cluster
Je provisionne un AKS avec le monitoring activé, comme expliqué [ici](http://thomasrannou.azurewebsites.net/2019/10/16/azure-monitor-pour-aks/) et je vais utiliser un container registry pour déployer mon app comme expliqué dans ce [tuto](http://thomasrannou.azurewebsites.net/2019/08/03/deployer-dans-aks).

###Déploiement de mon application
Voici le yaml que j’ai écrit pour mon application :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mjdthot7hpwchdwkhs8s.PNG)

L’élément qui va nous intéresser aujourd’hui est “ressources” avec la définition des propriétés requests et limits.

**Requests**, c’est ce que le pod est garanti d’avoir à sa disposition pour fonctionner. Ici mon container aura donc 100m de CPU et 15Mi de RAM.
**Limits** en revanche c’est une sécurité sur la consommation de ressources du container. Il ne pourra pas utiliser plus de 500m de CPU et 512Mi de RAM. 

Si le pod consomme trop de ressources :
- CPU : un mécanisme de [throttling](https://medium.com/omio-engineering/cpu-limits-and-aggressive-throttling-in-kubernetes-c5b20bd8a718) permet de limiter la charge CPU
- RAM : le pod est détruit

Les valeurs de CPU sont définies en milli-cores. Si vous avez besoin de deux VCPU il faudra indiquer 2000m. La notation “2” sera équivalente.

Déployons mon application avec un :

```powershell
Kubectl apply –f .\deploytoaks.yaml
```

###Ajoutons de l’HPA
La commande kubectl autoscale crée un objet HorizontalPodAutoscaler (HPA) qui cible une ressource spécifiée et la fait évoluer si nécessaire. Le HPA ajuste périodiquement le nombre d’instances dupliquées en fonction de la consommation CPU ou RAM.

En cas de modification de la charge, cet objet [augmente ou réduit](https://cloud.google.com/kubernetes-engine/docs/how-to/scaling-apps?hl=fr#autoscaling-deployments) le nombre d’instances de l’application.

Ici, je vais demander un HPA sur mon application aspwebsitenetcore. Je lui indique de scaler entre 2 et 10 réplicas. L’upscale se fera si le pourcentage CPU consommé dépasse les 10% de ce qui est alloué, ici c’est donc 10% de consommé sur  les 500millicores de CPU.

```powershell
kubectl autoscale deployment aspwebsitenetcore --max 10 --min 2 --cpu-percent 10
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kf8ltj3ilq7wrjen2xz2.png)

Précision : La commande kubectl get hpa me permet d’afficher mes différents scaling mis en place sur mon cluster k8s.
Je constate qu’après la mise en place de mon HPA, j’ai désormais deux pods d’opérationnel ! Les appels vers mon site web seront automatiquement répartis vers ces deux pods via le loadbalancer de Kubernetes.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n55u08hz0kahes9mekvi.png)

Si je rééxecute un get hpa, je visualise dans la colonne targets ou se situent mes conteneurs par rapport à la limite qu’on leur a fixée :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/v99gb7j311cf8kcs9jyf.png)

###Stress Test
Maintenant, je vais stresser un peu mon application et simuler un fort trafic sur mon site. En toute logique, l’autoscaling configuré pour mon cluster AKS doit intervenir et mon nombre de pods devraient se dupliquer. Pour ce faire je vais utiliser [Vegeta](https://github.com/tsenart/vegeta) !

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2stt7t3co4v0emlr7tbb.jpg)

L'installation et l'utilisation de l'outil est super simple.
Pour le télécharger, rendez vous [ici](https://github.com/tsenart/vegeta/releases)

La commande me permet de lancer une "attaque" pendant 5 minutes à raison de 5 appels seconde.
```powershell
echo GET http://40.121.84.148 | vegeta.exe attack -duration=5m -rate=5 -output=stress-results.bin
```

J'ai spécifié un output qui me permettra ensuite de générer un rapport comme ceci afin de constater la latence induite par mon test de charge.

```powershell
vegeta.exe plot -title=Results stress-results.bin > stress-results-plot.html
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w6qihiyav8aeynkjnalc.PNG)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7b01tp64od0cuo967crm.PNG)

Assez rapidement, des événements m’informent du scale up :
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7gzgqmfhpufkqi0a408c.png)

J’ai maintenant 5 pods d’actif :
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8gd6b3hpb1jnx4atf6e6.png)

Puis 6 :
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/e3m05xxkz0fcizxg0dcw.png)

Quand je stoppe le load, je vais constater l’inverse et voir progressivement mon nombre de pods diminuer :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/59zlv8zh2cx86lnx64a6.png)

Jusqu’à revenir à ma situation initiale :
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ijoas1dq6s5q1viptpeg.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/icy4rj7e2ymm99yr5ooi.png)

Pour déterminer à quel moment scale up ou down, Kubernetes utilise un algorithme présenté [ici](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details).

###Un autoscale déployé via un YAML
Et oui, il est possible de configurer un scaling via une description yaml comme celle-ci :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kfy56zb6btcmmesidcvf.png)

Je précise toujours sur quel déploiement je veux positionner mon scaling, un nombre de pod min et max et une condition ici représentée par “targetAverageValue” sur la consommation mémoire de mes conteneurs. Ici la montée en charge sera toujours entre 2 et 10 pods et le scale se fera si on dépasse les 32Mi de mémoire.

###KEDA

Il est possible de configurer un autoscaling sur des métriques autres que la charge CPU et RAM. Pour se faire je vous invite à vous intéresser à [KEDA](https://docs.microsoft.com/fr-fr/azure/azure-functions/functions-kubernetes-keda). 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3e0abdy3jjr6bq9fyag9.png)

Cet outil permet par exemple de configurer un autoscale sur :
- Le nombre de message dans une queue Azure Service Bus ou RabbitMQ
- Le nombre de fichiers dans Azure Storage
- Des métriques customs exposées via Prometheus
- et bien d'autres sources !

La liste complète [ici](https://keda.sh/#scalers) !

Après la gestion du scaling des pods, voyons comment configurer une montée en charge sur les nodes.

#Autoscaling de nodes

AKS nous offre la possibilité d’allouer et de désallouer automatiquement des nodes Kubernetes pour l’hébergement de nos applications : c’est grâce à l’autoscaler de cluster.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/i39semendr0591brsoou.png)

##Création du cluster
Demandons un cluster de test :
```powershell
az group create --name rg-aks --location eastus
az aks create --resource-group rg-aks --name myAKS --node-count 1 --generate-ssh-keys
```

##Autoscale manuel
Comme vu [ici](https://dev.to/thomasrannou/de-la-haute-disponibilite-avec-azure-kubernetes-services-le-focus-infra-2jdc) AKS fonctionne avec un système de node pool. Un pool est un regroupement de nœuds dans mon cluster, chaque nœud étant une machine virtuelle Azure.

Pour scaler manuellement mes nodes il faut cibler un pool :
```powershell
az aks scale --resource-group rg-aks --name myAKS --node-count 3 --nodepool-name #NodePoolName#
```
Pour obtenir un descriptif du/des pool(s), utilisez cette commande. On y voit le nom du pool à utiliser dans la commande ci-dessus.

```powershell
az aks nodepool list --resource-group rg-aks --cluster-name myAKS
```

Il y a maintenant 3 nœuds, donc 3 VM pouvant héberger des pods, dans notre cluster.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a9cdvb0o5450ie3a651f.png)

##Autoscale automatique
Nous avons parlé précédemment des “ressources” avec la définition des propriétés requests et limits.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/94hxs56yu5j6xzmx0gvb.png)

L’autoscaler AKS augmente ou diminue automatiquement la taille du pool de nœuds, en analysant la demande de ressources des pods.
•	Si les pods ne peuvent pas être démarrés, car il n’y a pas assez de puissance cpu/ram sur les nœuds du pool, l’autoscaler de cluster en ajoute, jusqu’à atteindre la taille maximale du pool de nœuds.
•	Si les nœuds sont sous-utilisés et que tous les pods peuvent être déployés en utilisant moins de noeuds, l’autoscaler de cluster déplace les pods puis supprime des nœuds, jusqu’à atteindre la taille minimale du pool.

Pour la mise en œuvre je dois provisionner un AKS avec le support de l’autoscale sur les nodes, il faut alors spécifier une taille minimale et maximale pour le pool de nœuds.
```powershell
az aks create --resource-group rg-aks --name myAKSWithAutoscale --node-count 1 --vm-set-type VirtualMachineScaleSets --enable-cluster-autoscaler --min-count 1 --max-count 3
```

Par ailleurs, il est possible de configurer plus finement le fonctionnement du scaling via des paramètres supplémentaires.
Par exemple :
--scan-interval : permet de fixer la fréquence d'évaluation du cluster pour un éventuel scale. Ce paramètre permettra de temporiser suite à au scaling avant de provisionner ou supprimer à nouveau une VM. La liste complète des paramètres est disponible [ici](https://docs.microsoft.com/fr-fr/azure/aks/cluster-autoscaler#using-the-autoscaler-profile)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jbrnawp8smv6xdu1164c.png)

On a maintenant un cluster avec une VM dans le node pool déclaré, scalable entre 1 et 3.

###Modifier son autoscale

Si mon cluster contient un seul nodepool, je peux utiliser la commande :
```powershell
az aks update --resource-group rg-aks --name myAKSWithAutoscale --update-cluster-autoscaler --min-count 1 --max-count 5
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/97hllx1k8qf3dfzgezxo.png)

Si mon cluster utilise plusieurs nodepools, je dois utiliser l'instruction az aks nodepool update, comme ceci : 
```powershell
az aks nodepool update --resource-group rg-aks --cluster-name myAKSWithAutoscale --name nodepool1 --update-cluster-autoscaler --min-count 1 --max-count 5
```

Dernier cas d'usage, si notre cluster existe mais que l’autoscale n’a pas été configuré à la création il faut l’activer via une commande sur le pool cible avec l'argument --enable-cluster-autoscaler :
```powershell
az aks nodepool update --resource-group rg-aks --cluster-name myAKSWithAutoscale --name nodepool1 --enable-cluster-autoscaler --min-count 1 --max-count 5
```
Nous avions précédemment demandé 3 nodes mais suite à notre configuration de l’autoscale, le nombre de nœud redescend progressivement à 1 puisqu’il n’y a pas de charge sur mes nœuds !

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oqmoyqpurxzrisjtjeti.png)

Nous avons donc vu ici un second niveau d’autoscaling, nous permettant de provisionner des nodes dynamiquement, afin de s’adapter à la demande en ressources des pods.

# Conclusion
Après avoir abordé la haute disponibilité coté [Infrastructure](https://dev.to/thomasrannou/de-la-haute-disponibilite-avec-azure-kubernetes-services-le-focus-infra-2jdc) nous avons vu aujourd'hui comment rendre nos applicatifs résilient grâce à l'autoscaling. En effet l'un ne vas pas sans l'autre ! Configuré en premier lieu sur les pods il doit également être géré sur les nodes pour une meilleure élasticité face à la charge.

**Merci à [Louis-Guillaume Morand](https://www.linkedin.com/in/lgmorand) et [Yves de Caqueray](https://www.linkedin.com/in/yves-de-cacqueray-88b5b793) pour la relecture et leurs précieux conseils.**

Thomas