Exécuter des charges de travail tolérantes aux pannes à moindre coût avec des pods Spot


Cette page explique comment exécuter des charges de travail tolérantes aux pannes à moindre coût avec des pods Spot dans des clusters Google Kubernetes Engine (GKE) Autopilot.

Présentation

Dans les clusters GKE Autopilot, les pods Spot sont des pods qui s'exécutent sur des nœuds sauvegardés par des VM Spot Compute Engine. Les pods Spot sont facturés moins chers que les pods Autopilot standards, mais peuvent être supprimés par GKE chaque fois que des ressources de calcul sont nécessaires pour exécuter des pods standards.

Les pods Spot sont parfaits pour exécuter des charges de travail sans état, par lots ou tolérantes aux pannes, à des coûts inférieurs à ceux des charges de travail standards. Pour utiliser des pods Spot dans des clusters Autopilot, modifiez le fichier manifeste avec la spécification de pod pour demander des pods Spot.

Vous pouvez exécuter des pods Spot sur la classe de calcul Autopilot à usage général par défaut, ainsi que sur des classes de calcul spécialisées qui répondent à des exigences matérielles spécifiques. Pour en savoir plus sur ces classes de calcul, consultez la page Classes de calcul dans Autopilot.

Pour en savoir plus sur la tarification des pods Spot dans les clusters Autopilot, consultez la page Tarifs de Google Kubernetes Engine.

Les pods Spot sont exclus du contrat de niveau de service Autopilot.

Avantages

L'utilisation de pods Spot dans vos clusters Autopilot offre les avantages suivants :

  • Tarifs plus bas que l'exécution des mêmes charges de travail sur les pods Autopilot standards.
  • GKE gère automatiquement l'autoscaling et la planification.
  • GKE rejette automatiquement les nœuds qui exécutent des pods Spot afin de s'assurer que les pods standards, tels que vos charges de travail critiques, ne sont pas programmés sur ces nœuds. Les déploiements qui utilisent les pods Spot sont automatiquement mis à jour avec la tolérance correspondante.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Si vous souhaitez utiliser Google Cloud CLI pour cette tâche, installez puis initialisez gcloud CLI. Si vous avez déjà installé gcloud CLI, assurez-vous de disposer de la dernière version en exécutant la commande gcloud components update.

Demander des pods Spot dans vos charges de travail Autopilot

Pour demander que vos pods s'exécutent en tant que pods Spot, utilisez l'étiquette cloud.google.com/gke-spot=true dans un nodeSelector ou une affinité de nœud dans la spécification des pods. GKE provisionne automatiquement les nœuds pouvant exécuter des pods Spot.

Les pods Spot peuvent être évincés et arrêtés à tout moment, par exemple si des ressources de calcul sont nécessaires ailleurs dans Google Cloud. En cas d'arrêt, les pods Spot du nœud d'arrêt peuvent demander un délai de grâce d'une durée maximale de 15 secondes avant l'arrêt, ce qui est accordé de la manière la plus optimale possible, en spécifiant le champ terminationGracePeriodSeconds.

Le délai de grâce maximal accordé aux pods Spot lors de la préemption est de 15 secondes. Le fait de demander plus de 15 secondes dans l'opération terminationGracePeriodSeconds n'accorde pas plus de 15 secondes lors de la préemption. En cas d'éviction, votre pod reçoit le signal SIGTERM et doit prendre les mesures nécessaires pour s'arrêter pendant le délai de grâce.

Pour Autopilot, GKE rejette également automatiquement les nœuds créés pour exécuter les pods Spot et modifie ces charges de travail avec la tolérance correspondante. Le rejet empêche la planification des pods standards sur les nœuds qui exécutent des pods Spot.

Utiliser un nodeSelector pour exiger des pods Spot

Vous pouvez utiliser un nodeSelector pour exiger des pods Spot dans un déploiement. Ajoutez le libellé cloud.google.com/gke-spot=true à votre déploiement, comme dans l'exemple suivant :

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      labels:
        app: pi
    spec:
      nodeSelector:
        cloud.google.com/gke-spot: "true"
      terminationGracePeriodSeconds: 15
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

Utiliser l'affinité de nœuds pour demander des pods Spot

