Ejecutar cargas de trabajo tolerantes a fallos a un coste inferior en pods de Spot


En esta página se explica cómo ejecutar cargas de trabajo tolerantes a fallos a un coste más bajo mediante pods esporádicos en tus clústeres Autopilot de Google Kubernetes Engine (GKE).

Información general

En los clústeres de Autopilot de GKE, los pods de Spot son pods que se ejecutan en nodos respaldados por VMs de Spot de Compute Engine. Los Pods de Spot tienen un precio inferior al de los Pods de Autopilot estándar, pero GKE puede desalojarlos cuando se necesiten recursos de computación para ejecutar Pods estándar.

Los pods Spot son ideales para ejecutar cargas de trabajo sin estado, por lotes o tolerantes a fallos a un coste inferior en comparación con la ejecución de esas cargas de trabajo como pods estándar. Para usar pods de Spot en clústeres de Autopilot, modifica el manifiesto con la especificación de tu pod para solicitar pods de Spot.

Puedes ejecutar pods esporádicos en la clase de computación de uso general predeterminada de Autopilot, así como en clases de computación especializadas que cumplan requisitos de hardware específicos. Para obtener información sobre estas clases de computación, consulta Clases de computación en Autopilot.

Para obtener más información sobre los precios de los pods de acceso puntual en clústeres de Autopilot, consulta la página Precios de Google Kubernetes Engine.

Los pods esporádicos no están incluidos en el acuerdo de nivel de servicio de Autopilot.

Ventajas

Usar pods de Spot en tus clústeres de Autopilot te ofrece las siguientes ventajas:

  • Precios más bajos que si ejecutaras las mismas cargas de trabajo en pods de Autopilot estándar.
  • GKE gestiona automáticamente el autoescalado y la programación.
  • GKE contamina automáticamente los nodos que ejecutan pods esporádicos para asegurarse de que los pods estándar, como tus cargas de trabajo críticas, no se programen en esos nodos. Las implementaciones que usan Pods de Spot se actualizan automáticamente con una tolerancia correspondiente.

Antes de empezar

Antes de empezar, asegúrate de que has realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si quieres usar Google Cloud CLI para esta tarea, instálala y, a continuación, inicialízala. Si ya has instalado la gcloud CLI, obtén la versión más reciente ejecutando gcloud components update.

Solicitar pods de Spot en cargas de trabajo de Autopilot

Para solicitar que tus pods se ejecuten como pods esporádicos, usa la etiqueta cloud.google.com/gke-spot=true en un nodeSelector o en un nodeAffinity de la especificación del pod. GKE aprovisiona automáticamente los nodos que pueden ejecutar pods esporádicos.

Los Pods Spot se pueden desalojar y finalizar en cualquier momento, por ejemplo, si los recursos de computación se necesitan en otra parte de Google Cloud. Cuando se produce una finalización, los pods de Spot del nodo que se va a finalizar pueden solicitar un periodo de gracia de hasta 15 segundos antes de la finalización, que se concede en la medida de lo posible, especificando el campo terminationGracePeriodSeconds.

El periodo de gracia máximo que se concede a los Spot Pods durante la expropiación es de 15 segundos. Solicitar más de 15 segundos en terminationGracePeriodSeconds no concede más de 15 segundos durante la apropiación. Cuando se expulsa un pod, se le envía la señal SIGTERM y debe tomar medidas para cerrarse durante el periodo de gracia.

En Autopilot, GKE también marca automáticamente los nodos creados para ejecutar pods esporádicos y modifica esas cargas de trabajo con la tolerancia correspondiente. La intolerancia impide que los pods estándar se programen en nodos que ejecuten pods de tipo spot.

Usar un nodeSelector para requerir pods de Spot

Puedes usar un nodeSelector para requerir pods esporádicos en una implementación. Añade la etiqueta cloud.google.com/gke-spot=true a tu Deployment, como en el siguiente ejemplo:

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

Usar la afinidad de nodos para solicitar pods de Spot

También puedes usar la afinidad de nodos para solicitar pods de Spot. La afinidad de nodos te ofrece una forma más extensible de seleccionar nodos para ejecutar tus cargas de trabajo. Por ejemplo, puedes combinar varios criterios de selección para tener un control más preciso sobre dónde se ejecutan tus pods. Cuando usas la afinidad de nodos para solicitar pods de Spot, puedes especificar el tipo de afinidad de nodos que quieres usar de la siguiente manera:

  • requiredDuringSchedulingIgnoredDuringExecution: debe usar pods de Spot.
  • preferredDuringSchedulingIgnoredDuringExecution: usa los pods de Spot de la mejor forma posible.

Para usar la afinidad de nodos y requerir Pods Spot en un Deployment, añade la siguiente regla nodeAffinity al manifiesto del Deployment:

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

Solicitar Pods de tipo spot en la medida de lo posible

Para usar la afinidad de nodos y solicitar pods esporádicos de la mejor forma posible, usa preferredDuringSchedulingIgnoredDuringExecution. Cuando solicitas Pods esporádicos de forma preferente, GKE programa tus Pods según el siguiente orden:

  1. Nodos que pueden ejecutar pods esporádicos y que tienen capacidad asignable disponible.
  2. Nodos estándar que tengan capacidad asignable disponible.
  3. Nuevos nodos que pueden ejecutar pods esporádicos si los recursos de computación están disponibles.
  4. Nuevos nodos estándar.

