Le Blog Amazon Web Services

Utiliser des instances Spot Amazon EC2 avec Karpenter

Karpenter est une solution d’autoscaling de cluster dynamique, haute performance et open source pour la plateforme Kubernetes, présentée lors de re:Invent 2021. Les clients choisissent une solution d’autoscaling pour plusieurs raisons, notamment pour améliorer la haute disponibilité et la fiabilité de leurs applications tout en réduisant les coûts. Avec l’introduction des instances Spot Amazon Elastic Compute Cloud (Amazon EC2), les clients peuvent réduire les coûts jusqu’à 90% par rapport aux prix à la demande. Les instances Spot EC2 sont des instances créées à partir de la capacité excédentaire d’AWS qui peuvent être interrompues, ce qui signifie que l’application doit être tolérante aux pannes, flexible et sans état. Les conteneurs étant conçus pour être immuables et éphémères, ils sont parfaitement adaptés aux instances Spot EC2. En combinant un autoscaler de cluster haute performance comme Karpenter avec les instances Spot EC2, les clusters Amazon Elastic Kubernetes Service (Amazon EKS) peuvent acquérir de la capacité de calcul en quelques minutes tout en maintenant les coûts bas.

Dans cet article, vous apprendrez à utiliser Karpenter avec les instances Spot EC2 et à gérer les interruptions des instances Spot.

Mise en route

Pour commencer à utiliser Karpenter sur AWS, vous avez besoin d’un cluster Kubernetes. Vous utiliserez un cluster Amazon EKS tout au long de cet article. Pour provisionner un cluster Amazon EKS et installer Karpenter, veuillez suivre le guide de démarrage dans la documentation Karpenter.

La seule responsabilité de Karpenter est de provisionner la capacité de calcul pour vos clusters Kubernetes, qui est configurée par une ressource personnalisée appelée NodePool. Actuellement, lorsqu’un pod est nouvellement créé, par exemple par le Horizonal Pod Autoscaler (HPA), kube-scheduler est responsable de trouver le meilleur nœud possible pour que kubelet puisse l’exécuter. Si aucun des critères de planification n’est satisfait, le pod reste en état d’attente et n’est pas planifié. Karpenter s’appuie sur le planificateur Kubernetes et attend les événements non planifiés, pour ensuite provisionner de nouveaux nœuds afin d’accueillir le ou les pods.