Vous pouvez également utiliser l'affinité de nœuds pour demander des pods Spot. L'affinité de nœuds vous permet de sélectionner plus facilement des nœuds pour exécuter vos charges de travail. Par exemple, vous pouvez combiner plusieurs critères de sélection afin de contrôler plus précisément l'emplacement d'exécution de vos pods. Lorsque vous utilisez l'affinité de nœuds pour demander des pods Spot, vous pouvez spécifier le type d'affinité de nœuds à utiliser comme suit :

  • requiredDuringSchedulingIgnoredDuringExecution : doit utiliser des pods Spot.
  • preferredDuringSchedulingIgnoredDuringExecution : utiliser les pods Spot de la manière la plus optimale possible.

Pour utiliser l'affinité de nœuds pour exiger des pods Spot dans un déploiement, ajoutez la règle nodeAffinity suivante à votre fichier manifeste de déploiement :

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      labels:
        app: pi
    spec:
      terminationGracePeriodSeconds: 15
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: cloud.google.com/gke-spot
                operator: In
                values:
                - "true"
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

Demander des pods Spot de la manière la plus optimale possible

Pour utiliser l'affinité de nœuds afin de demander les pods Spot de la manière la plus optimale possible, utilisez preferredDuringSchedulingIgnoredDuringExecution. Lorsque vous demandez des pods Spot de manière préférée, GKE planifie vos pods en fonction de l'ordre suivant :

  1. Nœuds existants pouvant exécuter des pods Spot qui disposent d'une capacité pouvant être allouée
  2. Nœuds standards existants qui disposent d'une capacité pouvant être allouée
  3. Nouveaux nœuds pouvant exécuter des pods Spot, si les ressources de calcul sont disponibles
  4. Nouveaux nœuds standards

GKE choisit de préférence les nœuds standards existants disposant d'une capacité pouvant être allouée plutôt que la création de nœuds pour des pods Spot. Par conséquent, il se peut que vous ayez plus de pods qui s'exécutent en tant que pods standards qu'en tant que pods Spot, ce qui vous empêche de tirer pleinement parti des coûts réduits des pods Spot.

Requêtes pour les pods préemptifs

Les clusters Autopilot acceptent les requêtes pour les pods préemptifs à l'aide du sélecteur cloud.google.com/gke-preemptible. Les pods qui utilisent ce sélecteur sont automatiquement migrés vers les pods Spot, et le sélecteur passe à cloud.google.com/gke-spot.

Rechercher et supprimer des pods arrêtés

Lors de l'arrêt optimal des pods, le kubelet attribue un état Failed et un motif Shutdown aux pods arrêtés. Lorsque le nombre de pods arrêtés atteint un seuil de 1 000, la récupération de mémoire nettoie les pods. Vous pouvez également supprimer manuellement des pods d'arrêt à l'aide de la commande suivante :

kubectl get pods --all-namespaces | grep -i shutdown | awk '{print $1, $2}' | xargs -n2 kubectl delete pod -n

Empêcher les charges de travail d'utiliser des pods Spot

Si vous disposez de pods Spot existants que vous souhaitez mettre à jour pour qu'ils s'exécutent en tant que pods standards, vous pouvez utiliser l'une des méthodes suivantes :

  • Recréez la charge de travail : supprimez le déploiement, supprimez les lignes du fichier manifeste qui sélectionnent les pods Spot, puis appliquez le fichier manifeste de déploiement mis à jour au cluster.
  • Modifiez la charge de travail : modifiez la spécification du déploiement pendant que les pods sont en cours d'exécution dans le cluster.

Avec ces deux méthodes, vous pouvez rencontrer des perturbations mineures des charges de travail.

Recréer la charge de travail

Les étapes suivantes vous montrent comment supprimer le déploiement existant et appliquer un fichier manifeste mis à jour au cluster. Vous pouvez également suivre ces étapes pour d'autres types de charges de travail Kubernetes, comme les jobs.