Como GKE prefiere los nodos estándar que ya tienen capacidad asignable en lugar de crear nodos para los pods de Spot, es posible que observes que se ejecutan más pods como pods estándar que como pods de Spot, lo que te impide aprovechar al máximo los precios más bajos de los pods de Spot.

Solicitudes de pods interrumpibles

Los clústeres de Autopilot admiten solicitudes de pods de desalojo mediante el selector cloud.google.com/gke-preemptible. Los pods que usan este selector se migran automáticamente a pods esporádicos y el selector se cambia a cloud.google.com/gke-spot.

Buscar y eliminar pods finalizados

Durante la finalización correcta de un pod, kubelet asigna el estado Failed y el motivo Shutdown a los pods finalizados. Cuando el número de pods finalizados alcanza un umbral de 1000, la recogida de elementos no utilizados limpia los pods. También puedes eliminar los pods de cierre manualmente con el siguiente comando:

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

Impedir que las cargas de trabajo usen pods de acceso puntual

Si tienes Spot Pods y quieres actualizarlos para que se ejecuten como Pods estándar, puedes usar uno de los siguientes métodos:

  • Recrea la carga de trabajo: elimina el Deployment, quita las líneas del manifiesto que seleccionan los pods esporádicos y, a continuación, aplica el manifiesto de Deployment actualizado al clúster.
  • Edita la carga de trabajo: edita la especificación de la implementación mientras los pods se ejecutan en el clúster.

Con ambos métodos, es posible que se produzcan interrupciones menores en la carga de trabajo.

Volver a crear la carga de trabajo

En los siguientes pasos se muestra cómo eliminar la implementación y aplicar un manifiesto actualizado al clúster. También puedes seguir estos pasos para otros tipos de cargas de trabajo de Kubernetes, como los trabajos.

Para asegurarse de que GKE coloca los pods actualizados en el tipo de nodo correcto, debe exportar el estado actual de la carga de trabajo del servidor de la API de Kubernetes a un archivo y editarlo.

  1. Escribe la especificación de la carga de trabajo en un archivo YAML:

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

    Sustituye DEPLOYMENT_NAME por el nombre de tu implementación. Para otros tipos de cargas de trabajo, como trabajos o pods, usa el nombre de recurso correspondiente en tu comando kubectl get, como kubectl get pod.

  2. Abre el archivo YAML en un editor de texto:

    vi DEPLOYMENT_NAME-on-demand.yaml
    
  3. Elimina el nodeSelector de los pods esporádicos y la tolerancia que GKE ha añadido a los pods esporádicos del archivo:

    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
    

    Debes eliminar tanto la tolerancia como el selector de nodos para indicar a GKE que los pods deben ejecutarse en nodos bajo demanda en lugar de en nodos Spot.

  4. Guarda el archivo de manifiesto actualizado.

  5. Elimina y vuelve a aplicar el manifiesto de Deployment al clúster:

    kubectl replace -f DEPLOYMENT_NAME-on-demand.yaml
    

    La duración de esta operación depende del número de pods que GKE tenga que finalizar y limpiar.

Editar la carga de trabajo in situ

En los siguientes pasos se muestra cómo editar una implementación en ejecución para indicar a GKE que los pods deben ejecutarse en nodos bajo demanda. También puedes seguir estos pasos para otros tipos de cargas de trabajo de Kubernetes, como los trabajos.

Debes editar el objeto de carga de trabajo en la API de Kubernetes porque GKE añade automáticamente una tolerancia para los pods esporádicos a la especificación de la carga de trabajo durante la admisión de la carga de trabajo.

  1. Abre el manifiesto de la carga de trabajo para editarlo en un editor de texto:

    kubectl edit deployment/DEPLOYMENT_NAME
    

    Sustituye DEPLOYMENT_NAME por el nombre de la implementación. Para otros tipos de cargas de trabajo, como trabajos o pods, usa el nombre de recurso correspondiente en tu comando kubectl edit, como kubectl edit pod/POD_NAME.

  2. En tu editor de texto, elimina el selector de nodos o la regla de afinidad de nodos de los pods esporádicos y la tolerancia que GKE ha añadido al manifiesto, como en el siguiente ejemplo:

    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. Guarda el archivo de manifiesto actualizado y cierra el editor de texto. La configuración del objeto actualizado indica a GKE que los pods deben ejecutarse en nodos bajo demanda. GKE vuelve a crear los pods para colocarlos en nodos bajo demanda nuevos.

Verificar que las cargas de trabajo se ejecutan en nodos bajo demanda

Para verificar que tus cargas de trabajo actualizadas ya no se ejecutan en pods de Spot, inspecciona la carga de trabajo y busca la tolerancia de los pods de Spot:

  • Inspecciona la carga de trabajo:

    kubectl describe deployment DEPLOYMENT_NAME
    

En el resultado no se muestra ninguna entrada de cloud.google.com/gke-spot en el campo spec.tolerations.

Siguientes pasos