La diversification et la flexibilité sont importantes lors de l’utilisation d’instances Spot : types d’instances, tailles d’instances, zones de disponibilité et même régions. Être aussi flexible que possible permet à Karpenter d’avoir un plus grand choix de pools de capacité excédentaire et donc de réduire le risque d’interruption. L’extrait de code suivant montre un exemple de configuration de NodePool Spot spécifiant certaines contraintes :

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["nano", "micro", "small", "medium"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
          # ["spot","on-demand"]
      nodeClassRef:
        name: default

Cette configuration limite l’utilisation d’instances plus petites « c », « m » ou « r » mais reste aussi diversifiée que possible. Par exemple, cela peut être nécessaire dans des scénarios où vous déployez des DaemonSets d’observabilité.

Sélection des nœuds

Karpenter prend des décisions de provisionnement basées sur les contraintes de planification définies sur chaque pod en attente. Karpenter obtient les pods en attente par lots et les regroupe en fonction du CPU et de la mémoire pour trouver le type d’instance le plus efficace (c’est-à-dire le plus petit type d’instance). Karpenter sélectionne une liste de types d’instances dans la gamme diversifiée de types d’instances définis dans le NodePool qui peuvent accueillir le lot de pods et les transmet à l’API Amazon EC2 Fleet. EC2 Fleet utilise ensuite une stratégie d’allocation pour sélectionner l’instance EC2 à lancer.

Lors de l’utilisation d’instances Spot, la stratégie d’allocation optimisée par capacité et prix (PCO) est utilisée pour sélectionner le pool d’instances Spot EC2 dans cette liste diversifiée d’instances. La stratégie PCO prend en compte à la fois le prix le plus bas et la plus faible probabilité d’interruption pour sélectionner le pool d’instances Spot EC2 optimal afin de réduire la fréquence des interruptions Spot tout en optimisant les coûts. Lors de l’utilisation d’instances à la demande, la stratégie d’allocation au prix le plus bas est utilisée pour provisionner le type d’instance le moins cher.

Vous pouvez vérifier quel type d’instance a été lancé en exécutant :

kubectl -n karpenter logs -l app.kubernetes.io/name=karpenter

Vous devriez voir un message « created nodeclaim » qui liste les types d’instances pouvant accueillir vos pods, et un message « launched nodeclaim » indiquant le type d’instance sélectionné :

{"level":"INFO","time":"2024-04-17T08:37:40.573Z","logger":"controller.provisioner","message":"created nodeclaim","commit":"6b868db","nodepool":"default","nodeclaim":"default-2brtn","requests":{"cpu":"5150m","pods":"8"},"instance-types":"a1.2xlarge, a1.4xlarge, a1.metal, c1.xlarge, c3.2xlarge and 55 other(s)"}

{"level":"INFO","time":"2024-04-17T08:37:43.645Z","logger":"controller.nodeclaim.lifecycle","message":"launched nodeclaim","commit":"6b868db","nodeclaim":"default-2brtn","provider-id":"aws:///us-west-2c/i-0eacaf21d9a3f91ef","instance-type":"a1.2xlarge","zone":"us-west-2c","capacity-type":"spot","allocatable":{"cpu":"7910m","ephemeral-storage":"17Gi","memory":"14103Mi","pods":"58","vpc.amazonaws.com/pod-eni":"38"}}

Type de capacité

Lors de la création d’un NodePool, vous pouvez utiliser soit Spot, soit à la demande, soit les deux. Lorsque vous spécifiez les deux et si le pod ne précise pas explicitement s’il a besoin d’utiliser Spot ou à la demande, Karpenter choisit d’utiliser Spot lors du provisionnement d’un nœud. Dans le cas où la capacité Spot n’est pas disponible, Karpenter se replie sur les instances à la demande. Cependant, diversifier nos types d’instances augmentera les chances que Karpenter n’ait pas besoin de provisionner de capacité à la demande, restant plus longtemps sur la capacité Spot et réduisant les coûts.

Prenez en compte que si une limite de quota Spot a été atteinte au niveau du compte, vous pourriez obtenir une exception MaxSpotInstanceCountExceeded. Dans ce cas, Karpenter n’effectuera pas de repli. Vous devez mettre en place une surveillance adéquate des quotas et des exceptions pour créer les alertes nécessaires et contacter le support AWS pour l’augmentation de quota nécessaire.

Pour configurer Spot comme type de capacité, ajoutez cette contrainte dans le bloc requirements du NodePool :

  requirements:
    - key: "karpenter.sh/capacity-type"
       operator: In
      values: ["spot"]     # ["spot","on-demand"] if you want both capacity types

Résilience

Karpenter peut gérer nativement l’interruption des instances Spot : il isolera et drainera automatiquement le nœud avant l’événement d’interruption. Le NodePool lancera un nouveau nœud dès qu’il verra l’avertissement d’interruption Spot, informant qu’Amazon EC2 récupérera l’instance dans 2 minutes.

Pour activer la fonction de gestion des interruptions Spot, vous devez créer une file d’attente SQS pour que Karpenter surveille les événements d’interruption et que Amazon EventBridge transmette les événements d’interruption des services AWS à la file d’attente SQS. Karpenter fournit des détails pour provisionner cette infrastructure dans le modèle CloudFormation du guide de démarrage. Ensuite, configurez l’argument CLI –interruption-queue-name avec le nom de la file d’attente d’interruption configurée pour gérer les événements d’interruption.

Une autre fonctionnalité utile pour les instances Spot dans Karpenter est la consolidation. Par défaut, Karpenter définit la consolidationPolicy à WhenUnderutilized pour détecter automatiquement les nœuds sous-utilisés qui peuvent être perturbés (consolidation par suppression) ou remplacés (consolidation par remplacement) par un nœud plus petit et moins cher. Vous pouvez modifier le comportement de consolidation pour vos Node Pools dans le bloc disruption comme ci-dessous :

disruption:
  consolidationPolicy: WhenUnderutilized # WhenEmpty | WhenUnderutilized
  expireAfter: 72h 

Les versions de Karpenter antérieures à v0.34.0 ne prenaient en charge la consolidation par remplacement que pour les instances à la demande, les instances Spot avaient une politique de consolidation par suppression activée par défaut. Depuis la v0.34.0, vous pouvez activer la fonctionnalité pour utiliser la consolidation Spot-to-Spot. Vous pouvez en savoir plus à ce sujet dans l’article consolidation best practices with karpenter.

La gestion des signaux SIGTERM est également une bonne pratique lorsqu’il s’agit de tout type d’interruptions de conteneurs. Lorsqu’une interruption est sur le point de se produire, Kubernetes envoie un signal SIGTERM au processus principal (PID 1) de chaque conteneur du Pod qui est en cours d’éviction pour l’informer de l’interruption. Ensuite, il attend un certain temps (30 secondes par défaut) pour s’arrêter proprement avant d’envoyer le signal SIGKILL final qui termine les conteneurs. Par conséquent, pour vous assurer que vos processus se terminent proprement, vous devez gérer correctement le signal SIGTERM.

Surveillance

Les interruptions Spot peuvent se produire à tout moment. La surveillance des métriques et des journaux du cluster Kubernetes peut aider à créer des notifications lorsque Karpenter ne parvient pas à acquérir de la capacité. Vous devez mettre en place une surveillance adéquate au niveau du cluster Kubernetes pour tous les objets Kubernetes et surveiller le NodePool Karpenter. Vous utiliserez Prometheus et Grafana pour collecter les métriques du cluster Kubernetes et de Karpenter. Amazon CloudWatch Logs sera utilisé pour collecter les journaux.

Pour commencer avec Prometheus et Grafana sur Amazon EKS, veuillez suivre les instructions d’installation de Prometheus et Grafana dans le guide de démarrage de Karpenter. Les tableaux de bord Grafana sont préinstallés avec des tableaux de bord contenant des métriques de contrôleur, des métriques de nœud et des métriques de pod.

En utilisant le panneau Pod Phase inclus dans le tableau de bord Grafana préconfiguré nommé Karpenter Capacity, vous pouvez vérifier les pods qui ont un statut En attente pendant une période prédéfinie (par exemple 3 minutes). Cela nous aidera à comprendre s’il y a des applications qui ne peuvent pas être planifiées.

Les journaux du contrôleur Karpenter peuvent être envoyés à CloudWatch Logs en utilisant soit Fluent Bit, soit FluentD. (Voici des informations sur la façon de commencer avec CloudWatch Logs pour Amazon EKS.) Pour afficher les journaux du contrôleur Karpenter, allez dans le groupe de journaux /aws/containerinsights/cluster-name/application et recherchez Karpenter.

Dans le flux de journaux, recherchez les messages Provisioning failed dans les journaux du contrôleur Karpenter pour tout échec de provisionnement. L’exemple ci-dessous montre un échec de provisionnement dû à l’atteinte de la limite de compte pour les instances Spot.

2021-12-03T23:45:29.257Z        ERROR   controller.provisioning Provisioning failed, launching capacity, launching instances, with fleet error(s), UnfulfillableCapacity: Unable to fulfill capacity due to your request configuration. Please adjust your request and try again.; MaxSpotInstanceCountExceeded: Max spot instance count exceeded; launching instances, with fleet error(s), MaxSpotInstanceCountExceeded: Max spot instance count exceeded   {"commit": "6984094", "provisioner": "default"

Nettoyage

Pour éviter d’encourir des frais supplémentaires, n’oubliez pas de nettoyer les ressources que vous avez créées. Si vous avez suivi le guide de démarrage de la documentation Karpenter, consultez la section « Supprimer le cluster ». L’exemple ci-dessous montre comment désinstaller Karpenter en utilisant helm :

1. Désinstallez le contrôleur Karpenter (selon la façon dont vous avez installé Karpenter, l’exemple ci-dessous montre l’utilisation de helm)

helm uninstall karpenter --namespace karpenter

2. Supprimez le compte de service, la commande suivante suppose que vous avez utilisé eksctl

eksctl delete iamserviceaccount
     --cluster ${CLUSTER_NAME}
     --name karpenter
     --namespace karpenter

3. Supprimez la pile à l’aide de aws cloudformation delete-stack –stack-name Karpenter-${CLUSTER_NAME} ou terraform destroy -var cluster_name=$CLUSTER_NAME

4. Supprimez le cluster si vous l’avez créé si nécessaire à l’aide de eksctl delete cluster –name ${CLUSTER_NAME}

Conclusion

Dans cet article de blog, vous avez appris à propos de Karpenter et comment vous pouvez utiliser les instances Spot EC2 avec Karpenter pour mettre à l’échelle les besoins de calcul dans un cluster Amazon EKS. Vous pouvez consulter la section Lectures complémentaires ci-dessous pour en savoir plus sur Karpenter.

Lectures complémentaires

Article original : https://aws.amazon.com/blogs/containers/using-amazon-ec2-spot-instances-with-karpenter/