Pour vous assurer que GKE place les pods mis à jour sur le bon type de nœud, vous devez exporter l'état existant de la charge de travail depuis le serveur d'API Kubernetes vers un fichier, puis modifier ce fichier.

  1. Écrivez la spécification de la charge de travail dans un fichier YAML :

    kubectl get deployment DEPLOYMENT_NAME -o yaml > DEPLOYMENT_NAME-on-demand.yaml
    

    Remplacez DEPLOYMENT_NAME par le nom de votre déploiement. Pour les autres types de charges de travail, comme les jobs ou les pods, utilisez le nom de ressource correspondant dans votre commande kubectl get, comme kubectl get pod.

  2. Ouvrez le fichier YAML dans un éditeur de texte :

    vi DEPLOYMENT_NAME-on-demand.yaml
    
  3. Supprimez le nodeSelector pour les pods Spot et la tolérance que GKE a ajoutée pour les pods Spot du fichier :

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      annotations:
      # lines omitted for clarity
    spec:
      progressDeadlineSeconds: 600
      replicas: 6
      revisionHistoryLimit: 10
      selector:
        matchLabels:
          pod: nginx-pod
      strategy:
        rollingUpdate:
          maxSurge: 25%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
        # lines omitted for clarity
        spec:
          containers:
          - image: nginx
            imagePullPolicy: Always
            name: web-server
            resources:
              limits:
                ephemeral-storage: 1Gi
              requests:
                cpu: 500m
                ephemeral-storage: 1Gi
                memory: 2Gi
            securityContext:
              capabilities:
                drop:
                - NET_RAW
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
          dnsPolicy: ClusterFirst
          nodeSelector:
            cloud.google.com/gke-spot: "true"
          restartPolicy: Always
          schedulerName: default-scheduler
          securityContext:
            seccompProfile:
              type: RuntimeDefault
          terminationGracePeriodSeconds: 15
          tolerations:
          - effect: NoSchedule
            key: kubernetes.io/arch
            operator: Equal
            value: amd64
          - effect: NoSchedule
            key: cloud.google.com/gke-spot
            operator: Equal
            value: "true"
    status:
      #lines omitted for clarity
    

    Vous devez supprimer à la fois la tolérance et le nodeSelector pour indiquer à GKE que les pods doivent s'exécuter sur des nœuds à la demande plutôt que sur des nœuds Spot.

  4. Enregistrez le fichier manifeste modifié.

  5. Supprimez le fichier manifeste de déploiement et réappliquez-le au cluster :

    kubectl replace -f DEPLOYMENT_NAME-on-demand.yaml
    

    La durée de cette opération dépend du nombre de pods que GKE doit arrêter et nettoyer.

Modifier la charge de travail sur place

Les étapes suivantes vous expliquent comment modifier un déploiement en cours d'exécution pour indiquer à GKE que les pods doivent s'exécuter sur des nœuds à la demande. Vous pouvez également suivre ces étapes pour d'autres types de charges de travail Kubernetes, comme les Jobs.

Vous devez modifier l'objet de charge de travail dans l'API Kubernetes, car GKE ajoute automatiquement une tolérance pour les pods Spot à la spécification de la charge de travail lors de l'admission de la charge de travail.

  1. Ouvrez le fichier manifeste de votre charge de travail dans un éditeur de texte pour le modifier :

    kubectl edit deployment/DEPLOYMENT_NAME
    

    Remplacez DEPLOYMENT_NAME par le nom du déploiement. Pour les autres types de charges de travail, comme les jobs ou les pods, utilisez le nom de ressource correspondant dans votre commande kubectl edit, comme kubectl edit pod/POD_NAME.

  2. Dans votre éditeur de texte, supprimez le sélecteur de nœuds ou la règle d'affinité de nœuds pour les Spot Pods, ainsi que la tolérance que GKE a ajoutée au fichier manifeste, comme dans l'exemple suivant :

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: example-deployment
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          type: dev
      template:
        metadata:
          labels:
            type: dev
        spec:
          nodeSelector:
            cloud.google.com/gke-spot: "true"
          tolerations:
          - effect: NoSchedule
            key: cloud.google.com/gke-spot
            operator: Equal
            value: "true"
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
    
  3. Enregistrez le fichier manifeste mis à jour et fermez l'éditeur de texte. La configuration d'objet mise à jour indique à GKE que les pods doivent s'exécuter sur des nœuds à la demande. GKE recrée les pods pour les placer sur de nouveaux nœuds à la demande.

Vérifier que les charges de travail s'exécutent sur des nœuds à la demande

Pour vérifier que vos charges de travail mises à jour ne s'exécutent plus sur des pods Spot, inspectez la charge de travail et recherchez la tolérance pour les pods Spot :

  • Inspectez la charge de travail :

    kubectl describe deployment DEPLOYMENT_NAME
    

Le résultat n'affiche pas d'entrée pour cloud.google.com/gke-spot dans le champ spec.tolerations.

Étapes suivantes